转载

关于Spring AOP,除了动态代理、CGLIB,你还知道什么?

Spring 作为 Java 中最流行的框架,主要归功于其提供的 IOC 和 AOP 功能。本文将讨论 Spring AOP 的实现。第一节将介绍 AOP 的相关概念,若熟悉可跳过,第二节中结合源码介绍 Spring 是如何实现 AOP 的各概念。

1. AOP 概念

1.1 JoinPoint

进行织入操作的程序执行点。

常见类型:

  • 方法调用(Method Call):某个方法被调用的时点。

  • 方法调用执行(Method Call Execution):某个方法内部开始执行的时点。

    方法调用是在 调用对象 上的执行点,方法调用执行是在 被调用对象 的方法开始执行点。

  • 构造方法调用(Constructor Call):对某个对象调用其构造方法的时点。

  • 构造方法执行(Constructor Call Execution):某个对象构造方法内部开始执行的时点。

  • 字段设置(Field Set):某个字段通过 setter 方法被设置或直接被设置的时点。

  • 字段获取(Field Get):某个字段通过 getter 方法被访问或直接被访问的时点。

  • 异常处理执行(Exception Handler Execution):某些类型异常抛出后,异常处理逻辑执行的时点。

  • 类初始化(Class Initialization):类中某些静态类型或静态块的初始化时点。

1.2 Pointcut

Jointpoint 的表述方式。

常见表述方式:

  • 直接指定 Joinpoint 所在方法名称
  • 正则表达式
  • 特定的 Pointcut 表述语言

1.3 Advice

单一横切关注点逻辑的载体,织入到 Joinpoint 的横切逻辑。

具体形式:

  • Before Advice :Joinpoint 处之前执行。
  • After Advice :Joinpoint 处之后执行,细分为三种:
    • After Returning Advice :Joinpoint 处正常完成后执行。
    • After Throwing Advice :Joinpoint 处抛出异常后执行。
    • After Finally Advice :Joinpoint 处正常完成或抛出异常后执行。
  • Around Advice :包裹 Joinpoint,在 Joinpoint 之前和之后执行,具有 Before Advice 和 After Advice 的功能。
  • Introduction :为原有的对象添加新的属性或行为。

1.4 Aspect

对横切关注点逻辑进行模块化封装的 AOP 概念实体,包含多个 Pointcut 和相关 Advice 的定义。

1.5 织入和织入器

织入:将 Aspect 模块化的横切关注点集成到 OOP 系统中。

织入器:用于完成织入操作。

1.6 Target

在织入过程中被织入横切逻辑的对象。

将上述 6 个概念放在一块,如下图所示:

关于Spring AOP,除了动态代理、CGLIB,你还知道什么?

在了解 AOP 的各种概念后,下面将介绍 Spring 中 AOP 概念的具体实现。

2. Spring 中的实现

前文提到 AOP 的 Joinpoint 有多种类型,方法调用、方法执行、字段设置、字段获取等。而在 Spring AOP 中,仅支持 方法执行 类型的 Joinpoint,但这样已经能满足 80% 的开发需要,如果有特殊需求,可求助其他 AOP 产品,如 AspectJ。由于 Joinpoint 涉及运行时的过程,相当于组装好所有部件让 AOP 跑起来的最后一步。所以将介绍完其他概念实现后,最后介绍 Joinpoint 的实现。

2.1 Pointcut

由于 Spring AOP 仅支持方法执行类别的 Joinpoint,因此 Pointcut 需要定义被织入的方法,又因为 Java 中方法封装在类中,所以 Pointcut 需要 定义被织入的类和方法 ,下面看其实现。

Spring 用 org.springframework.aop.Pointcut 接口定义 Pointcut 的顶层抽象。

public interface Pointcut {
    
   // ClassFilter用于匹配被织入的类   
   ClassFilter getClassFilter();

   // MethodMatcher用于匹配被织入的方法
   MethodMatcher getMethodMatcher();

   // TruePoincut的单例对象,默认匹配所有类和方法
   Pointcut TRUE = TruePointcut.INSTANCE;
}
复制代码

我们可以看出, Pointcut 通过 ClassFilter MethodMatcher 的组合来定义相应的 Joinpoint。 Pointcut 将类和方法拆开来定义,是为了能够 重用 。例如有两个 Joinpoint,分别是 A 类的 fun() 方法和 B 类的 fun() 方法,两个方法签名相同,则只需一个 fun() 方法的 MethodMatcher 对象,达到了重用的目的, ClassFilter 同理。

下面了解下 ClassFilterMethodMatcher 如何进行匹配

ClassFilter 使用** matches 方法**匹配被织入的类,定义如下:

public interface ClassFilter {
    
	// 匹配被织入的类,匹配成功返回true,失败返回false
	boolean matches(Class<?> clazz);

    // TrueClassFilter的单例对象,默认匹配所有类
	ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
复制代码

MethodMatcher 也是使用 matches 方法 匹配被织入的方法,定义如下:

public interface MethodMatcher {
    
   // 匹配被织入的方法,匹配成功返回true,失败返回false
   // 不考虑具体方法参数
   boolean matches(Method method, Class<?> targetClass);
    
   // 匹配被织入的方法,匹配成功返回true,失败返回false
   // 考虑具体方法参数,对参数进行匹配检查
   boolean matches(Method method, Class<?> targetClass, Object... args);
   
   // 一个标志方法
   // false表示不考虑参数,使用第一个matches方法匹配
   // true表示考虑参数,使用第二个matches方法匹配
   boolean isRuntime();

   // TrueMethodMatcher的单例对象,默认匹配所有方法
   MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}
复制代码

看到 matches 方法的声明,你是否会觉得有点奇怪,在 ClassFilter 中不是已经对类进行匹配了吗,那为什么在 MethodMatchermatches 方法中还有一个 Class<?> targetClass 参数。请注意,这里的 Class<?> 类型参数将 不会进行匹配 ,而仅是 为了找到具体的方法 。例如:

public boolean matches(Method method, Class<?> targetClass) {
    
    Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    ...
}
复制代码

MethodMatcher 相比 ClassFilter 特殊在有 两个 matches 方法 。将 根据 isRuntime() 的返回结果决定 调用哪个。而 MethodMatcherisRuntime() 分为两个抽象类 StaticMethodMatcher (返回false,不考虑参数)和 DynamicMethodMatcher (返回true,考虑参数)。

Pointcut 也因 MethodMathcer 可分为 StaticMethodMatcherPointcut DynamicMethodMatcherPointcut ,相关类图如下所示:

关于Spring AOP,除了动态代理、CGLIB,你还知道什么?

DynamicMethodMatcherPointcut 本文将不介绍,主要介绍下类图中列出的三个实现类。

(1)NameMatchMethodPointcut

通过指定 方法名称 ,然后与方法的名称直接进行匹配,还支持 “*” 通配符。

public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut implements Serializable {
    
	// 方法名称
	private List<String> mappedNames = new ArrayList<>();

	// 设置方法名称
	public void setMappedNames(String... mappedNames) {
		this.mappedNames = new ArrayList<>(Arrays.asList(mappedNames));
	}


	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		for (String mappedName : this.mappedNames) {
            // 根据方法名匹配,isMatch提供“*”通配符支持
			if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
				return true;
			}
		}
		return false;
	}
    
	// ...
}
复制代码

(2)JdkRegexpMethodPointcut

内部有一个 Pattern 数组,通过指定 正则表达式 ,然后和方法名称进行匹配。

(3)AnnotationMatchingPointcut

根据目标对象是否存在指定类型的 注解 进行匹配。

2.2 Advice

Advice 为横切逻辑的载体,Spring AOP 中关于 Advice 的接口类图如下所示:

关于Spring AOP,除了动态代理、CGLIB,你还知道什么?

(1)MethodBeforeAdvice

横切逻辑将在 Joinpoint 方法之前执行 。可用于进行资源初始化或准备性工作。

public interface MethodBeforeAdvice extends BeforeAdvice {
    
	void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
    
}
复制代码

下面来实现一个 MethodBeforeAdvice ,看下其效果。

public class PrepareResourceBeforeAdvice implements MethodBeforeAdvice {
    
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("准备资源");
	}
    
}
复制代码

定义一个 ITask 接口:

public interface ITask {
    
	void execute();
    
}
复制代码

ITask 的实现类 MockTask

public class MockTask implements ITask {
    
   @Override
   public void execute() {
      System.out.println("开始执行任务");
      System.out.println("任务完成");
   }
    
}
复制代码

Main 方法如下, ProxyFactoryAdvisor 在后续会进行介绍,先简单了解下,通过 ProxyFactory 拿到代理类, Advisor 用于封装 PointcutAdvice

public class Main {
    
   public static void main(String[] args) {
      MockTask task = new MockTask();
      ProxyFactory weaver = new ProxyFactory(task);
      weaver.setInterfaces(new Class[]{ITask.class});
      // 内含一个NameMatchMethodPointcut
      NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
      // 指定NameMatchMethodPointcut的方法名
      advisor.setMappedName("execute");
      // 指定Advice
      advisor.setAdvice(new PrepareResourceBeforeAdvice());
      weaver.addAdvisor(advisor);
      ITask proxyObject = (ITask) weaver.getProxy();
      proxyObject.execute();
   }
    
}

/** output:
准备资源
开始执行任务
任务完成
**/
复制代码

可以看出在执行代理对象 proxyObjectexecute 方法时,先执行了 PrepareResourceBeforeAdvice 中的 before 方法。

(2)ThrowsAdvice

横切逻辑将在 Joinpoint 方法抛出异常时执行 。可用于进行异常监控工作。

ThrowsAdvice 接口未定义任何方法,但约定在实现该接口时, 定义的方法需符合如下规则

void afterThrowing([Method, args, target], ThrowableSubclass)
复制代码

前三个参数为 Joinpoint 的相关信息,可省略。 ThrowableSubclass 指定需要拦截的异常类型。

例如可定义多个 afterThrowing 方法捕获异常:

public class ExceptionMonitorThrowsAdvice implements ThrowsAdvice {
    
	public void afterThrowing(Throwable t) {
		System.out.println("发生【普通异常】");
	}

	public void afterThrowing(RuntimeException e) {
		System.out.println("发生【运行时异常】");
	}

	public void afterThrowing(Method m, Object[] args, Object target, ApplicationException e) {
		System.out.println(target.getClass() + m.getName() + "发生【应用异常】");
	}
    
}
复制代码

修改下 MockTask 的内容:

public class MockTask implements ITask {
    
	@Override
	public void execute() {
		System.out.println("开始执行任务");
        // 抛出一个自定义的应用异常
		throw new ApplicationException();
        // System.out.println("任务完成");
	}
    
}
复制代码

修改下 Main 的内容:

public class Main {
    
	public static void main(String[] args) {
		MockTask task = new MockTask();
		ProxyFactory weaver = new ProxyFactory(task);
		weaver.setInterfaces(new Class[]{ITask.class});
		NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
		advisor.setMappedName("execute");
        // 指定异常监控Advice
		advisor.setAdvice(new ExceptionMonitorThrowsAdvice());
		weaver.addAdvisor(advisor);
		ITask proxyObject = (ITask) weaver.getProxy();
		proxyObject.execute();
	}
    
}

/** output:
开始执行任务
class com.chaycao.spring.aop.MockTaskexecute发生【应用异常】
**/
复制代码

当抛出 ApplicationException 时,被相应的 afterThrowing 方法捕获到。

(3)AfterReturningAdvice

横切逻辑将在 Joinpoint 方法正常返回时执行 。可用于处理资源清理工作。

public interface AfterReturningAdvice extends AfterAdvice {
    
   void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
    
}
复制代码

实现一个资源清理的 Advice :

public class ResourceCleanAfterReturningAdvice implements AfterReturningAdvice {
    
   @Override
   public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
      System.out.println("资源清理");
   }
    
}
复制代码

修改 MockTask 为正常执行成功, 修改 Main 方法为指定 ResourceCLeanAfterReturningAdvice ,效果如下:

/** output:
开始执行任务
任务完成
资源清理
**/
复制代码

(4)MethodInterceptor

相当于 Around Advice,功能十分强大, 可在 Joinpoint 方法前后执行,甚至修改返回值 。其定义如下:

public interface MethodInterceptor extends Interceptor {
    
	Object invoke(MethodInvocation invocation) throws Throwable;
    
}
复制代码

MethodInvocation 是对 Method 的封装,通过 proceed() 对方法进行调用。下面举个例子:

public class AroundMethodInterceptor implements MethodInterceptor {

   @Override
   public Object invoke(MethodInvocation invocation) throws Throwable {
      System.out.println("准备资源");
      try {
         return invocation.proceed();
      } catch (Exception e) {
         System.out.println("监控异常");
         return null;
      } finally {
         System.out.println("资源清理");
      }
   }
   
}
复制代码

上面实现的 invoke 方法,一下子把前面说的三种功能都实现了。

以上 4 种 Advice 会在目标对象类的所有实例上生效,被称为 per-class 类型的 Advice。还有一种 per-instance 类型的 Advice,可为实例添加新的属性或行为,也就是第一节提到的 Introduction。

(5)Introduction

Spring 为目标对象 添加新的属性或行为 ,需要 声明接口和其实现类 ,然后通过 拦截器 将接口的定义和实现类的实现织入到目标对象中。我们认识下 DelegatingIntroductionInterceptor ,其作为拦截器,当调用新行为时,会委派(delegate)给实现类来完成。

例如,想在原 MockTask 上进行加强,但不修改类的声明,可声明一个新的接口 IReinfore

public interface IReinforce {
   String name = "增强器";
   void fun();
}
复制代码

再声明一个接口的实现类:

public class ReinforeImpl implements IReinforce {

	@Override
	public void fun() {
		System.out.println("我变强了,能执行fun方法了");
	}

}
复制代码

修改下 Main 方法:

public class Main {
   
   public static void main(String[] args) {
      MockTask task = new MockTask();
      ProxyFactory weaver = new ProxyFactory(task);
      weaver.setInterfaces(new Class[]{ITask.class});
      // 为拦截器指定需要委托的实现类的实例
      DelegatingIntroductionInterceptor delegatingIntroductionInterceptor =
            new DelegatingIntroductionInterceptor(new ReinforeImpl());
      weaver.addAdvice(delegatingIntroductionInterceptor);
      ITask proxyObject = (ITask) weaver.getProxy();
      proxyObject.execute();
      // 使用IReinfore接口调用新的属性和行为
      IReinforce reinforeProxyObject = (IReinforce) weaver.getProxy();
      System.out.println("通过使用" + reinforeProxyObject.name);
      reinforeProxyObject.fun();
   }
   
}

/** output:
开始执行任务
任务完成
通过使用增强器
我变强了,能执行fun方法了
**/
复制代码

代理对象 proxyObject 便通过拦截器,可以使用 ReinforeImpl 实现类的方法。

2.3 Aspect

Spring 中用 Advisor 表示 Aspect,不同之处在于 Advisor 通常只持有 一个 Pointcut 一个 Advice Advisor 根据 Advice 分为 PointcutAdvisor IntroductionAdvisor

2.3.1 PointcutAdvisor

常用的 PointcutAdvisor 实现类有:

(1) DefaultPointcutAdvisor

最通用的实现类,可以指定 任意类型的 Pointcut 除了 Introduction 外的任意类型 Advice

Pointcut pointcut = ...; // 任意类型的Pointcut
Advice advice = ...; // 除了Introduction外的任意类型Advice
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
advisor.setAdvice(advice);
复制代码

(2)NameMatchMethodPointcutAdvisor

在演示 Advice 的代码中,已经有简单介绍过, 内部有一个 NameMatchMethodPointcut 的实例 ,可持有除 Introduction 外的任意类型 Advice

Advice advice = ...; // 除了Introduction外的任意类型Advice
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
advisor.setMappedName("execute");
advisor.setAdvice(advice);
复制代码

(3)RegexpMethodPointcutAdvisor

内部有一个 RegexpMethodPointcut 的实例。

2.3.2 IntroductionAdvisor

只能支持类级别的拦截,和 Introduction 类型的 Advice 。实现类有 DefaultIntroductionAdvisor

DelegatingIntroductionInterceptor introductionInterceptor =
				new DelegatingIntroductionInterceptor(new ReinforeImpl());
		DefaultIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(introductionInterceptor, IReinforce.class);
复制代码

2.4 织入和织入器

在演示 Advice 的代码中,我们使用 ProxyFactory 作为织入器

MockTask task = new MockTask();
// 织入器
ProxyFactory weaver = new ProxyFactory(task);
weaver.setInterfaces(new Class[]{ITask.class});
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
advisor.setMappedName("execute");
advisor.setAdvice(new PrepareResourceBeforeAdvice());
weaver.addAdvisor(advisor);
// 织入,返回代理对象
ITask proxyObject = (ITask) weaver.getProxy();
proxyObject.execute();
复制代码

ProxyFactory 生成代理对象方式有:

  • 如果目标类实现了某些接口,默认通过 动态代理 生成。
  • 如果目标类没有实现接口,默认通过 CGLIB 生成。
  • 也可以 直接设置 ProxyFactory 生成的方式,即使实现了接口,也能使用CGLIB。

在之前的演示代码中,我们没有启动 Spring 容器,也就是没有使用 Spring IOC 功能,而是独立使用了 Spring AOP。那么 Spring AOP 是如何与 Spring IOC 进行整合的?是采用了 Spring 整合最常用的方法 —— FactoryBean

ProxyFactoryBean 继承了 ProxyFactory 的父类 ProxyCreatorSupport ,具有了创建代理类的能力,同时实现了 FactoryBean 接口,当通过 getObject 方法获得 Bean 时,将得到代理类。

2.5 Target

在之前的演示代码中,我们直接为 ProxyFactory 指定一个对象为 Target。在 ProxyFactoryBean 中不仅能使用这种方式,还可以通过 TargetSource 的形式指定。

TargetSource 相当于为对象进行了一层封装, ProxyFactoryBean 将通过 TargetSourcegetTarget 方法来获得目标对象。于是,我们可以通过 getTarget 方法来 控制获得的目标对象TargetSource 的几种实现类有:

(1)SingletonTargetSource

很简单,内部只持有一个目标对象,直接返回。和我们直接指定对象的效果是一样的。

(2)PrototypeTargetSource

每次将返回一个新的目标对象实例。

(3)HotSwappableTartgetSource

运行时,根据特定条件,动态替换目标对象类的具体实现。例如当一个数据源挂了,可以切换至另外一个。

(4)CommonsPool2TargetSource

返回有限数目的目标对象实例,类似一个对象池。

(5)ThreadLocalTargetSource

为不同线程调用提供不同目标对象

2.6 Joinpoint

终于到了最后的 Joinpoint,我们通过下面的示例来理解 Joinpoint 的工作机制。

MockTask task = new MockTask();
ProxyFactory weaver = new ProxyFactory(task);
weaver.setInterfaces(new Class[]{ITask.class});
PrepareResourceBeforeAdvice beforeAdvice = new PrepareResourceBeforeAdvice();
ResourceCleanAfterReturningAdvice afterAdvice = new ResourceCleanAfterReturningAdvice();
weaver.addAdvice(beforeAdvice);
weaver.addAdvice(afterAdvice);
ITask proxyObject = (ITask) weaver.getProxy();
proxyObject.execute();

/** output   
准备资源    
开始执行任务     
任务完成    
资源清理   
**/
复制代码

我们知道 getProxy 会通过动态代理生成一个 ITask 的接口类,那么 execute 方法的内部是如何先执行了 beforeAdvicebefore 方法,接着执行 taskexecute 方法,再执行 afterAdviceafter 方法呢?

答案就在生成的 代理类 中。在动态代理中,代理类方法调用的逻辑由 InvocationHandler 实例的 invoke 方法决定,那答案进一步锁定在 invoke 方法

在本示例中, ProxyFactory.getProxy 会调用 JdkDynamicAopProxy.getProxy 获取代理类。

// JdkDynamicAopProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
复制代码

getProxy 中为 newProxyInstanceInvocationHandler 参数传入 this ,即 JdkDynamicAopProxy 就是一个 InvocationHandler 的实现,其 invoke 方法如下:

// JdkDynamicAopProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 通过advised(创建对象时初始化)获得指定的advice
    // 会将advice用相应的MethodInterceptor封装下
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

    if (chain.isEmpty()) {
        Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
        retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
    }
    else {
        // 创建一个MethodInvocation
        MethodInvocation invocation =
            new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        // 调用procced,开始进入拦截链(执行目标对象方法和MethodInterceptor的advice)
        retVal = invocation.proceed();
    }
    return retVal;
}
复制代码

首先获得指定的 advice,这里包含 beforeAdviceafterAdvice 实例,但会用 MethodInterceptor 封装一层,为了后面的拦截链。

再创建一个 RelectiveMethodInvocation 对象,最后通过 proceed 进入拦截链。

RelectiveMethodInvocation 就是 Spring AOP 中 Joinpoint 的一个实现,其类图如下:

关于Spring AOP,除了动态代理、CGLIB,你还知道什么?

首先看下 RelectiveMethodInvocation 的构造函数:

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;
}
复制代码

做了些相关属性的赋值,然后看向 proceed 方法,如何调用目标对象和拦截器。

public Object proceed() throws Throwable {
   // currentInterceptorIndex从-1开始
   // 当达到已调用了所有的拦截器后,通过invokeJoinpoint调用目标对象的方法
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }
   // 获得拦截器,调用其invoke方法
   // currentInterceptorIndex加1
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
复制代码

currentInterceptorIndex 从 -1 开始, interceptorsAndDynamicMethodMatchers 里有两个拦截器,再由于减 1,所有调用目标对象方法的条件是 currentInterceptorIndex 等于 1。

首先由于 -1 != 1 ,会获得包含了 beforeAdviceMethodBeforeAdviceInterceptor 实例, currentInterceptorIndex 加 1 变为 0。调用其 invoke 方法,由于是 Before-Advice,所以先执行 beforeAdvicebefore 方法,然后调用 proceed 进入拦截链的下一环。

// MethodBeforeAdviceInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    return mi.proceed();
}
复制代码

又回到了 proceed 方法, 0 != 1 ,再次获得 advice,这次获得的是包含 afterAdviceAfterReturningAdviceInterceptor 实例, currentInterceptorIndex 加 1 变为 1。调用其 invoke 方法,由于是 After-Returning-Adivce,所以会先执行 proceed 进入拦截链的下一环。

// AfterReturningAdviceInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
    Object retVal = mi.proceed();
    this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    return retVal;
}
复制代码

再次来到 proceed 方法, 1 == 1 ,已调用完所有的拦截器,将执行目标对象的方法。 然后 return 返回,回到 invoke 中,调用 afterAdviceafterReturning

所以在 Joinpoint 的实现中,通过 MethodInterceptor 完成了 目标对象方法和 Advice 的先后执行。

3. 小结

在了解了 Spring AOP 的实现后,笔者对 AOP 的概念更加清晰了。在学习过程中最令笔者感兴趣的是 Joinpoint 的拦截链,一开始不知道是怎么实现的,觉得很神奇 :astonished: 。最后学完了,总结下,好像也很简单,通过拦截器的 invoke 方法和 MethodInvocation.proceed 方法(进入下一个拦截器)的相互调用。好像就这么回事。:stuck_out_tongue:

关于Spring AOP,除了动态代理、CGLIB,你还知道什么?
原文  https://juejin.im/post/5eb5695a6fb9a043632a12d6
正文到此结束
Loading...