一 . 前言
今天对 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
publicObjectdoResolveDependency(DependencyDescriptordescriptor,@NullableStringbeanName,@NullableSet<String>autowiredBeanNames,@NullableTypeConvertertypeConverter)throwsBeansException{//............以下是主要逻辑if(autowiredBeanNames!=null){autowiredBeanNames.add(autowiredBeanName);}//获取Autowired的实际对象或者代理对象if(instanceCandidateinstanceofClass){instanceCandidate=descriptor.resolveCandidate(autowiredBeanName,type,this);}//判断该对象是否为nullObjectresult=instanceCandidate;if(resultinstanceofNullBean){if(isRequired(descriptor)){raiseNoMatchingBeanFound(type,descriptor.getResolvableType(),descriptor);}result=null;}//核心拦截逻辑if(!ClassUtils.isAssignableValue(type,result)){thrownewBeanNotOfRequiredTypeException(autowiredBeanName,type,instanceCandidate.getClass());}returnresult;}}
3.2 拦截的判断
publicstaticbooleanisAssignable(Class<?>lhsType,Class<?>rhsType){//类型判断if(lhsType.isAssignableFrom(rhsType)){returntrue;}else{ClassresolvedWrapper;//基本类型特殊处理if(lhsType.isPrimitive()){resolvedWrapper=(Class)primitiveWrapperTypeMap.get(rhsType);returnlhsType==resolvedWrapper;}else{resolvedWrapper=(Class)primitiveTypeToWrapperMap.get(rhsType);returnresolvedWrapper!=null&&lhsType.isAssignableFrom(resolvedWrapper);}}}
3.3 AOP 的使用
看到了拦截的入口 , 那就得看看 AOP 中是如何通过 PostProcessor 进行处理的了 , 首先看一下 PostProcessor 链表
Step 1 : 当对象 A 中字段是 @Autowired 注入的 AOP 代理类时
此时我们可以发现 , 在 DefaultListableBeanFactory # doResolveDependency 环节会去获取该代理类的对象 , 拿到的对象如下图所示 :
//doResolveDependency中获取对象环节if(instanceCandidateinstanceofClass){//此时拿到的对象就是一个cglib代理类instanceCandidate=descriptor.resolveCandidate(autowiredBeanName,type,this);
Step 2 : 判断类的关系入口
//doResolveDependency中判断类的关系->trueif(!ClassUtils.isAssignableValue(type,result)){thrownewBeanNotOfRequiredTypeException(autowiredBeanName,type,instanceCandidate.getClass());}//result.getClass()-name=com.gang.aop.demo.service.StartService$$EnhancerBySpringCGLIB$$d673b902
Step 3 : 判断类的关系逻辑
C-ClassUtilspublicstaticbooleanisAssignable(Class<?>lhsType,Class<?>rhsType){//核心语句,native方法->publicnativebooleanisAssignableFrom(Class<?>cls);if(lhsType.isAssignableFrom(rhsType)){returntrue;}//.........}//这里简单做了一个继承类,ChildServiceextendsChildService:------>ChildServiceByParentService:false<-------:------>ParentServiceByChildService:true<-------
由此可见 cglib 创建的对象满足该条件 : 相同 , 或者是超类或者超接口
这里回过头看之前的问题 , 就很简单了 :
//问题原因:我通过实现postProcessor去做了一个代理publicclassAopProxyImplextendsSourceable{privateSourceablesource;}//修改后:publicclassAopProxyImplextendsSource{privateSourceablesource;}//通过继承即可解决BeanNotOfRequiredTypeException,弄懂了就没什么难度了//
四 . 深入原理
那么继续回顾下 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个主要的方法 :
//代理方法publicObjectinvoke(finalintn,finalObjecto,finalObject[]array)throwsInvocationTargetException{finalCglibServicecglibService=(CglibService)o;switch(n){case0:{//代理对应的业务方法cglibService.run();returnnull;}case1:{//代理equeals方法returnnewBoolean(cglibService.equals(array[0]));}case2:{//代理toString方法returncglibService.toString();}case3:{//代理hashCode方法returnnewInteger(cglibService.hashCode());}}thrownewIllegalArgumentException("Cannotfindmatchingmethod/constructor");}//实例化对象publicObjectnewInstance(finalintn,finalObject[]array)throwsInvocationTargetException{switch(n){case0:{//此处总结通过new进行了实例化returnnewCglibService();}}thrownewIllegalArgumentException("Cannotfindmatchingmethod/constructor");}
enchance 对象
之前了解到 , cglib 通过重写字节码生成主类达到代理的目的 , 这里来看一下 , 原方法被改写成什么样了
finalvoidCGLIB$run$0(){super.run();}publicfinalvoidrun(){MethodInterceptorcglib$CALLBACK_2;MethodInterceptorcglib$CALLBACK_0;if((cglib$CALLBACK_0=(cglib$CALLBACK_2=this.CGLIB$CALLBACK_0))==null){CGLIB$BIND_CALLBACKS(this);cglib$CALLBACK_2=(cglib$CALLBACK_0=this.CGLIB$CALLBACK_0);}if(cglib$CALLBACK_0!=null){//调用拦截器对象cglib$CALLBACK_2.intercept((Object)this,CglibService$$EnhancerByCGLIB$$7aba7860.CGLIB$run$0$Method,CglibService$$EnhancerByCGLIB$$7aba7860.CGLIB$emptyArgs,CglibService$$EnhancerByCGLIB$$7aba7860.CGLIB$run$0$Proxy);return;}//没有拦截器对象,则直接调用super.run();}//实际被调用的拦截器publicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{//这里会调用关联类//最终通过super.run调用Objectresult=proxy.invokeSuper(obj,args);returnresult;}
此处也可以看到映射关系