代理是在我们日常开发中遇到的比较多的场景,虽然你可能自己在代码中不太常用代理,但是Spring里面很多的功能的实现都依托了代理,并且面试的时候面试官特别喜欢问这个问题。跟代理相关的包含了代理模式,静态代理,动态代理,正向代理,反向代理 。本篇文章主要将设计模式中的代理模式,内容来自于网络,我只是做一个收录整理,以便用时翻阅。
1.代理模式的定义
为其他对象提供一种代理,以控制对这个对象的访问,补充:代理对象在客户端和目标对象之间起到一个中介的作用。类型:结构型。
2.适用场景
- 保护目标对象
- 增强目标对象
3.代理模式的优点:
- 代理模式能将代理对象与真实被调动用的目标对象分离
- 一定程度上降低了系统的耦合度,扩展性好
- 保护目标对象 、
- 增强目标对象
4 .代理模式的缺点:
- 静态代理对造成系统中类的数量增加
- 客户端和目标对象之间增加代理类可能导致请求变慢
- 增加系统的复杂度
5.JDK动态代理与CGLIB动态代理
JDK的动态代理是生成一个新类,利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
代理类的生成是先通过生成代理的Class对象,然后利用反射生成代理对象,同时将InvocationHanlder作为构造参数传入。生成的代理类就是这样的:
那么JDK动态代理是如何找到目标对象的?
很明显了在代理类中,调用this.h.invoke方法,this就是代理类&Proxy0,h就是构造函数传进来的InvocationHanlder,然后调用invoke方法,invoke方法恰好是我们已经实现的,所以会调用到我们的invoke的实现类里面去,在 InvocationHanlder的invoke方法里面,就会调用到目标类的业务方法。
CGLIB是生成一个子类,他的业务逻辑是通过调用超类的方法生成的。使用CGLIB的时候一定要注意final这个修饰符,实现的原理是利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
当目标类的有实现接口的时候,可以使用JDK的动态代理,也可以使用CGLIB,当目标类没有实现接口的时候,只能使用CGLIB
整个过程相比jdk动态代理简单了一些。不同的是JDK动态代理强制要求实现类必须最少实现一个接口,但是CGLIB则可以要求实现类不用实现任何接口。CGLIB动态生成的代理类会继承我们的业务类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。CGLIB是高效的代码生成包,底层依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强。
代理类对象是由Enhancer类创建的。Enhancer是CGLIB的字节码增强器,可以很方便的对类进行拓展
通过操作字节码的技术生成的代理类,生成的代理类会先调用拦截器的方法,再调用业务类的方法
cglib里面是通过索引找到目标方法的。
动态代理类根据我们业务类的每一个方法都生成了2个代理方法,第一个代理方法直接调用父类的方法,也就是我们业务类的方法,第二个方法也就是代理类真正调用的方法是经过封装的,他会去判断是否实现了MethodInterceptor 的intercept方法,如果实现了则会调用intercept方法。这个MethodInterceptor 就是我们在enhancer.setCallback(new UserMethodInterceptor());这边设置进去的。
初始化完成后调用fci.f2.invoke(fci.i2, obj, args);方法。这个方法很像反射方法,其实它并不是通过反射找到指定的方法的,而是在创建代理类的时候为其中的方法建立hash索引,这样调用的时候通过索引调用提高了效率。
6.Spring AOP里面的代理选择
在最新版的 Spring 中,依然是如上策略不变。即能用 JDK 做动态代理就用 JDK,不能用 JDK 做动态代理就用 Cglib,即首选 JDK 做动态代理。
如果想指定,则:
但是在SpringBoot2.0之后,这个策略变了,Spring Boot 中的 AOP,2.0 之前和 Spring 一样;2.0 之后首选 Cglib 动态代理,如果用户想要使用 JDK 动态代理,需要自己手动配置。
spring.aop.proxy-target-class=false
我们可以在SpringBoot2.0中来验证这一点。
静态代理模式的实现
设计模式之禅学习笔记08--代理模式_时空恋旅人的博客-CSDN博客
JDK动态代理模式的实现
设计模式之禅学习笔记09--代理模式(动态代理)_时空恋旅人的博客-CSDN博客