概述
Spring aop通过动态代理的方式实现,在使用spring-aop时,工程中会引入一个非spring的jar包aopalliance包,spring5中将aopalliance包中的class打进了spring-aop包中(为了减少依赖?),先看看aopalliance包中的内容:
Aopalliance包非常简单,只包括aop相关的几个接口,可将其中的接口分为两类:
1.实体域:AOP所操作的核心实体
实体域是AOP中的核心概念,核心接口是Advice,其中MethodInterceptor和ConstructorInterceptor是Advice的子接口
2. 会话域:AOP运行时的上下文相关数据
这类对象中最主要的接口是Joinpoint,Invocation以及MethodInvocation和ConstructorInvocation接口都是Joinpoint的子接口
Spring-AOP是aopalliance的实现,并对aopalliance做了加强,增加了另外的实体——PointCut,PointCut表示切入点,即在哪些类的哪些方法上使用Advice
Spring AOP的设计
Spring AOP提供了多种使用方式,常用的有注释和XML,但注解和XML是非常简单易用的上层API,我们先看一下Spring AOP的更底层的API PointcutAdvisor接口,写一个PointcutAdvisor的实现类:
这里省略了getter/setter。可以看到,需要实现三个方法,先实现getPointcut方法,它需要返回一个Pointcut接口的对象,我们这里返回单例对象即可,本例简单起见,不管是否是单例,能说明问题即可,Pointcut接口中有两个方法,分别用于决定某个类是否要使用AOP的getClassFilter方法和决定某个方法是否需要使用AOP的getMethodMatcher方法。在getClassFilter方法的实现上,本例判断如果switchOn字段的值为true且 class被@SwitchOn注解标记,则返回true,否则返回false;getMethodMatcher方法返回的MethodMatcher接口要复杂一点,它包含三个方法,其中isRuntime返回true时表示运行时决定是否代理,如果isRuntime为true,则运行时会调用带args参数的matches方法,否则调用不带args参数的matches方法,代码实现如下:
Pointcut接口遵循了单一职责原则和接口分离原则,它没有将ClassFilter接口和MethodMatcher接口混在一起。
接下来实现Advice接口,这里由创建SwitchableAnnotationPointcutAdvisor时通过setter方法的调用设置advice:
最后使用这个advisor,需要创建一个Advice,以打日志为例:
注意:MethodInterceptor是Advice的子接口。
最后定义一个@Log注解,用于表示打了@Log注解的方法才打日志:
不同的环境可设置不同的switchOn的值,最后在需要打日志的方法调用上加上@Log注解,同时在其类上加上@SwitchOn注解即可:
以上是Spring-AOP的底层API的使用示例,对于AOP的其它更高层次的API和简单的使用方式,最终都是转化成Advisor被执行的。根据spring的xml解析机制可以发现<aop:config/>标签实际上是向spring中注册了一个代理对象创建类AspectJAwareAdvisorAutoProxyCreator,关键代码:
AspectJAwareAdvisorAutoProxyCreator类是一个BeanPostProcessor,创建动态代理对象的入口在其postProcessAfterInitializing方法中:
postProcessAfterInitializing方法的具体实现在抽象类AbstractAutoProxyCreator中,AbstractAutoProxyCreator类中处理了AOP代理对象的创建相关的逻辑,而AbstractAdvisorAutoProxyCreator类是继承自AbstractAutoProxyCreator,封装了Advisor的使用相关的逻辑,具体类AspectJAwareAdvisorAutoProxyCreator对父类中的几个方法做了覆盖,加入AspectJPointcutAdvisor相关的特定逻辑逻辑。
在AbstractAdvisorAutoProxyCreator中,与advisor相关的核心代码如下:
其中包含findCandidateAdvisors和findAdvisorsThatCanApply两个方法,其中前者用于拿到所有的Advisor,后者判断当前bean是否需要当前Advisor提供的切面逻辑(通过PointcutAdvisor中的Pointcut或者IntroductionAdvisor中的ClassFilter)。findCandidateAdvisors方法的实现可以有多种策略,默认实现是从BeanFactory中获取所有的Advisor接口的实例。
对于@Aspect注解定义的AOP切面逻辑,需要在xml配置文件中配置支持aspectj代理的标签<aop:aspectj-autoproxy/>,根据前面的xml解析机制,可以找到解析aspectj-autoproxy标签的类,它实际上是向spring中注入了AnnotationAwareAspectJAutoProxyCreator:
AnnotationAwareAspectJAutoProxyCreator类是AspectJAwareAdvisorAutoProxyCreator类的子类,其中实现对@Aspect等注解的处理,AnnotationAwareAspectJAutoProxyCreator覆盖了findCandidateAdvisors方法,实现了注解相关的Advisor创建逻辑,即从BeanFactory中获取标识了@Aspect注解的bean,并创建出Advisor:
通过aspectJAdvisorsBuilder.buildAspectJAdvisors的调用中可以找到创建Advisor使用了ReflectiveAspectJAdvisorFactory类,以下创建Advisor的核心代码:
最终会使用InstantiationModelAwarePointcutAdvisorImpl作为对应的Advisor。
最后再看看代理对象的创建过程,找到AbstractAutoProxyCreator类中创建代理对象的方法createProxy方法:
在proxyFactory.getProxy方法中就进入到了创建动态代理的阶段:
这里以jdk动态代理为例,JdkDynamicAopProxy就是一个InvocationHandler的实现,通过此InvocationHandler创建jdk动态代理,JdkDynamicAopProxy类的invoke方法实现比较长,其关键代码如下:
简单点说就是通过Advisor创建出拦截器链,拦截器链由advice组成,再通过拦截器链创建MethodInvocation对象,并调用proceed方法执行拦截器链。