Spring源码分析总结——AOP

该文章基于《Spring源码深度解析》撰写,感谢郝佳老师的奉献

AOP的实现原码

开启AOP需要在配置文件中声明<aop:aspectj-autoproxy>,针对声明我们进入AspectJAutoproxyBeanDefinitionParser类
由于所有的解析器都实现了BeanDefinitionParser接口,所以其入口都为Parse方法,AspeJAutoProxyBeanDefinitionParser也不例外,其入口如下:

public BeanDefinition parse(Element element, ParserContext parserContext) {
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    this.extendBeanDefinition(element, parserContext);
    return null;
}

很明显其中的registerAspectJAnnotationAutoProxyCreatorIfNecessary就是我们要深入研究的重点。
在registerAspectJAnnotationAutoProxyCreatorIfNecessary中主要完成了2个任务:

1.注册或者增强AnnotationAwareAspectJAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator实现了基本的AOP效果,所以第一步通过优先级判断哪个自动代理创建器最重要,然后通过改变对应的beanName进行注册和增强。

2.处理proxy-target-class以及expose-proxy属性。
proxy-target-class有两种实现方式:
I.JDK动态代理由于是通过在运行期间创建一个接口的实现类来完成代理所以至少实现一个接口。
II.CGLIB代理由于其操作字节码的特性(三个问题,无法通知final方法,需要引入额外包至classpath下,额外的配置)
expose-proxy存在的问题是对象内部的自我调用将无法实施切面中的增强。通过将this.method()修改为((Aservice)AopContext.currentProxy()).method()可以避免这个问题。

创建AOP代理

接下来就是创建代理的问题了,但是AspeJAutoProxyBeanDefinitionParser并不负责创建代理,创建代理是由其父类AbstractAutoProxyCreator的postProcessAfterInitialization方法完成的,正好我们可以观察他的类层次结构:
这里写图片描述
postProcessAfterInitialization完成代理主要包含两个步骤:获取增强方法或者增强器——>然后根据获取的增强进行代理,下面对这两个方法(两个方法都由AbstractAutoProxyCreator的getAdvicesAndAdvisorsForBean方法完成)进行分解:

获取增强器

该步可分为两步,第一步是获取全部增强器,第二步是进行增强器的筛选
获取全部增强器
增强器的获取又要回到AspeJAutoProxyBeanDefinitionParser这个子类中,其实他的过程无非是:
(1)获取所有bean
(2)找出所有带有@AspectJ注解的类
(3)对所有带有@AspectJ注解的类进行增强器的提取
(4)将提取结果加入缓存
第(3)步中增强器的获取并不是那么简单,它包括了三个步骤:

第一步:普通增强器的获取,该步骤又可细分为两步:1.切点信息的获取(由AspectJAdvisorFactory的getAdvisor方法完成)2.根据切点信息生成增强器(由InstantiationModelAwarePointcutAdvisorImpl进行统一封装)——Spring会根据不同的注解类型生成不同的增强器
对于前置增强器与后置增强器Spring处理的方式并不相同,前置增强主要是通过在拦截链中放置MethodBeforeAdviceInterceptor,然后在MethodBeforeAdvice中又放置了AspectJMethodBeforeAdvice,通过串联调用完成调用;后置增强通过直接在拦截链中使用AspectJAfterAdvice。

第二步:增加同步实例化增强器,该步骤的前提是寻找的增强器不为空并且又配置了增强延迟初始化

第三步:获取DeclareParents注解,DeclareParents主要用于引介增强的注解形式的实现,实现方式与普通增强非常相似,只不过使用DeclareParentsAdvisor进行封装。

寻找匹配的增强器
这一步不太复杂,大致就是将引介增强与普通增强分开进行处理

创建代理

对于代理类的创建Spring都交给了ProxyFactory去处理,初始化操作步骤基本上包括了下面的流程:
(1)获取当前类中的属性
(2)添加代理接口
(3)封装Advisor并添加至ProxyFactory中

这一步比较繁琐,所以多说一些,因为Spring中有数量众多的拦截器与增强器,增强方法等等,所以在封装时都需要进一步封装为Advisor进行代理的创建

(4)设置需要代理的类
(5)自定义customizeProxyFactory函数
(6)进行封装获取代理
Spring中通过判断(是否采用激进的优化 || 是否本身被代理而不是目标类接口 || 是否存在代理接口 )并且要求目标类不是接口的情况下可以采用CGLIB的方法实现AOP,否则使用JDK动态代理
其中JDK动态代理与CGLIB字节码生成的区别在于:
JDK动态代理只能对实现了接口的类生成代理,而不能针对类
CGLIB主要是针对类实现代理,主要是针对指定的类生成子类,覆盖其中的方法,所以最好不要使用final

下面来讲讲JDK和CGLIB是具体如何实现代理的:
JDK实现代理:JDK实现代理首先需要实现IvocationHandler接口并实现其中的invoke和getproxy方法,并且将需要代理的对象通过构造函数传入。实际在Spring中通过JdkDynamicAopProxy实现IvocationHandler接口来完成,在JdkDynamicAopProxy的invoke方法中通过ReflectiveMethodInvocation类进行了调用链的封装,并且由该类的proceed方法实现了拦截器的逐一调用,但是实际上的该类只是维护了链接调用的计数器,增强的顺序由增强器内部进行逻辑实现。

CGLIB实现代理:CGLIB实现代理需要一个实现了MethodInterceptor接口的类作为增强器,并通过Ehancer类的setCallbacks将MethodInterceptor接口的实现类作为增强器进行注入,Cglib2AopProxy类的getProxy完成了这一目的,并且封装了拦截器为DynamicAdvisedInterceptor类,在该类中又通过构造调用链的(CglibMethodInvocation(ReflectiveMethodInvocation的子类)proceed方法进行调用

静态AOP

原理
既然存在动态代理,那在Spring中也应该存在静态代理,由于静态代理在启动时便完成了对字节码的增强,所以效率上会高一些,Spring中的静态AOP直接使用了AspectJ提供的方法,而AspectJ又是在Instrument(java.lang.Instrument)基础上进行的封装,其中最重要的就是ClassFIleTransformer接口的实现类。通过Instumentation类的addTransformer方法添加ClassFIleTransformer接口的实现类就可以达到静态AOP的效果

实际上Spring框架在实现静态AOP的时候通过了下面两个步骤
(1)解析自定义标签(<context:load-time-weaver>)
首先查询是否开启AspectJ(<context:load-time-weaver>),同时检测是否存在META-INF/aop.xml文件,如果开启了AspectJ,那么接下来会注册
1.AspectJWeavingEnabler(封装在BeanDefinition),
2.DefaultContextLoadTimeWeaver(由<context:load-time-weaver>产生),
3.LoadTimeWeaverAwareProcessor(容器初始化时添加,激活AspectJ的前提)

(2)织入
首先来看一下接下来用到的几个类型的继承关系
继承关系
先解释一下其中出现的几个接口
1.Ordered接口保证在实例化bean时当前的bean会最先被初始化
2.BeanClassLoaderAware保证了在bean初始化时将会调用AbstractAutoWireCapableBeanFactory的invokeMethods时将beanClassLoader赋值给当前类
3.DisposableBean销毁bean时调用destroy方法
最后由于AspectJWeavingEnabler实现了BeanFactoryPostProcessor,所以当所有bean解析结束后会调用postProcessBeanFactory来进行织入

发布了52 篇原创文章 · 获赞 8 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章