【源码】Spring AOP 2 Advice

前言

aopalliance AOP联盟,一个定义了 AOP 规范接口的开源项目。本文从 Advice 接口出发,理解并深入其对 AOP Advice 的抽象。
aopalliance

Advice

public interface Advice {

}

Advice 是一个标记接口,位于最顶层
Advice

Interceptor

Interceptor ,也是个标记接口,我们上一文提到,通常在 AOP 规范下会将所有的通知维护成 Join point 上的 Interceptors Chain (拦截链)。
Interceptor
可以看到,有两个大的分支(MethodInterceptorConstructorInterceptor),即 AOP 支持方法级别和构造器级别的拦截。实际上,Spring AOP 并未(但完全可以)实现构造器级别的拦截,因为 Spring AOP 旨在让 AOP 的思想尽可能轻量、单纯地的与 Spring IOC 契合,而并非想要完完全全的实现一整套的 AOP。因此本文也只分析 MethodInterceptor

MethodInterceptor

@FunctionalInterface
public interface MethodInterceptor extends Interceptor {

	Object invoke(MethodInvocation invocation) throws Throwable;

}
  • 函数式接口声明
  • MethodInterceptor 可以看作整个方法的调用链,因此 invoke 是整个调用链的执行,这意味着其中那个包括源方法和 Advice 的执行,下文结合具体实现类理解
  • MethodInvocation 可理解为你要通知的对象。结合上一章节,不难理解其实它就是 Join point 。以后会介绍到,很重要的一个分支。

BeforeAdvice

public interface BeforeAdvice extends Advice {

}

又一个标记接口,这个猜也能猜到,Before advice 的抽象
BeforeAdvice

MethodBeforeAdvice

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

}

方法前置通知抽象,before 方法就是要执行的前置通知逻辑。上文说过,before 不能打断方法执行,除非抛出异常

AfterAdvice

public interface AfterAdvice extends Advice {

}

后置通知的抽象,标记接口
AfterAdvice

AfterReturningAdvice

public interface AfterReturningAdvice extends AfterAdvice {

	void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;

}

After returning advice 的抽象,可以看到,他是允许对返回值进行操作的。

具体实现类

基本上,整个接口的抽象及关系,我们做了简单地梳理。然后结合具体的几个实现类再深入体会

AfterReturningAdviceInterceptor

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

	private final AfterReturningAdvice advice;

	public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		Object retVal = mi.proceed();
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}
}
  • 它是一个 MethodInterceptor 又是一个 AfterAdvice,实际上是一个组合的关系
  • 整个 Interceptor 调用时,在 MethodInvocation#proceed 之后执行通知逻辑 afterReturning
  • AfterReturning 通知支持对方法返回值进行干涉

MethodBeforeAdviceInterceptor

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());
		return mi.proceed();
	}

}
  • AfterReturningAdviceInterceptor 类似,MethodInterceptorBeforeAdvice
  • Before 通知不能干涉返回值

ThrowsAdviceInterceptor

public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {

	private static final String AFTER_THROWING = "afterThrowing";

	// 通知类
	private final Object throwsAdvice;

	/**
	 * K:异常类型
	 * V:处理该类型异常的方法
	 */
	private final Map<Class<?>, Method> exceptionHandlerMap = new HashMap<>();

	public ThrowsAdviceInterceptor(Object throwsAdvice) {
		Assert.notNull(throwsAdvice, "Advice must not be null");
		this.throwsAdvice = throwsAdvice;

		Method[] methods = throwsAdvice.getClass().getMethods();

		/**
		 * 对应的 Advice 提供的处理异常方法名必须为 afterThrowing,对应参数为 1 个或 4 个,q签名格式:
		 * 		void afterThrowing([Method, args, target], ThrowableSubclass)
		 */
		for (Method method : methods) {
			if (method.getName().equals(AFTER_THROWING) &&
					(method.getParameterCount() == 1 || method.getParameterCount() == 4)) {

				// 去最后一个参数就是异常类型
				Class<?> throwableParam = method.getParameterTypes()[method.getParameterCount() - 1];
				if (Throwable.class.isAssignableFrom(throwableParam)) {
					this.exceptionHandlerMap.put(throwableParam, method);

				}
			}
		}
	}
	
	// ...

	@Override
	@Nullable
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		catch (Throwable ex) {
			/**
			 * 根据抛出的异常类型调用对应的 Advice 方法
			 */
			Method handlerMethod = getExceptionHandler(ex);
			if (handlerMethod != null) {
				invokeHandlerMethod(mi, ex, handlerMethod);
			}
			throw ex;
		}
	}
	
	@Nullable
	private Method getExceptionHandler(Throwable exception) {
		Class<?> exceptionClass = exception.getClass();

		// 找不到当前类型的处理器就依次找父类的,直到 Throwable
		Method handler = this.exceptionHandlerMap.get(exceptionClass);
		while (handler == null && exceptionClass != Throwable.class) {
			exceptionClass = exceptionClass.getSuperclass();
			handler = this.exceptionHandlerMap.get(exceptionClass);
		}

		return handler;
	}

	// ...
	
}
  • 忍不住把所有代码贴了上来,ThrowsAdviceInterceptor 的实现稍微复杂一点,它维护一个通知类 Object throwsAdvice 和一个具体异常与通知方法对应关系的 Map<Class<?>, Method> exceptionHandlerMapObject throwsAdvice 意味着可以是任何类
  • 构造方法中基于所有 Object throwsAdvice 的签名符合 void afterThrowing([Method, args, target], ThrowableSubclass) 的方法,构造 exceptionHandlerMap 把异常和对应的通知方法关联起来
  • 因此 invoke 的逻辑实现就是在目标方法抛出异常后找到对应的异常处理方法来处理,找不到的话会依次追溯它的父类
  • 最后目标异常还是会抛出去

demo

	public static class ThrowsAdviceSample {

        public void afterThrowing(IndexOutOfBoundsException ex) {
            System.out.println("catch IndexOutOfBoundsException");
        }

        public void afterThrowing(ExpressionException ex) {
            System.out.println("catch ExpressionException");
        }

    }

    @Test
    public void test1() throws Throwable {
        ThrowsAdviceInterceptor throwsAdviceInterceptor = new ThrowsAdviceInterceptor(new ThrowsAdviceSample());
        throwsAdviceInterceptor.invoke(
                new MethodInvocation() {
                    
                    // ...

                    @Override
                    public Object proceed() throws Throwable {
                        throw new ArrayIndexOutOfBoundsException();
                    }

                    // ...
                    
                }
        );
    }
  • ThrowsAdviceInterceptor 示例,异常通知可以定义任意类型,只要方法符合签名规则即可,比如示例中的 ThrowsAdviceSample
  • 方法执行抛出 ArrayIndexOutOfBoundsException 后会被其父类 IndexOutOfBoundsException 的通知拦截,输出 catch IndexOutOfBoundsException

类图

Advice

总结

忽略 AspectJ 下的实现,可以发现,这组接口的实现类多是 MethodInterceptorAdvice 的组合,前者负责整个调用链的执行,后者负责在恰当的时机执行通知

同时,MethodInterceptor 持有的 MethodInvocation,就是下一章节要了解的 Joinpoint

上一篇:【源码】Spring AOP 1 基础概念
下一篇:【源码】Spring AOP 3 Joinpoint

参考

【小家Spring】Spring AOP之Advisor、PointcutAdvisor、IntroductionAdvisor、IntroductionInterceptor(引介增强)

【小家Spring】探索Spring AOP中aopalliance的Joinpoint、MethodInvocation、Interceptor、MethodInterceptor…

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值