跟着小马哥学系列之 Spring AOP( Advice 组件详解)

Source

学好路更宽,钱多少加班。 ——小马哥

简介

大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间《小马哥讲Spring AOP 编程思想》基础上形成的个人一些总结。希望能帮助各位小伙伴, 祝小伙伴早日学有所成。

Advice 类图

在这里插入图片描述

由类图可知:

  • advice 分类以及代表接口:
    • around(环绕):无代表接口(底层是通过 MethodInterceptor 实现)
    • before(前置):BeforeAdvice
    • after(后置)
      • 最终:AfterAdvice
      • 返回:AfterReturningAdvice
      • 异常:ThrowsAdvice
  • 实现:
    • Spring 实现:
      • around(环绕):MethodInterceptor
      • before(前置):MethodBeforeAdvice + MethodBeforeAdviceInterceptor
      • after(后置)
        • AfterAdvice:MethodInterceptor
        • AfterReturningAdvice:AfterReturningAdviceInterceptor
        • ThrowsAdvice:ThrowsAdviceInterceptor
    • 整合 AspectJ 实现:
      • around(环绕):@Around 注解 (AspectJAroundAdvice)
      • before(前置):@Before 注解(AspectJMethodBeforeAdvice 会被适配成 MethodBeforeInterceptor )
      • after(后置):
        • 最终:@After(AspectJAfterAdvice)
        • 返回:@AfterReturning(AspectJAfterReturningAdvice 会通过 AdvisorAdapterRegistry 被适配成 AfterReturningAdviceInterceptor )
        • 异常:@AfterThrowing(AspectJAfterThrowingAdvice)

相关接口/类/注解介绍

MethodInterceptor

在这里插入图片描述

从类图以及 javadoc 中可知:

  1. MethodInterceptor 继承了 Interceptor ,而 Interceptor 又继承了 Advice。
  2. 这三个接口均来在 AOP 联盟中的包(org.aopalliance)。
  3. Advice: 接口是个标记接口,能表明任何类型的 Advice,比如拦截器
  4. Interceptor: 接口继承了 Advice 接口,但它也是一个标记接口。这个接口表示一个通用拦截器。通用拦截器可以拦截发生在基于程序中的运行时事件。这些事件是通过连接点具体化的。运行时连接点可以是调用、字段访问、异常……
  5. MethodInterceptor: 拦截对接口的调用在接口到达目标的过程中,它们嵌套在目标的“顶部”。用户应该实现 invoke(MethodInvocation) 方法来修改原始行为。最重要的 invoke(MethodInvocation invocation) 方法中的 MethodInvocation 参数!实现此方法以在调用之前和之后执行额外的处理。优雅的实现当然希望调用Joinpoint.proceed()(MethodInvocation 也继承了这个接口,详情请参阅我的另外一篇 跟着小马哥学系列之 Spring AOP( ReflectiveMethodInvocation/CglibMethodInvocation 详解 )。

使用 MethodInterceptor 来达到环绕(around)类型的 advice


public class Something {
    
      
    public String doSomething(String param) {
    
      
        try {
    
      
            System.out.println("参数是:" + param);
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
    
      
            e.printStackTrace();
        }
        return param;
    }
}


public class AroundEffectAdviceDemo implements MethodInterceptor {
    
      
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
    
      
        StopWatch sw = new StopWatch();
        sw.start(invocation.getMethod().getName());
        Object[] arguments = invocation.getArguments();
        arguments[0] = "大叔文海";
        // 控制目标方法要不要执行
        Object returnValue = invocation.proceed();
        sw.stop();
        cost(invocation, sw.getTotalTimeMillis());
        // 也可以对返回值进行修改
        return returnValue + "返回值已被修改";
    }

    private void cost(MethodInvocation invocation, long ms) {
    
      
        Method method = invocation.getMethod();
        Object target = invocation.getThis();
        Object[] arguments = invocation.getArguments();
        System.out.printf("执行方法:%s, 目标对象:%s, 参数:%s, 耗时:%s ms\n", method,
                target.getClass().getName(),
                Arrays.toString(arguments),
                ms);
    }

    public static void main(String[] args) {
    
      
        Something something = new Something();
        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(something);
        pf.addAdvice(new AroundEffectAdviceDemo());
        Something proxy = (Something) pf.getProxy();
        System.out.println(proxy.doSomething("文海"));
    }

}

AspectJPrecedenceInformation

该接口可以提供根据 AspectJ 的优先规则(PartiallyComparableAdvisorHolder 搭配 AspectJPrecedenceComparator 和 AnnotationAwareOrderComparator)对 Advice/Advisor 进行排序所需的信息。 这些信息包括 aspect 名称、 在 aspect 中定义的顺序、是前置 Advice 还是 后置 Advice

Advice

此接口是个标记接口,能表明任何类型的 Advice,比如拦截器。为什么命名为 Advice ?从 javadoc 里面可知:源码开发者认为运行时 joinpoint 是发生在静态 joinpoint(即程序中的一个位置)上的事件。例如,调用方法(静态 joinpoint)就是运行时 joinpoint。由 Pointcut 筛选出满足条件的方法(由于 Spring 只支持方法级别)构成 joinpoint 然后进行 Advice

BeforeAdvice

继承 Advice 接口,表明是前置 Advice 类型的标记接口。Spring 只支持方法级别的前置 Advice。

MethodBeforeAdvice

继承了 BeforeAdvice 接口,主要提供了一个 before 方法。在调用方法之前调用的 Advice。这样的 Advice 不能阻止方法调用继续进行,除非它们抛出一个 Throwable。

示例

public interface EchoService {
    
      
    String echo(String message);
}

public class DefaultEchoServiceImpl implements EchoService {
    
      
    @Override
    public String echo(String message) {
    
      
        return message;
    }
}

public class SimpleBeforeAdvice implements MethodBeforeAdvice {
    
      

    public static void main(String[] args) {
    
      
        EchoService echoService = new DefaultEchoServiceImpl();
        ProxyFactory pf = new ProxyFactory();
        pf.addAdvice(new SimpleBeforeAdvice());
        pf.setTarget(echoService);
        EchoService proxy = (EchoService) pf.getProxy();
        // 控制台返回的是 文海大叔
        System.out.println(proxy.echo("文海"));
    }


    @Override
    public void before(@NonNull Method method, @NonNull Object[] args, Object target) throws Throwable {
    
      
    	// 方法调用参数
        System.out.println(Arrays.toString(args));
        // 如果放开注释则会报错阻止 echo 方法调用
//        throw new Exception("MethodBeforeAdvice Exception");
		// 修改方法参数
        args[0] = "文海大叔";
    }
}

源码解读

由于 Advice 是基于拦截器进行实现的,Spring 只支持方法级别的拦截。所以最终都会转换为方法拦截。
由于 Advice 是基于拦截器进行实现的,Spring 只支持方法级别的拦截。所以最终都会转换为方法拦截。
由于 Advice 是基于拦截器进行实现的,Spring 只支持方法级别的拦截。所以最终都会转换为方法拦截。

重要的事说三遍。

在这里插入图片描述

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 {
    
      
		// 调用前置 advice 再执行调用链下一个方法
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}

}


细节可以参阅我的另外写的 跟着小马哥学系列之 Spring AOP(AdvisorChainFactory 详解)跟着小马哥学系列之 Spring AOP(AspectJAdvisorFactory 详解)

AfterAdvice

继承了 Advice ,表明是后置 advice 类型公共标记接口,例如 AfterReturningAdvice 和 ThrowsAdvice。

使用 MethodInterceptor 来达到最终(after)类型的 advice

public interface EchoService {
    
      
    String echo(String message);
}


public class DefaultEchoServiceImpl implements EchoService {
    
      
    @Override
    public String echo(String message) {
    
      
    	// 有一半几率报错
        if (new Random().nextBoolean()) {
    
      
            throw new RuntimeException("运行出错啦:" + message);
        }
        return message;
    }
}

public class AfterAdviceDemo implements MethodInterceptor {
    
      

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
    
      
        try {
    
      
            return invocation.proceed();
        } finally {
    
      
        	// 放入 finally 代码块,不管怎么样都会被执行,也可以通过 invocation 取出元信息进行处理
            invokeAdviceMethod();
        }
    }


    private void invokeAdviceMethod() {
    
      
        System.out.println("不管有没有异常我都会被调用到");
    }


    public static void main(String[] args) {
    
      
        EchoService echoService = new DefaultEchoServiceImpl();
        ProxyFactory pf = new ProxyFactory();
        pf.addAdvice(new AfterAdviceDemo());
        pf.setTarget(echoService);
        EchoService proxy = (EchoService) pf.getProxy();
        System.out.println(proxy.echo("文海"));
    }
}

ThrowsAdvice

继承了 AfterAdvice 接口,是 throws 类型的 advice 标记接口。这个接口上没有任何方法,因为方法是由反射调用的。实现类必须满足格式:public void afterThrowing([Method, args, target], ThrowableSubclass)

该接口为什么没有任何方法?

因为 Java 异常类型太多,不可能每个异常类型都来一个重载的方法,怎么解决自定义异常。所以 Spring 规定了格式:public void afterThrowing([Method, args, target], ThrowableSubclass) 通过反射来调用。

示例

public class CustomThrowsAdviceDemo implements ThrowsAdvice {
    
      
	// 处理 Exception 类型的异常
    public void afterThrowing(Exception ex) {
    
      
        System.out.println("***");
        System.out.println("Caught:" + ex.getClass().getName());
        System.out.println("***");
    }
	// 处理 IllegalArgumentException 类型的异常
    public void afterThrowing(IllegalArgumentException ex) {
    
      
        System.out.println("***");
        System.out.println("Caught:" + ex.getClass().getName());
        System.out.println("***");
    }
	// 处理 IllegalArgumentException 类型的异常并把一些元信息传过来
    public void afterThrowing(Method method, Object[] args, Object target, IllegalArgumentException ex) {
    
      
        System.out.println("***");
        System.out.println("Caught:" + ex.getClass().getName());
        System.out.println("Method: " + method.getName());
        System.out.println("***");
    }


    public static void main(String[] args) {
    
      
        ErrorClass errorClass = new ErrorClass();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(errorClass);
        proxyFactory.addAdvice(new CustomThrowsAdviceDemo());
        ErrorClass proxy = (ErrorClass) proxyFactory.getProxy();
        try {
    
      
            proxy.exceptionMethod();
        } catch (Exception e) {
    
      

        }
        try {
    
      
            proxy.illegalArgumentExceptionMethod();
        } catch (IllegalArgumentException e) {
    
      

        }

    }


    public static class ErrorClass{
    
      
        public void exceptionMethod() throws Exception {
    
      
            throw new Exception("Generic Exception");
        }

        public void illegalArgumentExceptionMethod() throws IllegalArgumentException {
    
      
            throw new IllegalArgumentException("IllegalArgument Exception");
        }
    }
}

源码解读

// 怎样获取 ThrowsAdviceInterceptor 流程图在 MethodBeforeAdvice 中已说明  
public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {
    
      
	// 这里限制了方法名必须是 afterThrowing 
	private static final String AFTER_THROWING = "afterThrowing";
	// advice 对象
	private final Object throwsAdvice;
	// 异常类型与方法映射(这也导致了同一种异常类型注册多个只有一个方法起效果)
	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();
		for (Method method : methods) {
    
      
			// 方法名必须是 afterThrowing 并且方法参数个数只能有 1 个或者 4个
			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);
					if (logger.isDebugEnabled()) {
    
      
						logger.debug("Found exception handler method on throws advice: " + method);
					}
				}
			}
		}

		if (this.exceptionHandlerMap.isEmpty()) {
    
      
			throw new IllegalArgumentException(
					"At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");
		}
	}

	public int getHandlerMethodCount() {
    
      
		return this.exceptionHandlerMap.size();
	}

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
    
      
		try {
    
      
			// 先调用拦截链中的方法。
			return mi.proceed();
		}
		catch (Throwable ex) {
    
      
			// 如果有异常则去异常映射方法(会递归往上找异常类型) Map 中取出对应方法进行处理,如果没找对应方法则把异常往上抛
			Method handlerMethod = getExceptionHandler(ex);
			if (handlerMethod != null) {
    
      
				invokeHandlerMethod(mi, ex, handlerMethod);
			}
			throw ex;
		}
	}
	
	@Nullable
	private Method getExceptionHandler(Throwable exception) {
    
      
		Class<?> exceptionClass = exception.getClass();
		if (logger.isTraceEnabled()) {
    
      
			logger.trace("Trying to find handler for exception of type [" + exceptionClass.getName() + "]");
		}
		Method handler = this.exceptionHandlerMap.get(exceptionClass);
		while (handler == null && exceptionClass != Throwable.class) {
    
      
			exceptionClass = exceptionClass.getSuperclass();
			handler = this.exceptionHandlerMap.get(exceptionClass);
		}
		if (handler != null && logger.isTraceEnabled()) {
    
      
			logger.trace("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler);
		}
		return handler;
	}

	private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable {
    
      
		Object[] handlerArgs;
		if (method.getParameterCount() == 1) {
    
      
			handlerArgs = new Object[] {
    
      ex};
		}
		else {
    
      
			handlerArgs = new Object[] {
    
      mi.getMethod(), mi.getArguments(), mi.getThis(), ex};
		}
		try {
    
      
			method.invoke(this.throwsAdvice, handlerArgs);
		}
		catch (InvocationTargetException targetEx) {
    
      
			throw targetEx.getTargetException();
		}
	}

}

AfterReturningAdvice

扩展了 AfterAdvice,提供了 afterReturning 方法。只有在不抛出异常的普通方法正常返回之后被调用的 Advice(类型为返回 Advice ) 。这样的 Advice 可以看到返回值,但不能更改它。

示例

public interface EchoService {
    
      
    String echo(String message);
}

public class DefaultEchoServiceImpl implements EchoService {
    
      
    @Override
    public String echo(String message) {
    
      
    	 if (new Random().nextBoolean()) {
    
      
            throw new RuntimeException("运行出错啦:" + message);
        }
        return message;
    }
}
public class CustomAfterReturningAdvice implements AfterReturningAdvice {
    
      
    @Override
    public void afterReturning(Object returnValue, @NonNull Method method, @NonNull Object[] args, Object target) throws Throwable {
    
      
        System.out.printf("返回值:%s, 方法: %s, 参数:%s, 目标对象:%s\n", returnValue, method.getName(), Arrays.toString(args), target.getClass().getSimpleName());
    }

    public static void main(String[] args) {
    
      
        EchoService echoService = new DefaultEchoServiceImpl();
        ProxyFactory pf = new ProxyFactory();
        pf.addAdvice(new CustomAfterReturningAdvice());
        pf.setTarget(echoService);
        EchoService proxy = (EchoService) pf.getProxy();
        // 如果 echo方法有异常,则返回 Advice 就不会执行
        proxy.echo("文海");
    }
}

源码分析

// 怎样获取 ThrowsAdviceInterceptor 流程图在 MethodBeforeAdvice 中已说明  
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();
		// 如果没有异常,将会调用返回 advice 
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}

}

Spring 整合 AspectJ 注解

@Aspect

定义一个 aspect(切面)。注解有 value 属性表明创建 aspect 实例方式,默认是空字符串,代表单例。注意在光标注 @Aspect 注解不会被 Spring IoC 容器自动识别,可以加上 @Component 注解或者通过 @Bean 等方式把 aspect 声明一个 Bean

@Around

环绕 advice 类型。value 属性是绑定 pointcut 表达式, argNames 属性是表达式中带有参数,指定参数名称

示例


public interface EchoService {
    
      
    String echo(String message);
    default Integer echo(Integer integer){
    
      
        return  integer;
    }
    @ProductPushAspect.ProductPush(ProductPushAspect.ProductStatusEnum.AUDIT)
    default Integer productPushAnnotation(Integer integer) {
    
      
        return integer;
    }
}

public class DefaultEchoServiceImpl implements EchoService {
    
      
    @Override
    public String echo(String message) {
    
      
        if (new Random().nextBoolean()) {
    
      
            throw new RuntimeException("发生异常啦!!!参数 message 为:" + message);
        }
        return message;
    }
}
 @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ProductPush {
    
      
        ProductStatusEnum value();
    }

    public enum ProductStatusEnum {
    
      
        // 提报审核,编辑,归档
        AUDIT, EDIT, ARCHIVE
    }

// 切面
@Aspect
public class AspectJAnnotationAspect {
    
      
	// 框架会默认传递一个 ProceedingJoinPoint  类型的参数
    @Around(value = "@annotation(productPush)", argNames = "joinPoint,productPush")
    public Object around(ProceedingJoinPoint joinPoint, ProductPushAspect.ProductPush productPush) throws Throwable {
    
      
        System.out.println("AspectJ around advice start ");
        System.out.println("productPush 元信息: " + productPush.value().name());
        Object retVal = joinPoint.proceed();
        System.out.println("AspectJ around advice end ");
        return retVal;
    }
}


public class AspectJAnnotationDemo {
    
      
    public static void main(String[] args) {
    
      
        EchoService echoService = new DefaultEchoServiceImpl();
        AspectJProxyFactory factory = new AspectJProxyFactory();
        factory.setTarget(echoService);
        factory.addAspect(AspectJAnnotationAspect.class);
        EchoService proxy = factory.getProxy();
        proxy.productPushAnnotation(2);
     }
}

@Before

前置 Advice。value 属性是绑定 pointcut 表达式, argNames 属性是表达式中带有参数,指定参数名称

示例

// 通过 @ProductPush 注解筛选并把注解元信息当成参数带过来
@Before(value = "@annotation(productPush)", argNames = "productPush")
public void pushProductBefore(ProductPush productPush) {
    
      
    
}
// 没有 @ProductPush 元信息,只通过 @ProductPush 注解筛选
@Before(value = "@annotation(ProductPush)")
public void pushProductBefore() {
    
      

}

@After

最终 adivce 类型。value 属性是绑定 pointcut 表达式, argNames 属性是表达式中带有参数,指定参数名称。

@AfterReturning

返回 advice 类型。除了 value/pointcut 和 argNames 属性是之外有个 returning 属性,表示 Advice 签名中要绑定返回值的参数的名称

示例

public interface EchoService {
    
      
    String echo(String message);
    default Integer echo(Integer integer){
    
      
        return  integer;
    }
}

public class DefaultEchoServiceImpl implements EchoService {
    
      
    @Override
    public String echo(String message) {
    
      
        if (new Random().nextBoolean()) {
    
      
            throw new RuntimeException("运行出错啦:" + message);
        }
        return message;
    }
}

@Aspect
public class AspectJAnnotationAspect {
    
      
    @AfterReturning(value = "execution(public * com.wenhai.spring.aop.features.service.*.*(..)))", returning = "retVal")
    public void afterReturning(String retVal) {
    
      
        System.out.println("返回值是:" + retVal);
    }
}

public class AspectJAnnotationDemo {
    
      
    public static void main(String[] args) {
    
      
        EchoService echoService = new DefaultEchoServiceImpl();
        AspectJProxyFactory factory = new AspectJProxyFactory();
        factory.setTarget(echoService);
        factory.addAspect(AspectJAnnotationAspect.class);
        EchoService proxy = factory.getProxy();
        System.out.println(proxy.echo("文海"));
        System.out.println(proxy.echo(1));
    }
}

@AfterThrowing

异常 advice 类型,除了 value/pointcut 和 argNames 属性是之外有个 throwing 属性,表示 Advice 签名中要绑定抛出异常的参数的名称

示例


public interface EchoService {
    
      
    String echo(String message);
    default Integer echo(Integer integer){
    
      
        return  integer;
    }
}
public class DefaultEchoServiceImpl implements EchoService {
    
      
    @Override
    public String echo(String message) {
    
      
        if (new Random().nextBoolean()) {
    
      
            throw new RuntimeException("发生异常啦!!!参数 message 为:" + message);
        }
        return message;
    }
}

@Aspect
public class AspectJAnnotationAspect {
    
      
    @AfterReturning(value = "execution(public * com.wenhai.spring.aop.features.service.*.*(..)))", returning = "retVal")
    public void afterReturning(String retVal) {
    
      
        System.out.println("afterReturning 返回值是:" + retVal);
    }


    @AfterThrowing(value = "execution(public * com.wenhai.spring.aop.features.service.*.*(..)))", throwing = "ex")
    public void afterThrowing(RuntimeException ex) {
    
      
        System.out.println("afterThrowing 抛出的异常为:" + ex);
    }
}



public class AspectJAnnotationDemo {
    
      
    public static void main(String[] args) {
    
      
        EchoService echoService = new DefaultEchoServiceImpl();
        AspectJProxyFactory factory = new AspectJProxyFactory();
        factory.setTarget(echoService);
        factory.addAspect(AspectJAnnotationAspect.class);
        EchoService proxy = factory.getProxy();
        System.out.println(proxy.echo("文海"));
        System.out.println(proxy.echo(1));
    }
}

注意

任何 AspectJ Advice 注解声明的 advice 方法,都会把类型为 org.aspectj.lang.JoinPointorg.aspectj.lang.JoinPoint.StaticPart(@Around 是ProceedingJoinPoint ) 作为第一次参数传递。这个很有用可以获得以下元信息:

  • getArgs(): 返回方法参数

  • getThis(): 返回 proxy 对象

  • getTarget(): 返回目标对象

  • getSignature(): 返回方法签名

  • toString(): 打印所 advice 方法的有用描述

AbstractAspectJAdvice

根据 AOP 联盟 Advice 类包装标注 AspectJ 注解@Aspect 的类或方法上标注 AspectJ 注解 @Around、@Before、@After 、@AfterReturning、@AfterThrowing 的基础类。

类图

在这里插入图片描述

构造器说明

public AbstractAspectJAdvice(
		Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory) {
    
      

	Assert.notNull(aspectJAdviceMethod, "Advice method must not be null");
	// 标注 AspectJ 注解的方法的类即 aspect
	this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
	// 获取方法名称
	this.methodName = aspectJAdviceMethod.getName();
	// 获取 advice 方法参数类型
	this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
	// 获取 advice 方法
	this.aspectJAdviceMethod = aspectJAdviceMethod;
	// pointcut
	this.pointcut = pointcut;
	// aspect 实例工厂
	this.aspectInstanceFactory = aspectInstanceFactory;
}

字段说明

// 当前 joinpoint 的 ReflectiveMethodInvocation userAttributes 映射中使用的键。
protected static final String JOIN_POINT_KEY = JoinPoint.class.getName();
// advice 方法所在的类即 aspect 类
private final Class<?> declaringClass;
// advice 方法名称
private final String methodName;
// advice 方法参数
private final Class<?>[] parameterTypes;
// advice 方法
protected transient Method aspectJAdviceMethod;
// AspectJ 表达式的 pointcut
private final AspectJExpressionPointcut pointcut;
// aspect 实例工厂
private final AspectInstanceFactory aspectInstanceFactory;
// aspect 名称(在确定 advice 优先级时使用,以便我们可以确定两条 advice 是否来自同一个 aspect)
private String aspectName = "";
// 同一个 aspect 声明的顺序
private int declarationOrder;
// 如果此 advice 对象的创建者知道参数名并显式地设置它们,则该参数将是非空的(对应注解中 argNames 属性 )。
private String[] argumentNames;
// 对应 @AfterThrowing 注解 throwing 属性
private String throwingName;
// 对应 @AfterReturning 注解 returning 属性
private String returningName;
// 对应 @AfterReturning 注解 returning 属性的具体类型
private Class<?> discoveredReturningType = Object.class;
// 对应 @AfterThrowing 注解 throwing 属性的具体类型
private Class<?> discoveredThrowingType = Object.class;
// JoinPoint/ProceedingJoinPoint 参数的索引(当前仅支持索引0如果存在)。
private int joinPointArgumentIndex = -1;
// JoinPoint.StaticPart 参数的索引(如果存在的话,目前只支持索引0)。
private int joinPointStaticPartArgumentIndex = -1;
// 参数名称对应的参数顺序
private Map<String, Integer> argumentBindings;
// 参数是否已绑定
private boolean argumentsIntrospected = false;
// 返回泛型类型
private Type discoveredReturningGenericType;

方法解读

calculateArgumentBindings

用于 @AspectJ Advice 注解元信息到 Advice 方法参数绑定

public final synchronized void calculateArgumentBindings() {
    
      
		// 参数绑定过了或者不需要绑定参数
		if (this.argumentsIntrospected || this.parameterTypes.length == 0) {
    
      
			return;
		}
		// 参数绑定的个数
		int numUnboundArgs = this.parameterTypes.length;
		// 参数绑定的类型
		Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
		// advice 方法第一个参数类型是 JoinPoint 或者 ProceedingJoinPoint(必须是 Around 类型的 Advice) 或者是 JoinPoint.StaticPart 则把对应的参数索引(joinPointArgumentIndex  或 joinPointStaticPartArgumentIndex)设置为 0
		if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0]) ||
				maybeBindJoinPointStaticPart(parameterTypes[0])) {
    
      
			// 个数减一
			numUnboundArgs--;
		}
		// 如果大于 0 则需要绑定额外的参数
		if (numUnboundArgs > 0) {
    
      
			// 根据参数名称进行绑定参数
			bindArgumentsByName(numUnboundArgs);
		}
		// 设置参数绑定标记为 true,下次再绑定直接跳过
		this.argumentsIntrospected = true;
	}


private void bindArgumentsByName(int numArgumentsExpectingToBind) {
    
      
	// 如果在 advice 注解上没有明确指定 argNames 属性值则由 ParameterNameDiscoverert 自动找出 
	if (this.argumentNames == null) {
    
      
		this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod);
	}
	if (this.argumentNames != null) {
    
      
		// 参数名称已确定之后则根据是不同类型(returning、throwing 和 pointcut)进行绑定
		bindExplicitArguments(numArgumentsExpectingToBind);
	}
	else {
    
      
		throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " +
				"requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " +
				"the argument names were not specified and could not be discovered.");
	}
}

private void bindExplicitArguments(int numArgumentsLeftToBind) {
    
      
	Assert.state(this.argumentNames != null, "No argument names available");
	this.argumentBindings = new HashMap<>();
	// 需要绑定的个数跟 advice 方法上的参数数量不一样则报错
	int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterCount();
	if (this.argumentNames.length != numExpectedArgumentNames) {
    
      
		throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames +
				" arguments to bind by name in advice, but actually found " +
				this.argumentNames.length + " arguments.");
	}

	// 这里主要是排除方法第一个参数是 JoinPoint 或者 ProceedingJoinPoint(必须是 Around 类型的 Advice) 或者是 JoinPoint.StaticPart
	int argumentIndexOffset = this.parameterTypes.length - numArgumentsLeftToBind;
	// 方法名称与方法位置进行缓存
	for (int i = argumentIndexOffset; i < this.argumentNames.length; i++) {
    
      
		this.argumentBindings.put(this.argumentNames[i], i);
	}

	// 如果指定了 returning 或者 throwing 参数名称,则找到对应的参数类型
	if (this.returningName != null) {
    
      
		if (!this.argumentBindings.containsKey(this.returningName)) {
    
      
			throw new IllegalStateException("Returning argument name '" + this.returningName +
					"' was not bound in advice arguments");
		}
		else {
    
      
			Integer index = this.argumentBindings.get(this.returningName);
			this.discoveredReturningType = this.aspectJAdviceMethod.getParameterTypes()[index];
			this.discoveredReturningGenericType = this.aspectJAdviceMethod.getGenericParameterTypes()[index];
		}
	}
	if (this.throwingName != null) {
    
      
		if (!this.argumentBindings.containsKey(this.throwingName)) {
    
      
			throw new IllegalStateException("Throwing argument name '" + this.throwingName +
					"' was not bound in advice arguments");
		}
		else {
    
      
			Integer index = this.argumentBindings.get(this.throwingName);
			this.discoveredThrowingType = this.aspectJAdviceMethod.getParameterTypes()[index];
		}
	}

	// 如果参数不来自 returning 或者 throwing 则来自 pointcut 表达式中,要相应地配置 pointcut 表达式。
	configurePointcutParameters(this.argumentNames, argumentIndexOffset);
}

private void configurePointcutParameters(String[] argumentNames, int argumentIndexOffset) {
    
      
int numParametersToRemove = argumentIndexOffset;
	if (this.returningName != null) {
    
      
		// 排除 retuning 
		numParametersToRemove++;
	}
	if (this.throwingName != null) {
    
      
		// 排除 throwing
		numParametersToRemove++;
	}
	String[] pointcutParameterNames = new String[argumentNames.length - numParametersToRemove];
	Class<?>[] pointcutParameterTypes = new Class<?>[pointcutParameterNames.length];
	Class<?>[] methodParameterTypes = this.aspectJAdviceMethod.getParameterTypes();
	
	int index = 0;
	for (int i = 0; i < argumentNames.length; i++) {
    
      
		 
		if (i < argumentIndexOffset) {
    
      
			continue;
		}
		// 排除 retuning 和 throwing
		if (argumentNames[i].equals(this.returningName) ||
			argumentNames[i].equals(this.throwingName)) {
    
      
			continue;
		}
		// 根据方法参数名称的顺序设置 pointcut 表达式参数名称和对应的参数类型
		pointcutParameterNames[index] = argumentNames[i];
		pointcutParameterTypes[index] = methodParameterTypes[i];
		index++;
	}
	// 设置 pointcut 表达式参数名
	this.pointcut.setParameterNames(pointcutParameterNames);
	// 设置 pointcut 表达式参数类型
	this.pointcut.setParameterTypes(pointcutParameterTypes);
}
argBinding

在方法执行 joinpoint 获取参数,并将一组参数输出到 advice 方法(实参绑定)。


/**
 * 在方法执行 joinpoint 获取参数,并将一组参数输出到 advice 方法(实参绑定)。
 * 
 * @param jp 是当前 JoinPoint
 * @param  jpMathch 是匹配此执行的 joinpoint 的 JoinPointMatch
 * @param returnValue 是方法执行返回值 (可能为 null)
 * @param ex 是方法执行抛出的异常(可能为 null)
 * @return 如果没有参数,则为空数组
 */
protected Object[] argBinding(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
		@Nullable Object returnValue, @Nullable Throwable ex) {
    
      
	// 上面已分析(参数名称绑定)
	calculateArgumentBindings();
	
	Object[] adviceInvocationArgs = new Object[this.parameterTypes.length];
	int numBound = 0;
	// 第一个参数需要绑定 JoinPoint 或者 ProceedingJoinPoint(必须是 Around 类型的 Advice) 或者是 JoinPoint.StaticPart
	if (this.joinPointArgumentIndex != -1) {
    
      
		adviceInvocationArgs[this.joinPointArgumentIndex] = jp;
		numBound++;
	}
	else if (this.joinPointStaticPartArgumentIndex != -1) {
    
      
		adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();
		numBound++;
	}
	// 除了 JoinPoint/ProceedingJoinPoint/JoinPoint.StaticPart 之外的其他参数需要绑定
	if (!CollectionUtils.isEmpty(this.argumentBindings)) {
    
      
		// 从 pointcut match 进行绑定
		if (jpMatch != null) {
    
      
			// 从 jpMatch 中获取 PointcutParameter 列表,然后通过参数名称获取实参
			PointcutParameter[] parameterBindings = jpMatch.getParameterBindings();
			for (PointcutParameter parameter : parameterBindings) {
    
      
				String name = parameter.getName();
				Integer index = this.argumentBindings.get(name);
				adviceInvocationArgs[index] = parameter.getBinding();
				numBound++;
			}
		}
		//  绑定 returning 
		if (this.returningName != null) {
    
      
			Integer index = this.argumentBindings.get(this.returningName);
			adviceInvocationArgs[index] = returnValue;
			numBound++;
		}
		// 绑定 throwing
		if (this.throwingName != null) {
    
      
			Integer index = this.argumentBindings.get(this.throwingName);
			adviceInvocationArgs[index] = ex;
			numBound++;
		}
	}

	if (numBound != this.parameterTypes.length) {
    
      
		throw new IllegalStateException("Required to bind " + this.parameterTypes.length +
				" arguments, but only bound " + numBound + " (JoinPointMatch " +
				(jpMatch == null ? "was NOT" : "WAS") + " bound in invocation)");
	}

	return adviceInvocationArgs;
}

invokeAdviceMethodWithGivenArgs

通过反射调用 Advice 方法

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    
      
	Object[] actualArgs = args;
	if (this.aspectJAdviceMethod.getParameterCount() == 0) {
    
      
		actualArgs = null;
	}
	try {
    
      
		// 设置方法访问权限(即使是私有的也能访问)
		ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
		// 这里就要用到 AspectInstanceFactory 对象了,我们通常把 Aspect 定义成一个类,
		// 类中包括 Pointcut 和 Advice,要调用 Advice,就得拿到 Aspect 对象
		return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
	}
	catch (IllegalArgumentException ex) {
    
      
		throw new AopInvocationException("Mismatch on arguments to advice method [" +
				this.aspectJAdviceMethod + "]; pointcut expression [" +
				this.pointcut.getPointcutExpression() + "]", ex);
	}
	catch (InvocationTargetException ex) {
    
      
		throw ex.getTargetException();
	}
}
setArgumentNamesFromStringArray

设置 Advice 注解中 argNames 属性

public void setArgumentNamesFromStringArray(String... args) {
    
      
	this.argumentNames = new String[args.length];
	for (int i = 0; i < args.length; i++) {
    
      
		// 去除空格
		this.argumentNames[i] = StringUtils.trimWhitespace(args[i]);
		// 验证参数名是不是符合 Java 命名规则的变量名
		if (!isVariableName(this.argumentNames[i])) {
    
      
			throw new IllegalArgumentException(
					"'argumentNames' property of AbstractAspectJAdvice contains an argument name '" +
					this.argumentNames[i] + "' that is not a valid Java identifier");
		}
	}
	if (this.argumentNames != null) {
    
      
		// 这里处理在注解 argNames 属性中没有指定
		// 但是在方法参数列表第一个参数又是 JoinPoint/ProceedingJoinPoint/JoinPoint.StaticPart 情况
		if (this.aspectJAdviceMethod.getParameterCount() == this.argumentNames.length + 1) {
    
      
			Class<?> firstArgType = this.aspectJAdviceMethod.getParameterTypes()[0];
			if (firstArgType == JoinPoint.class ||
					firstArgType == ProceedingJoinPoint.class ||
					firstArgType == JoinPoint.StaticPart.class) {
    
      
				String[] oldNames = this.argumentNames;
				this.argumentNames = new String[oldNames.length + 1];
				this.argumentNames[0] = "THIS_JOIN_POINT";
				System.arraycopy(oldNames, 0, this.argumentNames, 1, oldNames.length);
			}
		}
	}
}


getJoinPoint

重写 around 通知以返回正在进行的 joinPoint。

protected JoinPoint getJoinPoint() {
    
      
	return currentJoinPoint();
}

public static JoinPoint currentJoinPoint() {
    
      
	// 在获取 Advisor 的时候会添加这个 ExposeInvocationInterceptor(org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#extendAdvisors)
	MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
	if (!(mi instanceof ProxyMethodInvocation)) {
    
      
		throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
	}
	ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
	JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
	if (jp == null) {
    
      
		// 新建一个 MethodInvocationProceedingJoinPoint 对象放入 ProxyMethodInvocation 用户属性中
		jp = new MethodInvocationProceedingJoinPoint(pmi);
		pmi.setUserAttribute(JOIN_POINT_KEY, jp);
	}
	return jp;
}

invokeAdviceMethod

如方法名 调用 advice 方法

// 非 Around advice 调用,里面的方法都已经分析过了
protected Object invokeAdviceMethod(
		@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
		throws Throwable {
    
      
	
	return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}

// Around advice 调用,里面的方法都已经分析过了
protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
	 	@Nullable Object returnValue, @Nullable Throwable t) throws Throwable {
    
      

return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
}

setThrowingNameNoCheck/setReturningNameNoCheck

setThrowingNameNoCheck:我们需要在这个级别保存异常的名称,以便进行参数绑定计算,这个方法允许 afterThrowing advice 子类设置名称。
setReturningNameNoCheck:类似

protected void setThrowingNameNoCheck(String name) {
    
      
	// 如果不是变量名称,当成类的全限定名来处理
	if (isVariableName(name)) {
    
      
		this.throwingName = name;
	}
	else {
    
      
		try {
    
      
			this.discoveredThrowingType = ClassUtils.forName(name, getAspectClassLoader());
		}
		catch (Throwable ex) {
    
      
			throw new IllegalArgumentException("Throwing name '" + name  +
					"' is neither a valid argument name nor the fully-qualified " +
					"name of a Java type on the classpath. Root cause: " + ex);
		}
	}
}


示例
public interface EchoService {
    
      
    String echo(String message);
}

public class DefaultEchoServiceImpl implements EchoService {
    
      
    @Override
    public String echo(String message) {
    
      
        if (new Random().nextBoolean()) {
    
      
            throw new RuntimeException("发生异常啦!!!参数 message 为:" + message);
        }
        return message;
    }
}


@Aspect
public class AspectJAnnotationAspect {
    
      
    @AfterThrowing(value = "execution(public * com.wenhai.spring.aop.features.service.*.*(..)))", throwing = "java.lang.RuntimeException")
    public void afterThrowing() {
    
      
        System.out.println("afterThrowing 抛出的异常为" );
    }
}

public class AspectJAnnotationDemo {
    
      
    public static void main(String[] args) {
    
      
        EchoService echoService = new DefaultEchoServiceImpl();
        AspectJProxyFactory factory = new AspectJProxyFactory();
        factory.setTarget(echoService);
        factory.addAspect(AspectJAnnotationAspect.class);
        EchoService proxy = factory.getProxy();
        proxy.echo("文海");
    }
}

AspectJAroundAdvice

Spring AOP around adivce(MethodInterceptor) 包装 AspectJ advice 方法 。暴露 ProceedingJoinPoint。

类图

在这里插入图片描述
由类图可知:

  • 实现了 MethodInterceptor 接口,少一步适配步骤
  • 继承了 AbstractAspectJAdvice 抽象类
方法解读
isBeforeAdvice/isAfterAdvice

实现 AspectJPrecedenceInformation 用于同一个 aspect 排序(AspectJPrecedenceComparator#comparePrecedenceWithinAspect


	@Override
	public boolean isBeforeAdvice() {
    
      
		return false;
	}
	
	@Override
	public boolean isAfterAdvice() {
    
      
		return false;
	}

supportsProceedingJoinPoint

覆写了父类方法(默认是 false)只有 around advice 参数才能绑定 ProceedingJoinPoint 对象,

@Override
protected boolean supportsProceedingJoinPoint() {
    
      
	return true;
}
invoke

实现了 MethodInterceptor#invoke 方法,advice 功能都是基于拦截器实现的。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    
      
	if (!(mi instanceof ProxyMethodInvocation)) {
    
      
		throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
	}
	ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
	// 新建 ProceedingJoinPoint 对象绑定到 Around Advice 方法参数上
	ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
	// 调用父类 getJoinPointMatch 方法获取 JoinPointMatch(在 AbstractAspectJAdvice 中已分析))
	JoinPointMatch jpm = getJoinPointMatch(pmi);
	// 调用父类 invokeAdviceMethod 方法(在 AbstractAspectJAdvice 中已分析)
	return invokeAdviceMethod(pjp, jpm, null, null);
}

AspectJMethodBeforeAdvice

Spring AOP advice 包装 AspectJ 前置 advice 方法

类图

在这里插入图片描述
由类图可知:

  • 实现了 MethodBeforeAdvice 接口
  • 没有实现 MethodInterceptor 接口,多一步适配成 MethodBeforeAdviceInterceptor
  • 继承了 AbstractAspectJAdvice 抽象类
方法解读
isBeforeAdvice/isAfterAdvice

实现 AspectJPrecedenceInformation 用于同一个 aspect 排序(AspectJPrecedenceComparator#comparePrecedenceWithinAspect


	@Override
	public boolean isBeforeAdvice() {
    
      
		return true;
	}
	
	@Override
	public boolean isAfterAdvice() {
    
      
		return false;
	}

before

实现了 MethodBeforeAdvice 中的 before 方法。会由 MethodBeforeAdviceInterceptor#invoke 来调用

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 {
    
      
		// 在拦截链执行之前调用 advice 之前 adivce 方法
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}

}

AspectJMethodBeforeAdvice#before 方法源码

@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
    
      
	// 调用父类 getJoinPointMatch 和  invokeAdviceMethod (在 AbstractAspectJAdvice 中已分析)
	invokeAdviceMethod(getJoinPointMatch(), null, null);
}

AspectJAfterAdvice

Spring AOP advice 包装 AspectJ 最终 advice 方法

类图

在这里插入图片描述
由类图可知:

  • 实现了 MethodInterceptor 接口,少一步适配步骤
  • 继承了 AbstractAspectJAdvice 抽象类
方法解读
isBeforeAdvice/isAfterAdvice

实现 AspectJPrecedenceInformation 用于同一个 aspect 排序(AspectJPrecedenceComparator#comparePrecedenceWithinAspect


	@Override
	public boolean isBeforeAdvice() {
    
      
		return false;
	}
	
	@Override
	public boolean isAfterAdvice() {
    
      
		return true;
	}

invoke

实现了 MethodInterceptor#invoke 方法,advice 功能都是基于拦截器实现的。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    
      
	try {
    
      
		// 先执行拦截链中的方法,再finlly 代码块执行 最终 advice
		return mi.proceed();
	}
	finally {
    
      
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}
}

AspectJAfterReturningAdvice

Spring AOP advice 包装 AspectJ 返回 advice 方法

类图

在这里插入图片描述
由类图可知:

  • 实现了 AfterReturningAdvice 接口
  • 没有实现 MethodInterceptor 接口,多一步适配成 MethodBeforeAdviceInterceptor
  • 继承了 AbstractAspectJAdvice 抽象类
方法解读
isBeforeAdvice/isAfterAdvice

实现 AspectJPrecedenceInformation 用于同一个 aspect 排序(AspectJPrecedenceComparator#comparePrecedenceWithinAspect


	@Override
	public boolean isBeforeAdvice() {
    
      
		return false;
	}
	
	@Override
	public boolean isAfterAdvice() {
    
      
		return true;
	}

setReturningName

覆盖父类方法,通过 @AfterReturing 注解中的 returing 属性值,与 Advice 方法参数名称进行绑定

@Override
public void setReturningName(String name) {
    
      
    // 设置 returingName 属性或者设置 discoveredReturingType 属性(在父类已分析)
	setReturningNameNoCheck(name);
}

afterReturning

实现了 AfterReturningAdvice#afterReturning,会由 AfterReturningAdviceInterceptor#invoke 来调用

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();
		// 在调用 advice 返回 advice 方法,如果有异常则不知道
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}

}

AspectJAfterReturningAdvice#afterReturning 源码

@Override
public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
    
      
	// 返回值类型是否与 discoveredReturningType 或者 discoveredReturningGenericType 匹配
	if (shouldInvokeOnReturnValueOf(method, returnValue)) {
    
      
		invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
	}
}

AspectJAfterThrowingAdvice

Spring AOP advice 包装 AspectJ 异常 advice 方法

类图

在这里插入图片描述
由类图可知:

  • 实现了 MethodInterceptor 接口,少一步适配步骤
  • 实现 AfterAdvice 接口
  • 继承了 AbstractAspectJAdvice 抽象类
方法解读
isBeforeAdvice/isAfterAdvice

实现 AspectJPrecedenceInformation 用于同一个 aspect 排序(AspectJPrecedenceComparator#comparePrecedenceWithinAspect


	@Override
	public boolean isBeforeAdvice() {
    
      
		return false;
	}
	
	@Override
	public boolean isAfterAdvice() {
    
      
		return true;
	}

setThrowingName

覆盖父类方法,通过 @AfterThrowing 注解中的 throwing 属性值,与 Advice 方法参数名称进行绑定

@Override
public void setThrowingName(String name) {
    
      
	// 设置 throwingName 属性或者设置 discoveredThrowingType 属性(在父类已分析)
	setThrowingNameNoCheck(name);
}

invoke

实现了 MethodInterceptor#invoke 方法,advice 功能都是基于拦截器实现的。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    
      
	try {
    
      
		// 拦截链放在 try catch 语法块里面执行,如果有异常了,就执行 异常 advice
		return mi.proceed();
	}
	catch (Throwable ex) {
    
      
		// 异常类型是否与绑定的异常类型相匹配
		if (shouldInvokeOnThrowing(ex)) {
    
      
			// 调用 advice 方法
			invokeAdviceMethod(getJoinPointMatch(), null, ex);
		}
		throw ex;
	}
}

AspectJ 大总结(一张图搞定)

在这里插入图片描述