代理模式:动态代理
1. 为什么要用动态代理
对于一个类的方法
public class Calculator {
public void add(int i,int j){
System.out.println("add方法调用,参数为"+i+","+j);
System.out.println(i + j);
System.out.println("add方法结束,结果为"+(i+j));
}
public void sub(int i, int j){
System.out.println(i - j);
}
}
我们想打印日志信息,或者保存日志信息。我们需要在方法执行的前后加上打印输出或者日志记录的代码,如上的add方法中
但是,当我们如果某天我们不想要记录日志信息,我们需要手动删除日志语句。
2. 于是我们可以使用动态代理
- 使用某一个类,“包装”Calculator,并且在它执行前,执行日志代码,好像听起来很像装饰器模式哈
1. 首先我们需要一个接口
public interface Calculator {
public int add(int a, int b);
public int sub(int a, int b);
public int mul(int a, int b);
public int div(int a, int b);
}
2. 然后对该接口进行实现
public class MyMathCalculator implements Calculator {
public int add(int a, int b) { return a+b; }
public int sub(int a, int b) { return a-b; }
public int mul(int a, int b) { return a*b; }
public int div(int a, int b) { return a/b; }
}
3. 然后就是最重要的代理类了
主要是使用java的Proxy.newProxyInstance()来创建动态代理对象。
主要用到了反射的知识奥,8会的建议去看一看发射。
public class CalculatorProxy{
public static Calculator getProxy(final Calculator calculator){
ClassLoader classLoader = calculator.getClass().getClassLoader();
Class<?>[] classes = calculator.getClass().getInterfaces();
//方法执行器。帮我们目标对象执行目标方法-----匿名接口对象
InvocationHandler invocationHandler = new InvocationHandler() {
/**
* @param proxy 代理对象;给jdk使用,任何时候都不要动这个对象
* @param method 当前将要执行的目标对象的方法
* @param args 这个方法调用时外界传,入的参数值
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//利用反射执行目标方法
//目标执行后的返回值
System.out.println("动态代理执行");
Object result = method.invoke(calculator, args);
//返回值必须返回出去外界才能拿到真正执行后的返回值
return result;
}
};
//Proxy为目标对象创建代理对象
Object o = Proxy.newProxyInstance(classLoader, classes, invocationHandler);
return (Calculator) o;
}
}
4.使用
public static void main(String[] args) {
//普通调用
Calculator calculator = new CalculatorImpl();
calculator.add(2,3);
System.out.println(calculator.getClass());
//代理调用
Calculator proxy = CalculatorProxy.getProxy(calculator);
proxy.add(1,2);
System.out.println(proxy.getClass());
}
疑惑:
- 为什么要一个Calculator的接口
因为对于代理对象来说,我们可以通过上面使用中间的getClass看到,代理对象的类型是属于
class com.sun.proxy.$Proxy0
而我们的对象是
class com.xj.day0904Proxy.CalculatorImpl
如果没有接口存在,可以看出两个类型是没有任何关系的
但是有了这个接口后,两个类都实现了同一个接口,此时,代理类也就具有了add,sub等等的方法;
之后代理类调用add方法后,就会去执行被代理对象的各种方法,并且在执行前后进行日志等操作
本博客文章主要供博主学习交流用,所有描述、代码无法保证准确性,如有问题可以留言共同讨论。