要想搞明白动态代理之前,我们先来了解一下代理是什么意思,先来谈谈设计模式中的代理模式。
什么是代理模式(Proxy)
定义:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用。
在代理模式中,是需要代理对象和目标对象实现同一个接口(如果是不同的接口,那就是适配器模式了),看下面的UML图
为什么要用代理
最最最主要的原因就是,在不改变目标对象方法的情况下对方法进行增强,比如,我们希望对方法的调用增加日志记录,或者对方法的调用进行拦截,等等...
举一个例子
现有一个IPerson接口,只有一个方法say()
public interface IPerson {void say();
}
有一个Man类,实现了IPerson
public class Man implements IPerson{@Overridepublic void say() {L.d("man say");}
}
现在需要在say方法被调用的时候,记录方法被调用的时间,最直接的就是修改Man的say方法,但是这样做的弊端就是如果有很多实现了IPerson接口的类,那就需要修改多处代码,而且这样的修改可能会导致其他的代码出问题(可能并不是所有的say都需要记录调用时间)。怎么办呢,这时候代理就要登场了!
静态代理
public class ManProxy implements IPerson{private IPerson target;public IPerson getTarget() {return target;}public ManProxy setTarget(IPerson target) {this.target = target;return this;}@Overridepublic void say() {if (target != null) {L.d("man say invoked at : " + System.currentTimeMillis());target.say();}}
}
这样我们需要新建一个ManProxy类同样实现IPerson接口,将要代理的对象传递进来,这样就可以在不修改Man的say方法的情况下实现了我们的需求。这其实就是静态代理。那你可能要问,既然有了静态代理,为什么需要动态代理呢,因为静态代理有一个最大的缺陷:接口与代理类是1对1的,有多个接口需要代理,就需要新建多个代理类,繁琐,类爆炸。
动态代理
我们先尝试用动态代理来解决上面的问题。先新建一个类实现InvocationHandler
public class NormalHandler implements InvocationHandler {private Object target;public NormalHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {L.d("man say invoked at : " + System.currentTimeMillis());method.invoke(target, args);return null;}
}
然后可以这样使用
NormalHandler normalHandler = new NormalHandler(new Man());IPerson iPerson = (IPerson) Proxy.newProxyInstance(IPerson.class.getClassLoader(),new Class[] {IPerson.class}, normalHandler);iPerson.say();
可以看到NormalHandler中代理的对象是Object类型,所以它是被多个接口代理复用的,这样就解决静态代理类爆炸,维护困难的问题。
接着我们发现执行iPerson.say()时,会被拦截从而执行NormalHandler中的invoke方法。
总结
1、代理模式的概念
2、静态代理和动态代理的区别
3、如何使用动态代理:
3.1、创建被代理对象的接口IPerson
3.2、实现被代理的真实对象Man
3.3、创建调用处理器NormalHandler
3.4、生成代理对象
本文参考:https://blog.csdn.net/u011552404/article/details/79954199、https://www.jianshu.com/p/6e962d1e7ddd