定义
代理模式是指为对象提供一种代理,以控制对该对象的访问,属于一种结构型模式。在某些情况下,一个对象不能直接访问另一个对象,代理模式就在客户端和被服务端之间起到了一个中介的作用。
举例
静态代理
场景:张三要结婚,但是由于工作繁忙,找不到对象。那么张三的父亲可以代理张三找对象。
- 创建IPerson接口,给出找对象的抽象方法。
1 | public interface IPerson { |
- 创建张三的实例,继承IPerson接口,重写找对象的方法。
1 | public class ZhanSan implements IPerson { |
- 创建张三的代理对象(他父亲),在构造方法中传入张三,重写张三的找对象方法。
1 | public class ZhangLaoSan implements IPerson { |
- 测试。
1 | public class Test { |
- 结果。
这个场景有个弊端,如果还有赵六要相亲,就需要再创建一个赵六的代理。如果还有其他对象也需要相亲的话,就需要创建更多的代理对象,这样做就违背了开闭原则。而在真实的场景中,有专门找对象的中介或者机构:媒婆。所以这里可以用动态代理来构建媒婆这样角色。
jdk动态代理
Java的生态中,用的比较多的是JDK自带的动态代理。
- 创建媒婆代理类,继承InvocationHandler接口,重写invoke方法。
1 | import java.lang.reflect.InvocationHandler; |
- 创建赵六实例。
1 | public class ZhaoLiu implements IPerson { |
- 测试。
1 | public class Test { |
- 结果。
cglib动态代理
- 引入cglib的jar包。
1 | <dependency> |
- 写一个孟婆类继承MethodInterceptor。
1 | import net.sf.cglib.proxy.Enhancer; |
- 创建单身客户类。这里可以看到Customer是没有实现任何接口的。它是通过动态继承代理对象类来实现动态代理的。
1 | public class Customer { |
- 写一个测试类。
1 | public class Test { |
- 查看测试结果。
那么cglib的底层是如何实现动态代理的呢,这里可以通过一行代码,将cglib动态生成的代理class从内存读取到磁盘中。
1 | import net.sf.cglib.core.DebuggingClassWriter; |
这时可以在E盘根目录中看到从内存中读取的文件。打开目录就能看到cglib动态的生成了三个class文件。
把第二个文件Customer$$EnhancerByCGLIB$$ef2de5ed.class拖入到idea编辑器中,可以看到,这是cglib动态生成的一个代理对象,这个对象继承了目标对象。
重写了目标对象的方法。
可以看到这里有一个CGLIB$findLove$0() 的方法,这个方法是被 CglibMeipo类的 method.invokeSuper() 调用的,而下面的 findLove() 方法是被MethodProxy的invoke()方法调用的。
所以总的来说,调用的顺序是,代理对象调用findlove() –> 调用拦截器 –> methodProxy. invokeSuper() –> CGLIB$findLove$0() –> Customer.findLove()。
我们可以点开methodProxy. invokeSuper()的源码看看。
可以发现,这里获取了代理对象的FastClass信息。然后调用了被代理对象的对应方法。
总结
1.静态代理和动态代理
(1)静态代理,实际上就是一种通过继承来重写方法并实现方法增强的,代理类是没有实现任何接口的。而动态代理是通过实现jdk或者cglib的接口来创建一个拦截器,然后在拦截器中进行方法增强的。
(2)静态代理只能手动完成代理操作,如果代理类增加了方法,代理类需要同步增加该方法。违背了开闭原则。
2.jdk动态代理和cglib动态代理
(1)jdk动态代理要求被代理对象必须要实现一个接口,而cglib动态代理则只需要代理对象是一个类即可。
(2) jdk动态代理调用代理方法是通过反射机制调用的,而cglib动态代理是通过FastClass机制直接调用的,所以cglib动态代理的执行效率要更高。
3.代理模式的优点
(1)代理模式将目标对象与代理对象分离,起到了保护目标对象的作用。
(2)在一定程度上降低了系统的耦合性,扩展性比较好。
(3)可以增强目标对象的功能。
4.代理模式的缺点
(1)代理模式会造成系统设计中类的增加。
(2)在客户端和服务端多出了一个代理对象,请求的处理速度会变慢。
(3)增加了系统的复杂度。