AOP是Spring Core中几大重要能力之一,我们可以使用AOP实现很多功能,比如我们常用的日志处理与Spring中的声明式事务。
AOP的几个重要概念
Aspect:切面,在Spring中意为所有通知方法所在的类
Join point:连接点,程序执行中的一点,在Spring中只表示方法执行(Spring只支持方法级别的拦截)
Advice:通知,在特定连接点上采取的操作,Spring将通知抽象为拦截器,并围绕连接点维护拦截器链。共有5种类型,before(切点之前执行),around(环绕执行,即切点前后执行),After returning(切点正常执行完返回后执行),After throwing(切点抛出异常后执行),after(切点之后执行,不管是异常或正常结束),AOP拦截器链则为以上五种通知组成。我们可以在通知方法中获得我们需要的参数(返回值,异常信息,代理对象等)
Pointcut:切点,与通知一起出现,使用专门的切点表达式决定在何处执行通知方法。
Introduction:引入,为类添加新的方法或字段。
Target object:被代理的对象
AOP proxy:AOP代理对象,由JDK动态代理或CGLIB代理生成
Weaving:织入,将通知等织入代理类。Spring AOP是动态织入(运行时织入),AspectJ则是静态织入(编译时织入)
几个注意点
关于Spring AOP的具体使用这里不做介绍,具体见文档,这里说几个在使用代理时需要注意的地方。
由于Spring AOP框架基于代理的特性,目标对象内的调用根据定义不会被拦截。自调用即类似this.bar()或this.foo()这样的调用,即使在bar方法上有通知方法通知也不会执行。对于JDK代理,只能拦截代理上的公共接口方法调用。使用CGLIB,可以拦截代理上的公共和受保护方法调用(Cglib基于子父类实现代理,而私有方法不会被子类继承)。当多个通知都想在同一个连接点上运行时,他们将按照优先级顺序执行,优先级顺序可以使用Order接口来定义。
总结一句:自调用通知方法不执行,私有方法通知不执行。
原理
接下来从源码角度分析下Spring AOP的实现原理。
在Spring中我们使用@EnableAspectJautoProxy开启AOP功能,我们以此为入口。(其他的Enable注解分析原理都是一样的,比如EnableAsync等)。
它使用@Import注解导入了AspectJAutoProxyRegistrar类,该类实现了ImportBeanDefinitionRegistrar接口,用于向Spring中注册类。
在registerBeanDefinitions方法的第一行注册了AOP需要的相关bean,方法中的下面部分是取EnableAspectJAutoProxy注解的信息,根据参数值做相应的处理,这里主要关注方法的首行代码,进入registerAspectJAnnotationAutoProxyCreatorIfNecessary方法。
发现最终注册了AnnotationAwareAspectJAutoProxyCreator。该类是实现AOP的基础,我们对该类进行分析,首先来看继承结构
BeanFactoryAware接口主要用于设置BeanFactory,这里我们主要关注InstantiationAwareBeanPostProcessor与BeanPostProcessor接口,实现这两个接口意味着AnnotationAwareAspectJAutoProxyCreator是一个Spring的后置处理器,后置处理器会在bean的创建过程中起作用,关于后置处理器不熟悉的同学可以去看这篇文章Spring之IOC容器初始化。
InstantiationAwareBeanPostProcessor有一个before与after接口,由接口名可知两个方法分别在bean实例化前后调用,关于Spring中bean的实例化过程不清楚的可以看Spring Bean的实例化分析。我们在子类中找到他们的实现(after方法由于没有特别的处理这里就省略了)
首先从缓存中获取,然后调用this.isInfrastructureClass(beanClass)判断创建的类是否为Advice、Pointcut等相关类,若是则放入adviseBean集合并返回null,正常的bean经过该方法会返回null,这里主要是用来处理我们的切面类。
bean创建完成后接下来就是另一个接口BeanPostprocess(实例化,调用构造函数)开始起作用了。
他会在InstantiationAwareBeanPostProcessor(初始化,即BeanDefination的初始化)接口方法执行完之后调用,查看其实现(before方法由于没有特别的处理这里就省略了)
最终会调用wrapIfNecessary方法判断该bean是否需要增强。进入方法
正常bean的创建会进入到isInfrastructureClass这个分支,isInfrastructureClass这个方法就是之前分析的判断是否是Aspect等注解的类,如果不是则调用getAdvicesAndAdvisorsForBean方法获取到符合该bean的通知方法(即相应的Advisor)。
最终调用createProxy创建代理对象。
最终进入代理工厂创建代理对象的方法,根据是否实现接口自动选择创建JDK动态代理(基于接口)或者是Cglib代理(基于子父类)。到这里切面以及要被代理的类就都创建完成了,接下来就是如何运行通知方法了。
执行流程
我们这里假设上一步创建的对象为Cglib对象,了解过Cglib代理的同学都知道实现代理要实现MethodInterceptor接口,在里面的intercept方法中进行方法的拦截。我们找到代理类的intercept方法
首先调用getInterceptorsAndDynamicInterceptionAdvice方法获取所有通知方法的Advisor拦截器链,chain不为空会依次调用对应的Advisor拦截器的proceed方法进行代理调用,在此会按照通知的顺序执行原方法与通知方法。
大体的执行流程就分析完了,有时间的同学最好简单写个demo然后跟着一步步debug,这样能够更清晰的了解流程。
最后总结一下,容器初始化时将切面等信息放入通知集合中,正常bean在创建时会判断该bean是否需要被增强,若需要增强,创建相应的代理对象。在执行时,代理对象执行相应的invoke方法,在方法中获取到通知集合并抽象成拦截器链,使用拦截器模式按照顺序执行相应的方法。
附两张流程图: