Spring AOP( @Around, @Before, @After, @AfterReturning, @AfterThrowing )注解源码说明

前言

在springAOP的运用中,@Around, @Before, @After, @AfterReturning, @AfterThrowing的使用频率非常高,因此弄清他们的原理非常重要。

在学习其原理之前,我们先弄清这2个类JoinPoint和Advice

JoinPoint

JoinPoint作用:描述目标方法
在这里插入图片描述

public interface JoinPoint {

    //连接点的缩写字符串表示法
    String toString();
    String toShortString();
    String toLongString();

    //获取代理对象
    Object getThis();
    //获取目标方法的对象
    Object getTarget();

    //目标方法的入参
    Object[] getArgs();

   //忽略部分代码.....
}

ProceedingJoinPoint作用:描述和调用目标对象

public interface ProceedingJoinPoint extends JoinPoint {
    //忽略代码....

    //调用没有入参的目标方法
    public Object proceed() throws Throwable;
    //调用有入参的目标方法
    public Object proceed(Object[] args) throws Throwable;

}

Advice

Advice作用:建议忠告, 劝告, 通知。表示的是在 Pointcut 点上应该执行的方法。而这些方法可以在目标方法之前、之后、包裹、抛出异常等等任何地方执行。 其主要分成两类:普通advice 与Interceptor/MethodInterceptor

@Before

  • AspectJMethodBeforeAdvice是解析 AspectJ 中的@Before 属性来生成的Advice
  • 其拦截器是MethodBeforeAdviceInterceptor
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
	//忽略代码....
	@Override
	public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}	
}

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
	private final MethodBeforeAdvice advice;
    //忽略代码...
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
	    //这是执行Before增强方法
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		//这是执行目标方法
		return mi.proceed();
	}
}

结论:Before增强方法,是把方法放在目标方法前。如果Before增强方法抛出异常,目标方法就不会执行

@Before案例

public class HelloService {
	@Override
	public void hello() {
		System.out.println("调用hello方法...");
	}
}

@Aspect
public class MyAspect {
	@Pointcut("execution(* hello(..))")
	private void pointcut() {}

	@Before("pointcut()")
	public void before1(JoinPoint joinPoint) throws Throwable {
		System.out.println("执行Before方法....");
	}
}
public class App {
	public static void main( String[] args ){
		AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new HelloService());
		// 注意:此处得MyAspect类上面的@Aspect注解必不可少
		proxyFactory.addAspect(MyAspect.class);
		//proxyFactory.setProxyTargetClass(true);//是否需要使用CGLIB代理
		HelloService proxy = proxyFactory.getProxy();
		proxy.hello();
	}
}

运行程序输出:

执行Before方法....
调用hello方法...

@After

  • AspectJAfterAdvice即是一个After通知器,也是一个After拦截器
public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable {
	//忽略代码...
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
		    //执行目标方法
			return mi.proceed();
		}
		finally {
		   //执行After增强方法
			invokeAdviceMethod(getJoinPointMatch(), null, null);
		}
	}
}

结论:After增强方法是在目标方法执行之后调用。不管目标方法是否抛出异常。

After案例

public class HelloService {
	@Override
	public void hello() {
		System.out.println("调用hello方法...");
		int i = 0/0;
	}
}
@Aspect
public class MyAspect {

	@Pointcut("execution(* hello(..))")
	private void pointcut() {}

	@After("pointcut()")
	public void before1(JoinPoint joinPoint) throws Throwable {
		System.out.println("执行After方法....");
	}

}

public class App {
	public static void main( String[] args ){
		AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new HelloService());
		// 注意:此处得MyAspect类上面的@Aspect注解必不可少
		proxyFactory.addAspect(MyAspect.class);
		//proxyFactory.setProxyTargetClass(true);//是否需要使用CGLIB代理
		HelloService proxy = proxyFactory.getProxy();
		proxy.hello();
	}
}

注意:我在hello方法执行了这段代码:0/0

运行程序输出

调用hello方法...
执行Before方法....
Exception in thread "main" java.lang.ArithmeticException: / by zero
....

@AfterReturning

  • 其重要属性如下
@Target(ElementType.METHOD)
public @interface AfterReturning {
    //绑定建议的切入点表达式
    String value() default "";
    //通知签名中要将返回值绑定到的参数的名称
    String returning() default "";
}

AspectJAfterReturningAdvice是解析 AspectJ 中的 @AfterReturning 注解来生成的通知器,其拦截器是AfterReturningAdviceInterceptor

public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice	implements AfterReturningAdvice, AfterAdvice,{
    //忽略代码...
	public void afterReturning(Object returnValue, Method method, Object[] args,  Object target) throws Throwable {
		if (shouldInvokeOnReturnValueOf(method, returnValue)) {
			invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
		}
	}
	
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
	private final AfterReturningAdvice 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;
	}

}

结论:AfterReturning增强方法是在目标方法之后执行,如果目标方法执行错误,那么AfterReturning增强方法就不会执行

AfterReturning案例

public class HelloService {
	public int hello() {
		System.out.println("调用hello方法...");
        return 1;
	}
}
@Aspect
public class MyAspect {

	@Pointcut("execution(* hello(..))")
	private void pointcut() {}

	@AfterReturning(value="pointcut()",returning="result")
	public void before1(JoinPoint joinPoint,Object result) throws Throwable {
		System.out.println("执行AfterReturning方法....");
		System.out.println("目标方法返回值是"+result);
	}

}
public class App {
	public static void main( String[] args ){
		AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new HelloService());
		// 注意:此处得MyAspect类上面的@Aspect注解必不可少
		proxyFactory.addAspect(MyAspect.class);
		//proxyFactory.setProxyTargetClass(true);//是否需要使用CGLIB代理
		HelloService proxy = proxyFactory.getProxy();
		proxy.hello();
	}
}

运行程序输出

调用hello方法...
执行AfterReturning方法....
目标方法返回值是1

@AfterThrowing

  • 其重要属性如下
@Target(ElementType.METHOD)
public @interface AfterReturning {
    //绑定建议的切入点表达式
    String value() default "";
    //通知签名中要将引发的异常绑定到的参数的名称
    String throwing() default "";
}
  • AspectJAfterThrowingAdvice即是解析 AspectJ 中的 @AfterThrowing 注解来生成的通知器,也是其拦截器
public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice{
    //忽略代码....
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
		    //这里是调用目标方法
			return mi.proceed();
		}
		catch (Throwable ex) {
			if (shouldInvokeOnThrowing(ex)) {
			    //这里是AfterThrowing增强方法
				invokeAdviceMethod(getJoinPointMatch(), null, ex);
			}
			//这里又把异常重新抛出去了
			throw ex;
		}
	}
}

结论:AfterThrowing增强方法是目标方法抛出异常后调用的方法,其执行位置在catch块中。

AfterThrowing案例

public class HelloService {
	public void hello() {
		System.out.println("调用hello方法...");
        int i = 0/0;
	}
}
@Aspect
public class MyAspect {
	@Pointcut("execution(* hello(..))")
	private void pointcut() { }
    //注意:joinPoint参数必须写在第一位
	@AfterThrowing(value="pointcut()",throwing="exception")
	public void before(JoinPoint joinPoint,Exception exception) throws Throwable {
		System.out.println("执行AfterThrowing方法....");
		System.out.println(exception.getMessage());
	}
}
public class App {
	public static void main( String[] args ) {
		AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new HelloService());
		// 注意:此处得MyAspect类上面的@Aspect注解必不可少
		proxyFactory.addAspect(MyAspect.class);
		//proxyFactory.setProxyTargetClass(true);//是否需要使用CGLIB代理
		HelloService proxy = proxyFactory.getProxy();
		proxy.hello();
	}
}

运行程序输出

Connected to the target VM, address: '127.0.0.1:49250', transport: 'socket'
调用hello方法...
执行AfterThrowing方法....
/ by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero

@Around

  • AspectJAroundAdvice即是解析 AspectJ 中的 @Around 注解来生成的通知器,也是其拦截器

个人理解:Around增加方法有些特殊,它是把目标方法变成增加方法,和上面的不同,上面的几个是添加增强方法

public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {
	@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 pjp = lazyGetProceedingJoinPoint(pmi);
		JoinPointMatch jpm = getJoinPointMatch(pmi);
		//调用Around增加方法
		return invokeAdviceMethod(pjp, jpm, null, null);
	}
}

案例

public class HelloService {


	public int hello() {
		System.out.println("调用hello方法...");
		return 1;

	}
}

@Aspect
public class MyAspect {

	@Pointcut("execution(* hello(..))")
	private void pointcut() { }

	@Around("pointcut()")
	public Object before(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("环绕前(@Around)");
		Object result = joinPoint.proceed();
		System.out.println("环绕后(@Around),结果是:"+result);
        return result;
	}

}

public class App {


	public static void main( String[] args ) {


		AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new HelloService());
		// 注意:此处得MyAspect类上面的@Aspect注解必不可少
		proxyFactory.addAspect(MyAspect.class);
		//proxyFactory.setProxyTargetClass(true);//是否需要使用CGLIB代理
		HelloService proxy = proxyFactory.getProxy();
		proxy.hello();



	}
}

测试输出:
环绕前(@Around)
调用hello方法…
环绕后(@Around),结果是:1

如果把

@Aspect
public class MyAspect {

	@Pointcut("execution(* hello(..))")
	private void pointcut() { }

	@Around("pointcut()")
	public void before(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("环绕前(@Around)");
		Object result = joinPoint.proceed();
		System.out.println("环绕后(@Around),结果是:"+result);
        
	}

}

测试输出:

环绕前(@Around)
调用hello方法...
环绕后(@Around),结果是:1
Exception in thread "main" org.springframework.aop.AopInvocationException: Null return value from advice does not match 

1、为什么会报错:增强方法没有返回值与的目标方法返回类型不匹配?`
因为目标方法有返回值,但是增强方法没有,可以理解这是一个消弱,不是增强,所以报错
但是注意:如果目标方法没有返回值,但是增强方法有,那就不会报错。把无返回值的方法变成有返回值,这也是一种增强吧

2、在上面几个增强方法的案例中,你可能有这样疑惑,入参ProceedingJoinPoint或者JoinPoint是怎么注入的
在这里我们以AspectJMethodBeforeAdvice代码为例子。

这里是调用增强方法的入空

public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
	
	public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
	    //getJoinPointMatch是获取匹配目标方法的对象
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}	
}
public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation, Serializable {

protected Object invokeAdviceMethod(
			@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
			throws Throwable {
        //getJoinPoint获取切入点
        //argBinding是绑定增强方法的参数
		return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
	}

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

public static JoinPoint currentJoinPoint() {
		MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
		if (!(mi instanceof ProxyMethodInvocation)) {
			throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
		}
		ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
		//在缓存map中能获取以joinpoint为键的值
		JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
		//如果不能获取,那么就增加
		if (jp == null) {
			jp = new MethodInvocationProceedingJoinPoint(pmi);
			pmi.setUserAttribute(JOIN_POINT_KEY, jp);
		}
		//返回切入值
		return jp;
	}

protected Object[] argBinding(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
			@Nullable Object returnValue, @Nullable Throwable ex) {
        //验证JoinPoint类型,下面有讲解
		calculateArgumentBindings();

		// AMC启动
		Object[] adviceInvocationArgs = new Object[this.parameterTypes.length];
		int numBound = 0;

		if (this.joinPointArgumentIndex != -1) {
			adviceInvocationArgs[this.joinPointArgumentIndex] = jp;
			numBound++;
		}
		else if (this.joinPointStaticPartArgumentIndex != -1) {
			adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();
			numBound++;
		}

		if (!CollectionUtils.isEmpty(this.argumentBindings)) {
			// 从切入点匹配绑定
			if (jpMatch != null) {
				PointcutParameter[] parameterBindings = jpMatch.getParameterBindings();
				for (PointcutParameter parameter : parameterBindings) {
					String name = parameter.getName();
					Integer index = this.argumentBindings.get(name);
					adviceInvocationArgs[index] = parameter.getBinding();
					numBound++;
				}
			}
			// 绑定返回值
			if (this.returningName != null) {
				Integer index = this.argumentBindings.get(this.returningName);
				adviceInvocationArgs[index] = returnValue;
				numBound++;
			}
			// 绑定异常
			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;
	}

综上可述:增强方法的入参是AbstractAspectJAdvice对象帮我们注入的。

3、在上面几个增强方法的案例中,你可能有些疑惑,为什么@Around增强方法使用ProceedingJoinPoint作为入参,而其他的增强方法不用?
一:从几个增强方法代码调用理解来看,只有@Around增强方法需要我们主动调用目标方法,其他几个不需要,所以没必要使用
ProceedingJoinPoint。二,spring定死的,如下代码:

public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation, Serializable {

  
   public final synchronized void calculateArgumentBindings() {
		// 简单的例子。。。没什么好绑的。
		if (this.argumentsIntrospected || this.parameterTypes.length == 0) {
			return;
		}
		int numUnboundArgs = this.parameterTypes.length;
		//获取参数类型的class对象
		Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
		//进行参数类型的class对象验证
		//maybeBindJoinPoint是判断是否是JoinPoint类型
		//maybeBindProceedingJoinPoint是判断是否是支持ProceedingJoinPoint类型
		//AbstractAspectJAdvice的maybeBindProceedingJoinPoint默认返回false
		//目前只有AspectJAroundAdvice重写这个方法,返回值是true
		if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0]) ||
				maybeBindJoinPointStaticPart(parameterTypes[0])) {
			numUnboundArgs--;
		}

		if (numUnboundArgs > 0) {
			// 需要按从切入点匹配返回的名称绑定参数
			bindArgumentsByName(numUnboundArgs);
		}
		this.argumentsIntrospected = true;
	}

增强方法顺序

@Aspect
public class MyAspect {

	@Pointcut("execution(* hello(..))")
	private void pointcut() { }

	@After(value="pointcut()")
	public void after(JoinPoint joinPoint) throws Throwable {
		System.out.println("执行after方法....");

	}

	@Before(value="pointcut()")
	public void before(JoinPoint joinPoint) throws Throwable {
		System.out.println("执行before方法....");
	}


	@AfterReturning(value="pointcut()",returning="result")
	public void afterReturning(JoinPoint joinPoint,Object result) throws Throwable {
		System.out.println("执行afterReturning方法....");

	}

	@AfterThrowing(value="pointcut()",throwing="exception")
	public void afterThrowing(JoinPoint joinPoint,Exception exception) throws Throwable {
		System.out.println("执行afterThrowing方法....");

	}

	@Around(value="pointcut()")
	public void around(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("执行around前置方法方法....");
		joinPoint.proceed();
		System.out.println("执行around后置方法方法....");
	}
}

目标方法正常输出`

执行around前置方法方法....
执行before方法....
调用hello方法...
执行afterReturning方法....
执行after方法....
执行around后置方法方法....

目标方法错误输出`

执行around前置方法方法....
执行before方法....
调用hello方法...
执行afterThrowing方法....
执行after方法....

影响其调用顺序的源码是

public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable {
	private static final Comparator<Method> METHOD_COMPARATOR;

	static {
	  //从这可以看出Around增强方法优先级最高
		Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
				new InstanceComparator<>(
						Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
				(Converter<Method, Annotation>) method -> {
					AspectJAnnotation<?> annotation =
						AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
					return (annotation != null ? annotation.getAnnotation() : null);
				});
		Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
		METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
	}

延伸: 不同aspect,advice的执行顺序
Spring AOP通过Order控制aspect的优先级,来控制不同aspect,advice的执行顺序,有两种方式:
1、Aspect 类添加注解:org.springframework.core.annotation.Order,使用注解value属性指定优先级。
2、 Aspect 类实现接口:org.springframework.core.Ordered,实现 Ordered 接口的 getOrder() 方法。

发布了34 篇原创文章 · 获赞 0 · 访问量 1365

猜你喜欢

转载自blog.csdn.net/qq_41071876/article/details/104648575