前言
理一理AOP与切面(Aspect)、通知(Advice)的关系
概念
-
通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
-
连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
-
切点(PointCut): 可以插入增强处理的连接点。
-
切面(Aspect): 切面是通知和切点的结合。
做了张图
-
PointCut:指的是具体要被切的方法
-
JoinPoint:指的是切点的哪里要被切,执行前就是@before,执行后就是@after
-
Advice: 每个JoinPoint对应一个Advice,具体执行的方法就称为Advice
-
Aspect: JoinPoint+Advice组成了一个切面类,如代码定义的那样
实现
- 这部分主要讲一下AOP,AOP就是Aspect Oriented Programming(面向切面编程),主要通过上述几个组件实现的,那么具体源码层次是怎么实现的呢?
- 在之前的文章中,我对AOP进行了一次探究,并没有进行总结,在这里做一下总结
流程图
看完源码我喜欢画一张流程图,AOP也不例外
黄色部分(后置处理器注入)
- @EnableAspectJAutoProxy开启AOP
- 其中包括了@Import(AspectJAutoProxyRegistrar.class),熟悉IOC原理的知道,Spring会将@Import注解导入的类导入容器中
- AspectJAutoProxyRegistrar 是一个ImportBeanDefinitionRegistrar,因此作用就是进行Bean组件的注册
- 其调用registerBeanDefinitions最终将AnnotationAwareAspectJAutoProxyCreator注册到容器中
- 而AnnotationAwareAspectJAutoProxyCreator继承了AbstractAutoProxyCreator
- AbstractAutoProxyCreator是一个BeanPostProcessor
- 至此,与AOP相关的处理器注册完成
红色部分(初始化Bean)
- 这部分是简略版的IOC初始化流程
- 切点对应的代理对象这时候就会被创建
- 主要是在initializeBean时,执行了applyBeanPostProcessorsAfterInitialization
- 即调用对应BeanPostProcessor的postProcessAfterInitialization方法,与AOP相关的BeanPostProcessor就是黄色部分注入的AbstractAutoProxyCreator
蓝色部分(生成代理对象)
- 在AbstractAutoProxyCreator的postProcessAfterInitialization方法中
- 会先通过getAdvicesAndAdvisorsForBean获取Bean对应的Advice
- 这里的Advice就是在Spring扫描定义好的@Aspect切面类时保存好的,具体Advice扫描过程在此不展开,上述截图例子中,Advice就对应那三个方法
- 接着,将相关的advice作为参数,传到createProxy方法中,这一步通过CGlib为Bean生成一个代理对象,并且其中保存了advice
最终就能返回一个代理Bean,并将其注入容器中
至于代理对象执行对应方法时,advice的调用过程这里就不分析了,前一篇AOP源码解析中有讲
简单的说就是,遍历advice,不同连接点对应的advice实现的接口不同,通过接口类型的判断,得出不同的advice执行的时间,进而先执行advice,就能实现AOP的功能了
Advice
由于Advice在后面理解事务底层实现上要用到,在此进一步进行分析
上面也提到了,不同连接点对应的Advice实现的接口类型不同,我们通过debug可以观察到上述例子在生成代理对象时会创建哪些advice
一次点开,可以发现分别为
也就意味着,如果我们要自定义Advice,并让其起作用的话,我们只需要实现相关连接点的接口,并且能让AOP在创建代理对象时扫描到,即可实现自定义,这部分在事务解析时会提及
适配器模式
我们发现,在创建代理对象时,涉及了一个Interceptor,四个advice,调用代理对象方法时,获取到的调用链chain确是三个Interceptor,2个advice,为什么?
首先,调用Cglib代理对象方法时,会调用intercept方法,其中就会完成方法链的调用,如下所示
我们可以看看proceed()做了什么,如下图所示,可以看到其实主要是调用invoke方法,并且invoke方法是MethodInterceptor接口定义的
也就意味着,如果advice要在代理方法被调用时执行,那必须是MethodInterceptor接口的
那么我们看看在创建代理对象时,绑定的四个adivce有没有实现MethodInterceptor接口
可以看到四个中有两个实现了MethodInterceptor接口,分别为AspectJAfterThrowingAdvice、AspectJAfterAdvice,这两个advice最后也原封不动地在chain调用链中出现,那么其他两个怎么办呢?
为了将没有实现MethodInterceptor接口的advice实现接口,就需要适配器模式了
对应的适配器接口为AdvisorAdapter,其定义了两个方法
public interface AdvisorAdapter {
boolean supportsAdvice(Advice advice);
MethodInterceptor getInterceptor(Advisor advisor);
}
可以看到只需要调用AdvisorAdapter的getInterceptor,传入一个advisor,就能够将其适配为MethodInterceptor
具体看看其中一个实现 MethodBeforeAdviceAdapter,getInterceptor返回的是MethodBeforeAdviceInterceptor
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
MethodBeforeAdviceInterceptor相当于在调用invoke的时候,执行了MethodBeforeAdvice的before方法
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;
/**
* Create a new MethodBeforeAdviceInterceptor for the given advice.
* @param advice the MethodBeforeAdvice to wrap
*/
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
}
那么这个适配过程是什么时候发生的呢,当然就是在获取调用链chain时发生的
跟踪getInterceptorsAndDynamicInterceptionAdvice
再跟踪
而registry其实就是DefaultAdvisorAdapterRegistry,初始化时会初始化适配器
至此,适配器模式结束