持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第33天,点击查看活动详情
基于 Spring Framework v5.2.6.RELEASE
概述
之前的三篇文章,分析了 JdkDynamicAopProxy 的invoke方法,如何获取到拦截器链,并通过 ReflectiveMethodInvocation 的proceed方法执行拦截器链的增强方法和目标方法的逻辑。在拦截器链的增强方法的执行逻辑中,分别分析了五种增强类型对应的拦截器中的invoke方法的逻辑。
但其实,在拦截器链中,还有一个不是这五种增强类型对应的拦截器,它就是 ExposeInvocationInterceptor,这个拦截器并不是与我们配置的增强方法对应的,所以很容易被忽略。
这个拦截器类型,其实我们之前的源码分析中是见过的,我们先回顾一下,它是什么时候被注册的。
ExposeInvocationInterceptor 的注册
Spring AOP 的特性,是通过在后处理器中创建目标 Bean 对象的代理对象来实现的。当 Bean 对象初始化完成之后,会在后处理器中查找与当前 Bean 对象的类型是配的增强逻辑,这个查找的逻辑是由 AbstractAdvisorAutoProxyCreator 的findEligibleAdvisors完成的。
// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
在这个方法中,前两行代码的作用是找出 Spring 容器中所有的增强以及从这些增强中筛选出与当前 Bean 得类型匹配的增强。之后,通过extendAdvisors方法,对这个增强列表进行了扩展操作。
// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#extendAdvisors
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
}
这个方法在 AbstractAdvisorAutoProxyCreator 中是一个空方法,但是,无论是 XML 配置的切面配置,还是注解配置的切面配置,其对应的后处理器实现类,并不是 AbstractAdvisorAutoProxyCreator 本身,而是它的子类,在子类 AspectJAwareAdvisorAutoProxyCreator 中,重写了这个方法,逻辑如下。
// org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#extendAdvisors
@Override
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}
这里执行了一个方法,我们进入这个方法。
// org.springframework.aop.aspectj.AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessary
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
// Don't add advisors to an empty list; may indicate that proxying is just not required
if (!advisors.isEmpty()) {
boolean foundAspectJAdvice = false;
for (Advisor advisor : advisors) {
// Be careful not to get the Advice without a guard, as this might eagerly
// instantiate a non-singleton AspectJ aspect...
if (isAspectJAdvice(advisor)) {
foundAspectJAdvice = true;
break;
}
}
if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
return true;
}
}
return false;
}
可以看到这个方法的逻辑并不复杂,其主要作用是,如果之前获取到的 Advisor 列表包含符合isAspectJAdvice的判断逻辑(我们配置的增强逻辑都符合这个条件),则向列表的开头增加一个ExposeInvocationInterceptor.ADVISOR 。
下面我们看一下这里添加的ExposeInvocationInterceptor.ADVISOR
ExposeInvocationInterceptor 是什么
我们直接找到 ExposeInvocationInterceptor 类和ADVISOR常量的定义。
public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable {
/** Singleton instance of this class. */
public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor();
/**
* Singleton advisor for this class. Use in preference to INSTANCE when using
* Spring AOP, as it prevents the need to create a new Advisor to wrap the instance.
*/
public static final Advisor ADVISOR = new DefaultPointcutAdvisor(INSTANCE) {
@Override
public String toString() {
return ExposeInvocationInterceptor.class.getName() +".ADVISOR";
}
};
// 省略其他代码
}
从以上的代码中可以看出:
- ExposeInvocationInterceptor 是一个 MethodInterceptor 的实现类,也就是和我们之前分析过的五种增强类型对应的拦截器一样,通过
invoke方法来实现其拦截器的逻辑。 ADVISOR常量,其实就是一个包含了一个 ExposeInvocationInterceptor 单例对象的 DefaultPointcutAdvisor 对象。
了解了这些,我们可以得出结论,这个拦截器其实和五种增强类型对应的拦截器很相似,因此,要分析它的作用,只要看它的invoke方法就行了。
ExposeInvocationInterceptor 的执行
下面看它的invoke方法源码。
// org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
invocation.set(mi);
try {
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}
方法中除了执行mi的proceed方法外,频繁操作了invocation成员变量,我们看一下这个它是什么。
private static final ThreadLocal<MethodInvocation> invocation =
new NamedThreadLocal<>("Current AOP method invocation");
它是一个 ThreadLocal 变量。在invoke方法中,会先获取到invocation中的内容,暂存在oldInvocation变量,然后把mi放到invocation中。当mi的proceed方法执行完之后,再将invocation中最初保存的内容还原。
因为 ExposeInvocationInterceptor 是拦截器链中第一个拦截器,因此,它的作用是,在进入拦截器执行逻辑的时候,将 MethodInvocation 方法调用对象暴露到 ThreadLocal 中,在拦截器链执行完之后再还原。根据之前的分析,我们也能知道,这里参数传入的值,就是在 JdkDynamicAopProxy 的invoke方法中创建的 ReflectiveMethodInvocation 对象。
这也是 ExposeInvocationInterceptor 过滤器起到的作用。
总结
本文分析了 Spring AOP 拦截器链中的一个特殊拦截器 ExposeInvocationInterceptor 的注册的时机以及它的作用。至此,基于 JDK 的 AOP 代理拦截器链执行的逻辑就分析完了。