Spring 源码阅读 64:AOP 代理获取拦截器链时 Advisor 与目标方法的匹配

315 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第30天,点击查看活动详情

基于 Spring Framework v5.2.6.RELEASE

相关阅读:Spring 源码阅读 61:基于 JDK 动态代理的 AOP 代理回调方法 invoke 分析

接上一篇:Spring 源码阅读 63:AOP 代理获取拦截器链的来源

概述

上一篇,分析了 JdkDynamicAopProxy 的invoke回调方法,根据被调用的目标方法查询拦截器链的流程,其中最重要的一部分,也就是如何从创建代理对象时配置的 Advisor 列表中找到与当前目标方法匹配的拦截器,单独放到本文中尽心分析。为了内容的完整性和连贯性,建议从之前的文章开始阅读。

获取拦截器链

我们接着分析之前,再看一下 DefaultAdvisorChainFactory 的getInterceptorsAndDynamicInterceptionAdvice方法。

在上一篇中,已经分析过了方法开头的几个局部变量,接下来进入核心的步骤,也就是for循环的部分,它的大致接口是这样的。

for (Advisor advisor : advisors) {
   if (advisor instanceof PointcutAdvisor) {
      // Add it conditionally.
      PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
      // 处理逻辑
   }
   else if (advisor instanceof IntroductionAdvisor) {
      IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
      // 处理逻辑
   }
   else {
      // 处理逻辑
   }
}

对于advisor列表中的每一个 Advisor,都会先对其进行具体实现类型的判断,在根据不同的类型,执行不同的逻辑。其中, PointcutAdvisor 是我们最常见也是比较重要的类型。

先看 PointcutAdvisor 部分的逻辑。

if (advisor instanceof PointcutAdvisor) {
   // Add it conditionally.
   PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
   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) {
         MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
         if (mm.isRuntime()) {
            // 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));
         }
      }
   }
}

其中,前半部分逻辑,是对 Advisor 进行筛选,如果与当前目标方法匹配, 那么,会将match的值设置为true,后半部分针对matchtrue的情况,也就是匹配成功的情况,根据 Advisor 获取拦截器列表并添加到最终的结果列表中。下面进行详细的分析。

为目标方法匹配增强逻辑

首先,将 Advisor 强转为 PointcutAdvisor 类型之后,有一个if判断,只有符合条件的 Advisor 才会进行处理。判断条件有两个,符合其一即可。第一个是configpreFiltered属性,这个属性是初始化 ProxyFactory 时设置的,值为true(参考 Spring 源码阅读 58:配置 ProxyFactory 的 Advisor 列表 最后一节)。如果这个值为false,第二个判断条件会判断当前的 PointcutAdvisor 的切入点是否和当前调用的目标方法的类型匹配。

匹配相关的逻辑,在之前的代理对象创建过程的分析中已经介绍过了,这里不再作为分析的重点。

符合上述条件之后,会创建一个用于匹配方法的 MethodMatcher,后续的核心逻辑就是通过它的matches方法,判断当前调用的目标方法是否与 PointcutAdvisor 匹配。如果匹配的话,match的值会被设置为true

对匹配的增强进行处理

匹配完之后,matchtrue的情况下,还需要对 Advisor 进行处理,处理的第一步就是调用registry的getInterceptors方法,获取 Advisor 对应的拦截器列表。这里的registry上一篇中介绍过了,它是一个全局单例的 DefaultAdvisorAdapterRegistry 对象。

获取到拦截器列表之后,会根据 MethodMatcher 是否是动态的,来决定是否将拦截器封装为 InterceptorAndDynamicMethodMatcher,最终添加到结果列表中。InterceptorAndDynamicMethodMatcher 会包含拦截器和 MethodMatcher。

MethodMatcher 是动态的,指的是即使此处match方法返回true,也需要在拦截器被执行时再次通过match方法匹配,也就是这里的匹配条件是动态的。

IntroductionAdvisor 和其他情况的处理

上面我们分析的是 PointcutAdvisor 类型的增强的处理,之后还有两种情况的处理。

else if (advisor instanceof IntroductionAdvisor) {
   IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
   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));
}

对这两者的执行逻辑,从 PointcutAdvisor 的处理过程中都能找到相同的步骤,在此就不进行详细的分析了。如果 Advisor 是 IntroductionAdvisor 类型,则判断其是否能与当前被调用的方法的类型匹配,通过匹配的 Advisor 获取到拦截器列表,并添加到最终的记过列表中。而对于除 PointcutAdvisor 和 IntroductionAdvisor 的其他情况,则直接获取拦截器列表,放入最终的结果列表中。

getInterceptors 方法

以上三种情况的处理逻辑中,除了想最终结果列表中添加拦截器之外,还有一个相同的步骤,就是通过registrygetInterceptors方法,根据 Advisor 获取拦截器列表。这也是一个值得深入分析的部分,对这个方法的分析,会放到下一篇中。

总结

本文分析了如何从代理对象的 Advisor 列表中,获取到与当前被调用的目标方法匹配的拦截器链,其中涉及到了 PointcutAdvisor 和 IntroductionAdvisor 类型的 Advisor 的处理,以及切点与类和方法的匹配。下一篇,将分析如何从 Advisor 中获取到拦截器链。