spring aop (下)调用拦截链

时间:2022-06-23
本文章向大家介绍spring aop (下)调用拦截链,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

参考

1. JdkDynamicAopProxy

1.1 invoke

之前我们说到,当使用jdk动态代理时,会调用该类的getProxy方法生成一个代理对象,返回给外界调用。该类继承了InvocationHandler,所有代理对象的方法调用都会被拦截到该对象的invoke上。

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    @Nullable
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        ...

        try {
            ...
            // 获取与方法相关的那组拦截器
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

            // 判断方法上是否有一组拦截器组
            if (chain.isEmpty()) {
                // 方法上没有拦截器,不必创建拦截器链,直接用反射机制调用方法
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            }
            else {
                // 创建拦截器链
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // 调用拦截器链
                retVal = invocation.proceed();
            }

            ...
        }
        finally {
            ...
        }
    }
}

invoke所做的事情在上文代码注释已经写到。具体如下:

  • 执行List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);。首先拿到方法上关联的一组拦截器(数组形式保存)
    • 如果chain.isEmpty()为真,数组长度等于0,说明没有拦截器。 只需执行retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); },调用该方法即可
    • 如果chain.isEmpty()为假,则数组长度大于0,说明有拦截器,就需要:
      1. 执行retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);。根据拦截器数组,和其它参数,创建拦截器链
      2. 执行retVal = invocation.proceed();。调用这个拦截器链,它会依次调用上面的每个拦截器(虽然有的拦截器是动态的,其执行与否要靠临时检查决定)。最后,它还会调用方法本体。

现在有三处代码不太明白,我将在1.2、1.3、1.4章对它们分别进行分析:

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);

invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();

1.2 AopUtils.invokeJoinpointUsingReflection 反射机制调用方法

我们首先把最容易理解的方法给解决:

@Nullable
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) {
        ...
    }
    ...
}

显然就是用反射机制直接调用method,没什么特别的。也确实没必要搞复杂,因为刚才提到,这个方法是在拦截器组长度为0的时候调用的。 方法没有拦截器链的时候,直接调用方法就行了。

1.3 getInterceptorsAndDynamicInterceptionAdvice 获取拦截器组

代理类的invoke方法中,调用了List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);获取方法上的拦截器组。 该方法的返回值是List<Object>数组,Object类型如此宽泛,似乎比较奇怪,我们后面会解释。

介绍advised: 成员变量JdkDynamicAopProxy.advised是AdvisedSupport类型,它是一个配置类,是用来设置代理的,其内有各种配置参数。

/** Config used to configure this proxy. */
private final AdvisedSupport advised;

分析getInterceptorsAndDynamicInterceptionAdvice: 然后,this.advised.getInterceptorsAndDynamicInterceptionAdvice调用的方法,属于AdvisedSupport类:

// AdvisedSupport.java
/** Cache with Method as key and advisor chain List as value. */
private transient Map<MethodCacheKey, List<Object>> methodCache;
// AdvisedSupport.java
/**
 * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
 * for the given method, based on this configuration.
 * @param method the proxied method
 * @param targetClass the target class
 * @return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
 */
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
    MethodCacheKey cacheKey = new MethodCacheKey(method); // 根据method计算缓存key
    List<Object> cached = this.methodCache.get(cacheKey); // 用method计算得到的key,试图获取缓存的拦截器组
    if (cached == null) {
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                this, method, targetClass); // 没有缓存,要计算拦截器组。
        this.methodCache.put(cacheKey, cached); // 将拦截器组缓存起来。
    }
    return cached;
}

方法里既保留了英文注释原文,也附加了中文注释。这个方法有以下三点:

  1. 用途。用途是获取方法上的拦截器组。
  2. 缓存技巧。每个方法上都会关联一组拦截器,这个关联关系可以用一个Map保存起来。这正是methodCache变量做的事。 我们为每个Method生成一个key,并将Method对应的拦截器组保存的key对应的value中。 用第一次获取拦截器组时,要进行计算,但会缓存起来。下次再获取时,直接取缓存即可。
  3. 将拦截器组以List<Object> 形式返回。为什么类型是Object呢?注释里说了:@return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)这个数组包含的元素有两种:MethodInterceptor和InterceptorAndDynamicMethodMatcher。
    1. 前者是静态拦截器,与方法有固定的关联关系。
    2. 后者是动态的拦截器,它是否被调用,需要在拦截器内进行临时判断。其类名中的...DynamicMethodMatcher也说明,它与方法的匹配是动态的,即使加入了某个方法上的拦截器组,也不一定每次都被调用。

篇幅关系,不便再深入this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice,就先只讲到这个深度,之后会专门开个专题讲的。我们只需知道,这个函数能计算出方法对应的拦截器数组组即可。

1.4 创建、调用拦截链

JdkDynamicAopProxy的invoke方法中,当获取的拦截器组长度大于0时,会调用如下代码,

invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 创建拦截链
retVal = invocation.proceed(); // 调用拦截链

1.4.1 创建拦截器链

我们看下构造函数:

protected ReflectiveMethodInvocation(
        Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
        @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

    this.proxy = proxy; // 代理对象
    this.target = target; // 被代理对象
    this.targetClass = targetClass; // 被代理对象的类
    this.method = BridgeMethodResolver.findBridgedMethod(method); // 原方法
    this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments); // 方法的参数
    this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers; // 拦截器组。有静态和动态的
}

这里保存了一堆拦截链相关的变量,其含义已经说了。这里的method、arguments是原方法的本体和参数。interceptorsAndDynamicMethodMatchers是1.3章提过的,拦截器组,其中有静态和动态的拦截器。但你可能奇怪,target是真正要调用对象的本体,这个target怎么来的?请看下面我已经整理好的代码:

// JdkDynamicAopProxy.java
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
    ...
    this.advised = config;
}
...
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    ...
    TargetSource targetSource = this.advised.targetSource;
    ...
    target = targetSource.getTarget();
    ...
    invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
}

target来自于targetSource,targetSource来自于this.advised,this.advised是在构造时就设置好的。 我们记得在上篇文章讲过,通过AnnotationAwareAspectJAutoProxyCreator的父类方法postProcessAfterInitialization,将原本的Bean替换成了代理对象。 而在wrapIfNecessary中,将bean本体封装成SingletonTargetSource,传入了createProxy生成代理对象,我们跟踪查看其中的代码:

// AbstractAutoProxyCreator.java
Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// AbstractAutoProxyCreator.java
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
        @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    ...
    ProxyFactory proxyFactory = new ProxyFactory();
    ...
    proxyFactory.setTargetSource(targetSource);
    ...
    return proxyFactory.getProxy(getProxyClassLoader());
}

// AdvisedSupport.java
// proxyFactory.setTargetSource(targetSource);调用至此
@Override
public void setTargetSource(@Nullable TargetSource targetSource) {
    this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE);
}
// ProxyFactory.java
// proxyFactory.getProxy调用至此
public Object getProxy(@Nullable ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

// ProxyCreatorSupport.java
// createAopProxy()调用至此
protected final synchronized AopProxy createAopProxy() {
    ...
    return getAopProxyFactory().createAopProxy(this);
}
// DefaultAopProxyFactory.java
// getAopProxyFactory().createAopProxy(this);调用至此
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (...) {
        ...
        if (...) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

可见,原bean封装成SingletonTargetSource,传入了proxyFactory,而proxyFactory经过一番调用,把自己作为参数传给了createAopProxy,createAopProxy把它视为配置,创建了Aop对象,以便之后调用getProxy获取代理对象。 图如下:

生成代理对象时,bean对象的传递.gif

至此,拦截链创建时的各个参数含义和来源,尤其是拦截器组和原bean对象的来源都已明了,该对象概览如下图。

ReflectiveMethodInvocation.jpg

1.4.2 调用拦截链

根据代码,我们创建好拦截链后,第一次调用proceed方法(代码如下)。我们可以猜到,拦截链中每个拦截器的行为,和最后方法本体的调用,都在这个方法内完成了。

// 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();

proceed方法如下:

@Override
@Nullable
public Object proceed() throws Throwable {
    //  currentInterceptorIndex初始值为-1,且采用先自增的方式。
    // 当它等于拦截器数组长度-1时,说明所有拦截器都已被递归调用,此时调用原方法。
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    // 获取拦截器
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // 是动态的,需要动态匹配
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            // 动态匹配成功,调用该拦截器
            return dm.interceptor.invoke(this);
        }
        else {
            // 匹配失败,调用后面的拦截器和方法本体
            return proceed();
        }
    }
    else {
        // 是静态的,直接调用
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

proceed方法根据拦截器数组下标currentInterceptorIndex,取到拦截器。如果这个拦截器是动态的,需要动态匹配,并决定是否执行。 如果这个拦截器是静态的,则直接执行。 我们假设拦截器数组长度为3,存储的3个拦截器是[静态,动态,动态],第一次调用proceed时,currentInterceptorIndex为-1,先自增成为0,我们会取到第一个拦截器——静态拦截器,并直接调用之。 而第二次proceed的调用,我们交给MethodInterceptor执行。根据约定,它会在里面调用第二次proceed。

第一次proceed

MethodInterceptor调用第二次proceed后,我们来到proceed方法内。此时拦截器数组下标currentInterceptorIndex值为0,先自增成为1,取到第二个拦截器——动态拦截器。根据代码逻辑,我们要调用

if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {

进行动态匹配,若成功,则执行return dm.interceptor.invoke(this);,调用这个动态拦截器;若失败,则执行return proceed();,递归调用第三次proceed。我们假设此时匹配成功。

第二次proceed

MethodInterceptor调用第三次proceed后,我们再次来到proceed方法内,尽管进到的是同一个代码片段,但其实这三次proceed调用属于不同的调用。此时的方法调用栈如下:

调用栈

其实proceed被递归调用了,栈里面的方法都还没完全返回呢。 那么我们在第三次proceed时,拦截器数组下标currentInterceptorIndex为1,自增到2,我们会取到第三个拦截器——动态拦截器。既然是动态拦截器,就要进行动态匹配。我们假设此时匹配失败,那么会执行return proceed();,进行第四次proceed的调用。

第三次proceed

第四次proceed是在刚才的第三次proceed中调用的,此时的方法调用栈如下:

此时拦截器数组下标currentInterceptorIndex为2,等于数组的最大下标,说明说有拦截器都已被检查过,最后我们要执行方法本体了。这会是最后一次proceed调用。我们会执行如下代码:

// ReflectiveMethodInvocation.java
@Override
@Nullable
public Object proceed() throws Throwable {
    //  We start with an index of -1 and increment early.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    ...
}

@Nullable
protected Object invokeJoinpoint() throws Throwable {
    return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}

显然是通过反射机制方法本体。

第四次proceed

最后,递归方法栈会依次退出,并提交返回值。最后第一次proceed会返回值给retVal

retVal = invocation.proceed();

总结

  1. 根据class缓存对应的增强器
  2. 根据method缓存对应的增强器数组,或者说拦截器数组。一个拦截器数组就相当于一个拦截链。换句话说,每个method都会缓存一个拦截链。
  3. 每次调用一个方法时,首先要拿到缓存好的方法的拦截链,依次调用链上的拦截器,最后才调用方法本身。 拦截链里有两种拦截器:动态的、静态的(其实指的是MethodInterceptor和InterceptorAndDynamicMethodMatcher)。动态的拦截器在调用拦截链时,要临时判断是否符合调用条件。静态的拦截器在调用时,不用判断,直接调用。
  4. 拦截链的调用时通过proceed和MethodInterceptor.invoke轮流递归调用的,在最后一次proceed中会调用方法本体。