Skip to content

AOP 链式调用

熟悉 JDK 动态代理的都知道通过代理对象调用方法时,会进入到 InvocationHandler 对象的 invoke 方法,所以我们直接从 JdkDynamicAopProxy 类的这个方法开始:

java
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      MethodInvocation invocation;
      Object oldProxy = null;
      boolean setProxyContext = false;

      //从代理工厂中拿到TargetSource对象,该对象包装了被代理实例bean
      TargetSource targetSource = this.advised.targetSource;
      Object target = null;

      try {
          //被代理对象的equals方法和hashCode方法是不能被代理的,不会走切面
          //.......

          Object retVal;

          // 可以从当前线程中拿到代理对象
          if (this.advised.exposeProxy) {
              // Make invocation available if necessary.
              oldProxy = AopContext.setCurrentProxy(proxy);
              setProxyContext = true;
          }

          //这个target就是被代理实例
          target = targetSource.getTarget();
          Class<?> targetClass = (target != null ? target.getClass() : null);

          //从代理工厂中拿过滤器链 Object是一个MethodInterceptor类型的对象,其实就是一个advice对象
          List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

          //如果该方法没有执行链,则说明这个方法不需要被拦截,则直接反射调用
          if (chain.isEmpty()) {
              Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
              retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
          }
          else {
              invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, 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())) {
              retVal = proxy;
          }
          return retVal;
      }
      finally {
          if (target != null && !targetSource.isStatic()) {
              // Must have come from TargetSource.
              targetSource.releaseTarget(target);
          }
          if (setProxyContext) {
              // Restore old proxy.
              AopContext.setCurrentProxy(oldProxy);
          }
      }
  }
}

首先来看 this.advised.exposeProxy 这个属性,这在@EnableAspectJAutoProxy 注解中可以配置,当为 true 时,会将该代理对象设置到当前线程的 ThreadLocal 对象中, 这样就可以通过 AopContext.currentProxy 拿到代理对象。

这个有什么用呢?我相信有经验的 Java 开发都遇到过这样一个 BUG,在 Service 实现类中调用本类中的另一个方法时,事务不会生效, 这是因为直接通过 this 调用就不会调用到代理对象的方法,而是原对象的,所以事务切面就没有生效。 因此这种情况下就可以从当前线程的 ThreadLocal 对象拿到代理对象,不过实际上直接使用@Autowired 注入自己本身也可以拿到代理对象。 接下来就是通过 getInterceptorsAndDynamicInterceptionAdvice 拿到执行链,看看具体做了哪些事情:

java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
        Advised config, Method method, @Nullable Class<?> targetClass) {

    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    //从代理工厂中获得该被代理类的所有切面advisor,config就是代理工厂对象
    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;
            //如果切面的pointCut和被代理对象是匹配的,说明是切面要拦截的对象
            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 {
                    //接下来判断方法是否是切面pointcut需要拦截的方法
                    match = mm.matches(method, actualClass);
                }
                //如果类和方法都匹配
                if (match) {

                    //获取到切面advisor中的advice,并且包装成MethodInterceptor类型的对象
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    if (mm.isRuntime()) {
                        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;
            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;
}

看关键的部分,因为之前我们创建的基本上都是 InstantiationModelAwarePointcutAdvisorImpl 对象, 该类是 PointcutAdvisor 的实现类,所以会进入第一个 if 判断里,这里首先进行匹配,看切点和当前对象以及该对象的哪些方法匹配,如果能匹配上,则调用 getInterceptors 获取执行链:

java
private final List<AdvisorAdapter> adapters = new ArrayList<>(3);
public DefaultAdvisorAdapterRegistry() {
    registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
    registerAdvisorAdapter(new AfterReturningAdviceAdapter());
    registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<>(3);
    Advice advice = advisor.getAdvice();
    //如果是MethodInterceptor类型的,如:AspectJAroundAdvice
    //AspectJAfterAdvice
    //AspectJAfterThrowingAdvice
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }

    //处理 AspectJMethodBeforeAdvice  AspectJAfterReturningAdvice
    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]);
}

这里我们可以看到如果是 MethodInterceptor 的实现类,则直接添加到链中,如果不是,则需要通过适配器去包装后添加, 刚好这里有 MethodBeforeAdviceAdapter 和 AfterReturningAdviceAdapter 两个适配器对应上文两个没有实现 MethodInterceptor 接口的类。最后将 Interceptors 返回。

java
if (chain.isEmpty()) {
	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();
}

返回到 invoke 方法后,如果执行链为空,说明该方法不需要被增强,所以直接反射调用原对象的方法(注意传入的是 TargetSource 封装的被代理对象); 反之,则通过 ReflectiveMethodInvocation 类进行链式调用,关键方法就是 proceed:

java
private int currentInterceptorIndex = -1;

public Object proceed() throws Throwable {
    //如果执行链中的advice全部执行完,则直接调用joinPoint方法,就是被代理方法
    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 {
        //调用MethodInterceptor中的invoke方法
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

这个方法的核心就在两个地方:invokeJoinpoint 和 interceptorOrInterceptionAdvice.invoke(this)。 当增强方法调用完后就会通过前者调用到被代理的方法,否则则是依次调用 Interceptor 的 invoke 方法。下面就分别看看每个 Interceptor 是怎么实现的。

这里的调用顺序是怎样的呢?其核心就是通过 proceed 方法控制流程,每执行完一个 Advice 就会回到 proceed 方法中调用下一个 Advice。

但是这只是只有一个切面类的情况,如果有多个@Aspect 类呢,这个调用过程又是怎样的?

AOP 扩展知识

自定义全局拦截器 Interceptor

在上文创建代理对象的时候有这样一个方法:

java
protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
    //自定义MethodInterceptor.拿到setInterceptorNames方法注入的Interceptor对象
    Advisor[] commonInterceptors = resolveInterceptorNames();

    List<Object> allInterceptors = new ArrayList<>();
    if (specificInterceptors != null) {
        allInterceptors.addAll(Arrays.asList(specificInterceptors));
        if (commonInterceptors.length > 0) {
            if (this.applyCommonInterceptorsFirst) {
                allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
            }
            else {
                allInterceptors.addAll(Arrays.asList(commonInterceptors));
            }
        }
    }

    Advisor[] advisors = new Advisor[allInterceptors.size()];
    for (int i = 0; i < allInterceptors.size(); i++) {
        //对自定义的advice要进行包装,把advice包装成advisor对象,切面对象
        advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
    }
    return advisors;
}

这个方法的作用就在于我们可以扩展我们自己的 Interceptor,首先通过 resolveInterceptorNames 方法获取到通过 setInterceptorNames 方法设置的 Interceptor, 然后调用 DefaultAdvisorAdapterRegistry.wrap 方法将其包装为 DefaultPointcutAdvisor 对象并返回:

java
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
    if (adviceObject instanceof Advisor) {
        return (Advisor) adviceObject;
    }
    if (!(adviceObject instanceof Advice)) {
        throw new UnknownAdviceTypeException(adviceObject);
    }
    Advice advice = (Advice) adviceObject;
    if (advice instanceof MethodInterceptor) {
        return new DefaultPointcutAdvisor(advice);
    }
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            return new DefaultPointcutAdvisor(advice);
        }
    }
    throw new UnknownAdviceTypeException(advice);
}

public DefaultPointcutAdvisor(Advice advice) {
    this(Pointcut.TRUE, advice);
}

需要注意 DefaultPointcutAdvisor 构造器里面传入了一个 Pointcut.TRUE,表示这种扩展的 Interceptor 是全局的拦截器。下面来看看如何使用:

java
public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        System.out.println("自定义拦截器");
        return invocation.proceed();
    }
}

首先写一个类实现 MethodInterceptor 接口,在 invoke 方法中实现我们的拦截逻辑,然后通过下面的方式测试,只要 UserService 有 AOP 拦截就会发现自定义的 MyMethodInterceptor 也生效了。

java
public void costomInterceptorTest() {
    AnnotationAwareAspectJAutoProxyCreator bean = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
    bean.setInterceptorNames("myMethodInterceptor");

    UserService userService = applicationContext.getBean(UserService.class);
    userService.queryUser("dark");
}

但是如果换个顺序,像下面这样:

java
public void costomInterceptorTest() {
    UserService userService = applicationContext.getBean(UserService.class);
    AnnotationAwareAspectJAutoProxyCreator bean = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
    bean.setInterceptorNames("myMethodInterceptor ");
    userService.queryUser("dark");
}

这时自定义的全局拦截器就没有作用了,这是为什么呢?

因为当执行 getBean 的时候,如果有切面匹配就会通过 ProxyFactory 去创建代理对象,注意 Interceptor 是存到这个 Factory 对象中的, 而这个对象和代理对象是一一对应的,因此调用 getBean 时,还没有 myMethodInterceptor 这个对象,自定义拦截器就没有效果了, 也就是说要想自定义拦截器生效,就必须在代理对象生成之前注册进去。

其他博客参考:https://blog.csdn.net/l6108003/article/details/106577515/