Spring AOP源码分析(三)——拦截器链的执行过程分析
前面两篇文章分别介绍了Spring AOP是如何为目标bean筛选合适的通知器,以及如何创建代理对象的过程。现在得到了bean的代理对象,接下来就是代理对象的方法调用及通知的执行。通知可能在目标方法前执行,也可能在目标方法后执行。具体的执行时机,取决于用户的配置。当目标方法被多个通知匹配到时,Spring通过引入拦截器链来保证每个通知的正常执行。本文将会通过源码分析Spring AOP是如何支持expose-proxy配置的,以及通知与拦截器之间的关系,拦截器链的执行过程等。
一、背景知识
关于expose-proxy配置,先来说说它有什么用,然后再来说说怎么用。Spring引入expose-proxy特性是为了解决目标方法调用同对象中其他方法时,其他方法的切面逻辑无法执行的问题。下面通过示例来演示一下它的用法,先来看看expose-proxy的配置:
<bean id="hello" class="xyz.coolblog.aop.Hello"/>
<bean id="aopCode" class="xyz.coolblog.aop.AopCode"/>
<aop:aspectj-autoproxy expose-proxy="true" />
<aop:config expose-proxy="true">
<aop:aspect id="myaspect" ref="aopCode">
<aop:pointcut id="helloPointcut" expression="execution(* xyz.coolblog.aop.*.hello*(..))" />
<aop:before method="before" pointcut-ref="helloPointcut" />
</aop:aspect>
</aop:config>
如上,expose-proxy可配置在 <aop:config/>
和 <aop:aspectj-autoproxy />
标签上。在使用expose-proxy时,需要对内部调用进行改造,比如:
public class Hello implements IHello {
@Override
public void hello() {
System.out.println("hello");
this.hello("world");
}
@Override
public void hello(String hello) {
System.out.println("hello " + hello);
}
}
hello()方法调用了同类中的另一个方法hello(String),此时hello(String)上的切面逻辑就无法执行。这里需要对hello()方法进行改造,强制它调用代理对象中的hello(String)。改造结果如下:
public class Hello implements IHello {
@Override
public void hello() {
System.out.println("hello");
((IHello) AopContext.currentProxy()).hello("world");
}
@Override
public void hello(String hello) {
System.out.println("hello " + hello);
}
}
如上,AopContext.currentProxy()用于获取当前的代理对象。当expose-proxy被配置为true时,该代理对象会被放入ThreadLocal(AopContext)中。
二、拦截器链的执行过程分析
下面分析的源码来自JdkDynamicAopProxy,至于CglibAopProxy中的源码,有机会再分析。
1. JDK动态代理的方法调用
对于JDK Proxy生成的动态代理,其方法调用时,执行逻辑封装在InvocationHandler接口实现类的invoke方法中。JdkDynamicAopProxy实现了InvocationHandler接口,下面就来分析一下JdkDynamicAopProxy的invoke方法。如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation; // 方法调用
Object oldProxy = null; // 旧的代理对象
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null; // 目标对象
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
// AOP不代理equals方法,直接使用JdkDynamicAopProxy实例的equals方法
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
// AOP不代理hashCode方法,直接使用JdkDynamicAopProxy实例的hashCode方法
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) { // getDecoratedClass方法
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// 方法的声明类是Advised接口或者TargetClassAware接口,以this.advised为实例调用该方法
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal; // 方法返回值
// 如果expose-proxy属性为true,则暴露代理对象
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy); // 向AopContext中设置代理对象
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget(); // 目标对象
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method. 获取适合当前方法的拦截器
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
// 如果拦截器链为空,则直接执行目标方法
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
// 适配方法参数
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); // 反射调用
}
else {
// We need to create a method invocation... 创建一个方法调用,并将拦截器链传入其中
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain. 执行拦截器链
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType(); // 获取方法返回值类型
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
// 如果方法返回值为this,即 return this; 则将代理对象proxy赋值给retVal
retVal = proxy;
}
// 如果返回值类型为基础类型,比如int,long等,当返回值为null,抛出异常
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy. // 还原老的代理
AopContext.setCurrentProxy(oldProxy);
}
}
}
上面invoke方法的执行流程如下:
- 检测expose-proxy配置是否为true,若为true,则暴露代理对象;
- 获取适合当前方法的拦截器;
- 如果拦截器链为空,则直接通过反射执行目标方法;
- 若拦截器链不为空,则创建ReflectiveMethodInvocation 对象;
- 调用ReflectiveMethodInvocation对象的proceed()方法启动拦截器链;
- 处理返回值,并返回该值。
在以上步骤中,重点关注第2步和第5步中的逻辑。
2.获取调用方法的拦截器
拦截器是指用于对目标方法的调用进行拦截的一种工具。下面给出前置通知拦截器的代码:
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
/** 方法前置通知 */
private final MethodBeforeAdvice advice;
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());
// 通过MethodInvocation调用下一个拦截器,若所有拦截器均执行完,则调用目标方法
return mi.proceed();
}
}
如上,前置通知的逻辑在目标方法执行前被执行。这里先简单介绍一下拦截器的作用,关于拦截器更多的描述将在后面分析。下面先来看看如何获取拦截器:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey); // 从缓存中获取
if (cached == null) { // 缓存未命中,则进行下一步处理
// 获取所有的拦截器
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached); // 存入缓存
}
return cached;
}
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
// registry为DefaultAdvisorAdapterRegistry类型
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
for (Advisor advisor : advisors) { // 遍历通知器列表
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
// 调用ClassFilter对bean类型进行匹配,无法匹配则说明当前通知器不适合应用在当前bean上。已经预过滤的直接进入下面的逻辑
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
}
else {
// 通过方法匹配器对目标方法进行匹配
match = mm.matches(method, actualClass);
}
if (match) {
// 将advisor中的advice转成相应的拦截器
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) { // 运行时需要调用3个参数的match方法进行匹配
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
// IntroductionAdvisor类型的通知器,仅需进行类级别的匹配即可
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
Advice advice = advisor.getAdvice();
// 若advice是MethodInterceptor类型的,直接添加到interceptors中即可。
// 比如AspectJAfterAdvice就实现了MethodInterceptor接口
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
// 对于AspectJMethodBeforeAdvice等类型的通知,由于没有实现MethodInterceptor
// 接口,所以这里需要通过适配器进行转换
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[0]);
}
以上就是获取拦截器的过程。这里简单总结一下以上代码的执行过程:
- 从缓存中获取当前方法的拦截器链;
- 若缓存未命中,则调用getInterceptorsAndDynamicInterceptionAdvice获取拦截器链;
- 遍历通知器列表;
- 对于PointcutAdvisor类型的通知器,这里要调用通知器所持有的切点(Pointcut)对类和方法进行匹配;对于IntroductionAdvisor,只需进行类级别的匹配即可。匹配成功说明应向当前方法织入通知逻辑。
- 调用getInterceptors方法,将Advisor转换为MethodInterceptor;
- 返回拦截器数组,并在随后存入缓存中。
这里需要说明一下,部分通知器是没有实现MethodInterceptor接口的,比如AspectJMethodBeforeAdvice。可以看一下前置通知适配器是如何将前置通知转为拦截器的,如下:
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();
// 创建MethodBeforeAdviceInterceptor拦截器
return new MethodBeforeAdviceInterceptor(advice);
}
}
如上所示,适配器的逻辑比较简单。现在已经获得了拦截器链,那接下来要做的事情就是启动拦截器。
3. 启动拦截器链
(1) 执行拦截器链
首先介绍一下ReflectiveMethodInvocation。ReflectiveMethodInvocation贯穿于拦截器链执行的始终。该类的 proceed方法用于启动拦截器链的执行,下面就去看看这个方法的逻辑。
private int currentInterceptorIndex = -1;
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 拦截器链中的最后一个拦截器执行完后,即可执行目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint(); // 调用目标方法
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
// 调用具有三个参数(3-args)的matches方法动态匹配目标方法,
// 两个参数(2-args)的matches方法用于静态匹配
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this); // 匹配后调用拦截器逻辑
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed(); // 如果匹配失败,则忽略当前的拦截器,调用下一个拦截器
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
// 调用拦截器逻辑,并传递当前ReflectiveMethodInvocation对象本身
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
如上,proceed方法根据currentInterceptorIndex来确定当前应执行哪个拦截器,并在调用拦截器的invoke方法时,将自己作为参数传给该方法。前面介绍了前置拦截器的代码,这里来看一下后置拦截器:
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
public AspectJAfterAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed(); // 调用proceed方法
}
finally {
// 调用后置通知逻辑
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
@Override
public boolean isBeforeAdvice() {
return false;
}
@Override
public boolean isAfterAdvice() {
return true;
}
}
如上,由于后置通知需要在目标方法返回后执行,所以AspectJAfterAdvice先调用mi.proceed()执行下一个拦截器逻辑,等下一个拦截器返回后,再执行后置通知逻辑。这里举个例子,假设目标方法method在执行前,需要执行两个前置通知和一个后置通知。下面看一下由三个拦截器组成的拦截器链是如何执行的:
注:这里用advice.after()表示执行后置通知。
本部分的最后,介绍一个特殊的拦截器,即ExposeInvocationInterceptor。在Spring AOP源码分析(一)——筛选合适的通知器这篇文章中,在介绍extendAdvisors方法时,有一个点没有详细说明。这里再贴一下 extendAdvisors方法的代码,如下:
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}
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循环用于检测advisors列表中是否存在
// AspectJ类型的Advisor或Advice
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;
}
}
// 向 advisors 列表的首部添加 DefaultPointcutAdvisor,
if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
return true;
}
}
return false;
}
private static boolean isAspectJAdvice(Advisor advisor) {
return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
advisor.getAdvice() instanceof AbstractAspectJAdvice ||
(advisor instanceof PointcutAdvisor &&
((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
}
如上,extendAdvisors方法所调用的方法会向通知器列表首部添加ExposeInvocationInterceptor.ADVISOR。
现在来看下ExposeInvocationInterceptor的源码:
public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable {
public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor();
// 创建DefaultPointcutAdvisor对象
public static final Advisor ADVISOR = new DefaultPointcutAdvisor(INSTANCE) {
@Override
public String toString() {
return ExposeInvocationInterceptor.class.getName() +".ADVISOR";
}
};
private static final ThreadLocal<MethodInvocation> invocation =
new NamedThreadLocal<>("Current AOP method invocation");
public static MethodInvocation currentInvocation() throws IllegalStateException {
MethodInvocation mi = invocation.get();
if (mi == null) {
throw new IllegalStateException(
"No MethodInvocation found: Check that an AOP invocation is in progress, and that the " +
"ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that " +
"advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor!");
}
return mi;
}
// 私有构造方法
private ExposeInvocationInterceptor() {
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get(); // 旧的方法调用
invocation.set(mi); // 设置新方法调用到ThreadLocal
try {
return mi.proceed(); // 执行拦截器链
}
finally {
invocation.set(oldInvocation); // 还原旧的方法调用
}
}
@Override
public int getOrder() {
return PriorityOrdered.HIGHEST_PRECEDENCE + 1;
}
private Object readResolve() {
return INSTANCE;
}
}
如上,ExposeInvocationInterceptor.ADVISOR经过DefaultAdvisorAdapterRegistry.getInterceptors方法(前面已分析)处理后,即可得到ExposeInvocationInterceptor。ExposeInvocationInterceptor的作用是用于暴露 MethodInvocation对象到ThreadLocal中。如果其他地方需要使用当前的MethodInvocation对象,直接通过调用 currentInvocation方法取出即可。
拦截器链的执行过程分析结束,下面来看一下目标方法的执行过程。
(2) 执行目标方法
protected Object invokeJoinpoint() throws Throwable {
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
throws Throwable {
// Use reflection to invoke the method.
try {
// 反射调用连接点方法
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
}
catch (InvocationTargetException ex) {
// Invoked method threw a checked exception.
// We must rethrow it. The client won't see the interceptor.
throw ex.getTargetException();
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
method + "] on target [" + target + "]", ex);
}
catch (IllegalAccessException ex) {
throw new AopInvocationException("Could not access method [" + method + "]", ex);
}
}
至此,代理对象的方法调用及拦截器链的执行过程分析结束。