一、SpringAOP基本概念
1、JoinPoint 连接点
连接点就是程序调用过程中经过的一些点,例如方法调用、字段访问、异常处理等。在SpringAOP中仅支持到方法级别的连接点。所以后面所有的操作都是针对于方法级别的AOP。
2、PointCut 切入点
由于所有方法上都具有连接点,而我们实际使用中只希望在指定的地方用AOP做增强处理,切入点的作用就是使用一组规则来匹配指定的连接点。可以看到AspectJExpressionPointcut实现了ClassFilter和MethodMatcher两个接口来实现对连接点的筛选。
3、Advice 通知
通知就是我们定义的横切逻辑,也就是需要在目标方法上增强的逻辑。上面的切点帮我们确定了“在那里”调用通知的能力,但还需要确定”何时“调用,所以通知有下面几种类型。
- 前置通知(Before Advice)
- 后置通知(After Advice)
- 返回通知(After Returning Advice)
- 环绕通知(Around Advice)
- 异常通知(After Throwing Advice)
4、Aspect/Advisor
在AOP的概念中还有一个切面的概念,它并没有具体的类或接口来定义,只是一个概念。一个切面整合了切点和通知两个模块。就拿做一件事情来说,切点解决了 ”在哪里“ 的问题,通知解决了 ”什么时候“ 和 ”怎么做“ 的问题,这两者结合就是一个切面。但在spring中增强器(Advisor )的作用和切面很像。一个增强器只包含一个切点和一个通知。
二、SpringAOP初始化流程
Aop的初始化流程会包含在IOC容器的初始化流程中,这里只关注AOP初始化部分。
主要逻辑
-
@EnableAspectJAutoProxy引入AnnotationAwareAspectJAutoProxyCreator 到容器。
这个东西看一下继承树是InstantiationBeanPostProcessor的子类,这意味着容器中所有Bean的实例化前
实例化后、初始化前、初始化后都会调用它的逻辑。而我们的AOP功能的实现就依赖这些钩子方法。
-
在Bean初始化前调判断该Bean是否被标注了@Aspect注解
-
如果标注了注解就对该类型中的方法进行判断,如果被标注了@Before @AfterRetuning等注解的话
就包装成一个增强器放入缓存。
-
在所有Bean实例化并初始化之后会执行AOP后置通知,通过上面缓存的增强器判断当前Bean是否需要被增强
-
如果需要被增强则判断使用哪种代理方式(JDK CGLIB),默认情况没有实现接口的话使用JDK,否则是CGLIb。当然@EnableAspectJAutoProxy中proxyTargetClass=true 的话就强制使用CGLIb。
-
最后返回被增强的代理对象,其中包含调用目标方法的执行链。这样就完成了增强
1、AnnotationAwareAspectJAutoProxyCreator创建增强器
我们对容器中的实例做增强,首先需要我们定义好的一个个增强器,所以增强器的创建则在后置处理器的
2、创建代理对象
1>AbstractApplicationContext#refresh
// 初始化所有非懒加载的单例
2>DefaultListableBeanFactory#preInstantiateSingletons
3>AbstractBeanFactory#getBean
4>AbstractBeanFactory#doGetBean
5>DefaultSingletonBeanRegistry#getSingleton
// 创建bean实例,填充bean实例,应用后处理器等。
6>AbstractAutowireCapableBeanFactory#createBean
// 这一步比较重要,在每个Bean实例化之前会调用后置处理器的postProcessBeforeInstantiation
// 方法,会尝试着让后置处理器去创建该Bean。如果后置处理器创建了Bean则直接返回
// 跳过后面的填充、调用初始化逻辑等..
// 而我们的AOP创建增强器则是在这一步完成,并不是直接在这里就创建代理对象了。
7>AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
// 循环调用所有后置处理器的postProcessBeforeInstantiation方法
// 来尝试对该Bean进行实例化,而这里我们只关注AOP相关的后置处理
8>AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation
9>AbstractAutoProxyCreator#postProcessBeforeInstantiation
// 判断是否应跳过,重写了父类的这个方法,这一步比较重要
// 因为是在这里获取容器中所有的增强器
10>AspectJAwareAdvisorAutoProxyCreator#shouldSkip
// 这里先调用父类的重载方法获取增强器,然后使用自己的逻辑再获取一遍。两个加起来
11>AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
// 【这一步非常重要】它从容器中拿出所有在容器中的类型
// 判断类上是不是有@Aspect注解,有的话解析其中的方法
// 生成一个一个的增强器,放到缓存中(advisorsCache)
12>BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
// 如果类上标注了@Aspect注解,获取它所有的方法进行遍历
13>ReflectiveAspectJAdvisorFactory#getAdvisors
// 通过AspectJExpressionPointcut类判断当前方法上是否标注了
// @After @Before...注解,如果标注了。则创建一个切入点(AspectJExpressionPointcut)
// 将之作为参数创建一个增强器(InstantiationModelAwarePointcutAdvisorImpl)返回
14>ReflectiveAspectJAdvisorFactory#getAdvisor
// 上面这些是创建并缓存增强器
// 这里开始创建Bean实例,中间跳过一些初始化填充属性的环节,直接到应用AOP后置处理器的逻辑
1>AbstractAutoProxyCreator#postProcessAfterInitialization
2>AbstractAutoProxyCreator#wrapIfNecessary
3>AbstractAutoProxyCreator#getAdvicesAndAdvisorsForBean
// 找到针对于当前Bean适用的增强器
// 我们之前缓存的增强器会在这里取出来,然后排序并返回
4>AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
// 先找出之前缓存的所有增强器
5>AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
// 从这些增强器中拿出切入点,然后循环遍历当前Bean的方法进行匹配。匹配上的就返回这个增强器。
// 所以一个类中的A B C 方法分别匹配了 a b c三种增强器。
// 那么创建这个对象的代理的时候就会有3个增强器。在真正调用A的时候再执行匹配选出a增强器
// 进行增强
6>AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply
// 这时候就创建好了代理对象
2>AbstractAutoProxyCreator#createProxy
3>ProxyFactory#getProxy()
4>ProxyCreatorSupport#createAopProxy
// 在这里进行判断,有接口就走JDK代理,没有就走CGLIB
5>DefaultAopProxyFactory#createAopProxy(AdvisedSupport config)
三、拦截器链的创建和执行
1、创建方法调用
这里演示CGLIB方式的动态代理,我们在调用目标方法时由于对象已经被代理增强,所以会执行到下面这个逻辑。主要就是从缓存中获取拦截器链,然后创建方法调用,由方法调用来驱动对整个拦截器链的执行。
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
// 判断是否暴露代理对象,如果是则将代理对象放到上下文中供以后使用
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取拦截器链接,即由多个MethodInterceptor组成的有序连
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 判断连接器连是否为空,如果为空则直接调用目标方法
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// 否则就创建一个方法调用对象,由MethodInvocation来驱动对拦截器链的遍历执行。
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
2、创建拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
// 先取缓存(也就是一个ConcurrentHahsMap)
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
// 缓存没有,则创建拦截器链
// 里面的逻辑就是
// 1、拿出适用于当前实例的所有增强器
// 2、挨个遍历这些增强器判断是否能够适用于当前的方法
// 3、如果能够适用于当前的方法则将增强器中组合的Advice拿出来进行下一步操作
// 4、如果这个Advice是一个MethodInvocation那就直接放到拦截器链中
// 5、如果不是则使用对应的适配器来包装一下放到拦截器链中
// 例如:AspectJMethodBeforeAdvice 需要被 MethodBeforeAdviceInterceptor包装
// 因为它只实现了BeforeAdvice。
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
3、调用拦截器链
拦截器链的调用入口即 CglibMethodInvocation.proceed()
public Object proceed() throws Throwable {
//判断当前的拦截器是否处于拦截器链的最后一个,是的话就直接调用目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 从拦截器链中拿出当前的拦截器(MethodInterceptor)
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 调用MethodINterceptor的Invoke方法并传入自己(MethodInvocation)
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
以异常通(AspectJAfterThrowingAdvice)知为例
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 直接调用MethodInvocation.proceed()去执行下一个拦截器
return mi.proceed();
}
catch (Throwable ex) {
// 如果抛出异常则调用异常通知
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
4、Aop调用流程
MethodInvocation.proceed()
方法会从连接器链中拿出一个拦截器去执行,同时推进拦截器的执行位置,并自己传入其中。MethodInterceptor.invoke(MethodInvocation)
则负责执行自己的业务逻辑以及再次调用步骤1的方法- 这种像是两个方法在递归彼此,比单个方法的自我递归高级。这叫责任链模式