首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca…
一 . 前言
今天对 Spring 进行深度使用的时候 , 想仿照 AOP 去实现对应的代理 , 但是却触发了 BeanNotOfRequiredTypeException 异常 , 原因是因为 Spring 会进行类的校验
于是突然产生了好奇 , 决定研究一下 , AOP 是通过什么方式避开这个校验过程
二 . 前置知识
- AOP 通过 AopProxy 进行代理
- SpringBoot 1.5 默认使用 JDK Proxy , SpringBoot 2.0 基于自动装配(AopAutoConfiguration)的配置 , 默认使用 CGlib
JDK Proxy 和 CGLib 的区别
老生常谈的问题 , 问了完整性(凑字数) , 还是简单列一下 :
- JDK Proxy : 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
- CGLIB动态代理:利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
PS : 通过 proxy-target-class 可以进行配置
三 . 原理探索
常规方式是 CGLIB , 所以主流程还是通过这种方式分析 , 有了前置知识的补充 , 实现猜测是由于 CGLIB 的特性 , 实际上被校验出来.
- 源头 :autowired 导入得时候会校验注入的类是否正确
3.1 拦截的入口
- Step 1 : AbstractAutowireCapableBeanFactory # populateBean
- Step 2 : AutowiredAnnotationBeanPostProcessor # postProcessProperties
- Step 3 : InjectionMetadata # inject
- Step 4 : AutowiredAnnotationBeanPostProcessor # inject
- Step 5 : DefaultListableBeanFactory # doResolveDependency
1 | java复制代码public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, |
3.2 拦截的判断
1 | java复制代码public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) { |
3.3 AOP 的使用
看到了拦截的入口 , 那就得看看 AOP 中是如何通过 PostProcessor 进行处理的了 , 首先看一下 PostProcessor 链表
Step 1 : 当对象 A 中字段是 @Autowired 注入的 AOP 代理类时
此时我们可以发现 , 在 DefaultListableBeanFactory # doResolveDependency 环节会去获取该代理类的对象 ,
拿到的对象如下图所示 :
1 | java复制代码// doResolveDependency 中获取对象环节 |
Step 2 : 判断类的关系入口
1 | java复制代码// doResolveDependency 中判断类的关系 -> true |
Step 3 : 判断类的关系逻辑
1 | java复制代码C- ClassUtils |
由此可见 cglib 创建的对象满足该条件 : 相同 , 或者是超类或者超接口
这里回过头看之前的问题 , 就很简单了 :
1 | java复制代码// 问题原因 : |
四 . 深入原理
那么继续回顾下 CGLIB 的创建过程 , 实际上在编译的结果上是可以很直观的看到代理的对象的 :
关于 CGLIB 的基础 , 可以看看菜鸟的文档 CGLIB(Code Generation Library) 介绍与原理 , 写的很详细
4.1 CGLIB 的创建过程
FastClass 的作用
FastClass 就是给每个方法编号,通过编号找到方法,这样可以避免频繁使用反射导致效率比较低
CGLIB 会生成2个 fastClass :
- xxxx$$FastClassByCGLIB$$xxxx :为生成的代理类中的每个方法建立了索引
- xxxx$$EnhancerByCGLIB$$xxxx$$FastClassByCGLIB$$xxxx : 为我们被代理类的所有方法包含其父类的方法建立了索引
原因 : cglib代理基于继承实现,父类中非public、final的方法无法被继承,所以需要一个父类的fastclass来调用代理不到的方法
FastClass 中有2个主要的方法 :
1 | java复制代码// 代理方法 |
enchance 对象
之前了解到 , cglib 通过重写字节码生成主类达到代理的目的 , 这里来看一下 , 原方法被改写成什么样了
1 | java复制代码final void CGLIB$run$0() { |
此处也可以看到映射关系
总结
本文转载自: 掘金