Spring Aop-@Before,@After,@Around,@AfterReturning,@AfterThrowing链路调用分析
发布日期:2021-06-29 03:44:37 浏览次数:3 分类:技术文章

本文共 23599 字,大约阅读时间需要 78 分钟。

文章目录


一 简介

在Spring Aop的源码中,关于拦截器的调度顺序是一个值得分析的点,也可以让我们深入的理解关于@After,@Before,@Around,@AfterReturning的相关执行顺序,废话不多说,直接上源码

二 分析

无论是基于Xml还是基于注解的方式,实际上都是在Spring的ioc容器中注入一个BeanPostProcessor类型的AnnotationAwareAspectJAutoProxyCreator类,并且该类有两个比较重要的属性,proxyTargetClassexposeProxy

  • proxyTargetClass:属性默认为false,该属性是用来控制被动态代理的类是基于CGLIB动态代理(true),还是基于JDK的动态代理,设置为true是,使用CGLIB动态代理,默认情况下,并且被代理的类不存在对应的实现接口时,仍然使用基于CGLIB的动态代理来创建对应的代理类,存在对应的实现接口时,使用JDK动态代理。
  • exposeProxy:默认属性为false,该属性用于暴露该代理对象,可以通过AopContext.currentProxy();因为在一般情况下,对于一个存在增强方法的被代理类的a方法,如果内部调用通过this.b(),去执行存在增强方法的b方法的时候,实际上最终得执行结果是并不会执行配置在b上的增强方法。原因很简单,因为在执行a方法的时候,实际上是通过proxy代理类进行执行,会进行拦截,而后执行this.b()方法的时候,实际上执行的是当前被代理类对象上的b方法,并不是执行proxy代理类上的b方法,因为无法增强b方法,因此我们可以通过(A)AopContext.currentProxy().b()
    的方式进行增强b方法

补充:

对于上面的exposeProxy中的 含有增强方法的被代理类的a方法内部通过this.b()调用通用含有增强方法的b的时候,b实际上不增强的问题我们可以很简单的举一个例子说明一哈:
例如,我们创建一个接口:HelloTest

public interface HelloTest {    int say(String name);    void hello();}

实现类:HelloTestImpl

public class HelloTestImpl implements HelloTest{    @Override    public int say(String name) {        System.out.println("Hello:" + name);        this.hello();        return 0;    }    @Override    public void hello() {        System.out.println("你好,zhoucg");    }}

InvocationHandler类:CustomInvocationHandler

public class CustomInvocationHandler implements InvocationHandler {    private Object target ;    public CustomInvocationHandler(Object target) {        this.target = target;    }    /**     * 设置被动态代理的类的增强方法     * @param proxy     * @param method     * @param args     * @return     * @throws Throwable     */    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("Before invocation==============");        Object retVal = method.invoke(target,args);        System.out.println("After invocation===============");        return retVal;    }}

我们通过JDK的动态代理,看一下

//设置为true,会在工程根目录生成$Proxy0.class代理类(com.sun.proxy.$Proxy0.class)        System.getProperties().put(                "sun.misc.ProxyGenerator.saveGeneratedFiles", "true");        //String saveGeneratedFiles = System.getProperty("sun.misc.ProxyGenerator.saveGeneratedFiles");        //System.out.println(saveGeneratedFiles);        HelloTest helloWord = new HelloTestImpl();        CustomInvocationHandler customInvocationHandler = new CustomInvocationHandler(                helloWord);        //通过Proxy.newProxyInstance生成代理对象        HelloTest proxy = (HelloTest) Proxy.newProxyInstance(                HelloTest.class.getClassLoader(),                helloWord.getClass().getInterfaces(), customInvocationHandler);        //调用say方法        proxy.say("test");

这个时候,我们反编译获取到的动态代理类:$Proxy0

public final class $Proxy0 extends Proxy implements HelloTest {    private static Method m1;    private static Method m3;    private static Method m2;    private static Method m4;    private static Method m0;    public $Proxy0(InvocationHandler var1) throws  {        super(var1);    }    public final void hello() throws  {        try {            super.h.invoke(this, m3, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    public final int say(String var1) throws  {        try {            return ((Integer)super.h.invoke(this, m4, new Object[]{var1})).intValue();        } catch (RuntimeException | Error var3) {            throw var3;        } catch (Throwable var4) {            throw new UndeclaredThrowableException(var4);        }    }	。。。。。 equals,hash,tostring。。。    static {        try {            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});            m3 = Class.forName("com.zcswl.pattern.proxy.HelloTest").getMethod("hello", new Class[0]);            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);            m4 = Class.forName("com.zcswl.pattern.proxy.HelloTest").getMethod("say", new Class[]{Class.forName("java.lang.String")});            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);        } catch (NoSuchMethodException var2) {            throw new NoSuchMethodError(var2.getMessage());        } catch (ClassNotFoundException var3) {            throw new NoClassDefFoundError(var3.getMessage());        }    }}

我们会很清晰的看到:

当执行 proxy.say(“test”);的时候,实际上是执行了

public final int say(String var1) throws  {        try {            return ((Integer)super.h.invoke(this, m4, new Object[]{var1})).intValue();        } catch (RuntimeException | Error var3) {            throw var3;        } catch (Throwable var4) {            throw new UndeclaredThrowableException(var4);        }    }

方法,内部调用了InvocationHandler的invoke方法进行执行,实际上就是执行了CustomInvocationHandler中的invoke方法

这里的
return ((Integer)super.h.invoke(this, m4, new Object[]{var1})).intValue(); 是proxy的代理对象,我们在看对应的CustomInvocationHandler中的invoke的方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("Before invocation==============");        Object retVal = method.invoke(target,args);        System.out.println("After invocation===============");        return retVal;    }

因为其method的invoke的参数是target,即目标对象(注意这里不是代理对象),因此其自然是没有走对应的代理对象的,因此不会执行其对应的增强方法

这里,肯定有人会问,那既然CustomInvocationHandler中的invoke方法中的 Object retVal = method.invoke(target,args); 传递的是targer,即目标对象,那我这样写

Object retVal = method.invoke(proxy,args); 不就好了
这个想法很危险,你这个就违背了动态代理的设计初衷,你这样写,实际上就没得代理了,动态代理就是代理对应的目标对象,并进行增强。

这样写直接是会导致栈溢出,一直调用错误

可以参考我的另一个文章:


正题:

首先,我们定义一个接口ITest

public interface ITest {	void test();	void subTest();	String afterReturn(String a);}

定义接口的实现类(被代理对象)TestBean

public class TestBean implements ITest{	private String testStr = "testStr";	public String getTestStr() {		return testStr;	}	public void setTestStr(String testStr) {		this.testStr = testStr;	}	@Override	public void test() {		System.out.println("内部执行====test");		this.subTest();		System.out.println(AopContext.currentProxy()  == this);//		boolean aopProxy = AopUtils.isAopProxy(this);//		boolean aopProxy1 = AopUtils.isAopProxy(AopContext.currentProxy());//		System.out.println(this);		//(ITest)AopContext.currentProxy().subTest();	}	@Override	public void subTest() {		System.out.println("内部执行=======subTest");	}	@Override	public String afterReturn(String a) {		return a+"zcg";	}	@Override	public String toString() {		return "TestBean{" +				"testStr='" + testStr + '\'' +				'}';	}}

定义切面,和相关的切入点,以及对应的增强方法AspectJTest

@Aspectpublic class AspectJTest {	@Pointcut("execution(* com.zcs.aop.usedemo.TestBean.test(..))")	public void test() {	}	@Pointcut("execution(* com.zcs.aop.usedemo.TestBean.subTest(..))")	public void subTest() {	}	@Before("test()")	public void beforeTest() {		System.out.println("beforeTest");	}	@After("test()")	public void afterTest() {		System.out.println("afterTest");	}	@Around("test() || subTest()")	public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {		System.out.println("=========beforeSubTest==========");		Object o = null;		try {			o = proceedingJoinPoint.proceed();		} catch (Throwable throwable) {			throwable.printStackTrace();		}		System.out.println("=========afterSubTest==========");		return o;	}	/**	 * AfterReturning 增强处理可以访问到方法的返回值,但它不能改变目标方法的返回值	 * @param rvt	 * @return	 */	@AfterReturning(returning="rvt", pointcut="execution(* com.zcs.aop.usedemo.TestBean.afterReturn(..))")	public String afterRet(String rvt) {		return rvt+" wl";	}}

这里,我们通过XML配置的方式,

执行代码:

@Test	public void testAopDemo() {			ApplicationContext context = new ClassPathXmlApplicationContext("aopDemo-Test.xml");			ITest testBean = (ITest) context.getBean("test");			testBean.test();	}

三 源码

1,在注入了AnnotationAwareAspectJAutoProxyCreator关键的AOP处理类之后,实际上,我们会在Bean的实例化之前执行InstantiationAwareBeanPostProcessor对应的postProcessBeforeInstantiation(Class<?> beanClass, String beanName)方法,该方法定义在AbstractAutoProxyCreator抽象类

该方法,

  • 1,对于配置了customTargetSourceCreators属性的情况,该方法是会在实例化被代理对象之前进行实例化对应的代理对象,
  • 2,对于不是切面类(含有@AspectJ)的实例化,该方法会查找当前ioc容器中得的强器,并加入缓存操作,方便下次直接获取增强器

2,实际的实例化动态代理对象实际上是在BeaPostProcessor接口的Object postProcessAfterInitialization(@Nullable Object bean, String beanName) 方法中

@Override	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {		if (bean != null) {			// 根据给定的bean的class和name构建出个key			Object cacheKey = getCacheKey(bean.getClass(), beanName);			if (!this.earlyProxyReferences.contains(cacheKey)) {				// 如果它适合被代理,则需要封装指定bean				Object wrapObject = wrapIfNecessary(bean, beanName, cacheKey);				System.out.println(wrapObject);				return wrapObject;			}		}		return bean;	}

wrapIfNecessary方法中,实际上是首先确认当前容器内的增强器是否适用于当前创建的对象,并获取到适用于创建当前类的增强器

/**		 * 1,判断当前容器是否存在对应的切面类(使用@Aspect注释的类)		 * 2,遍历每一个切面类的方法,判断是否为对应的增强方法,如果未对应的增强方法,每一个增强方法创建对应的增强类		 * 3,判断容器中的增强类适用于当前bean的增强类AbstractAutoProxyCreator#findAdvisorsThatCanApply		 */		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

同时,我们会将对应创建动态代理类的能力交给ProxyFactory进行处理

protected Object createProxy(Class
beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } ProxyFactory proxyFactory = new ProxyFactory(); // 获取当前类中的相关属性 proxyFactory.copyFrom(this); // 决定对于给定的原始bean是否直接对它的类型(targetClass)进行代理而不是对它实现的接口进行代理 // 检查proxyFactory的proxyTargetClass属性设置以及目标bean的beanDefinition中preserveTargetClass属性设置 if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { //添加代理接口 evaluateProxyInterfaces(beanClass, proxyFactory); } } Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); // 加入增强器 proxyFactory.addAdvisors(advisors); // 定制代理 proxyFactory.setTargetSource(targetSource); // 定制代理,留作子类扩展 customizeProxyFactory(proxyFactory); // 用来控制代理工厂被配置后,是否还允许修改通知 // 缺省值为false(即在代理被配置之后,不允许修改代理的配置) proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 委托ProxyFactory去真正创建代理对象 return proxyFactory.getProxy(getProxyClassLoader()); }

并且,在创建动态代理,我们会通过proxyTargetClass参数分析是使用CGLIB动态代理还是JDK动态代理,在这个示例中,系统会使用JdkDynamicAopProxy创建动态代理对象,

@Override	public Object getProxy(@Nullable ClassLoader classLoader) {		if (logger.isDebugEnabled()) {			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());		}		// 获取接口		Class
[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); // 该类实现了InvocationHandler接口,所以传this return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }

一系列山穷水绕的逻辑之后,我们会看到我们熟悉的通过JDK创建动态代理的代码逻辑:

@Override	public Object getProxy(@Nullable ClassLoader classLoader) {		if (logger.isDebugEnabled()) {			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());		}		// 获取接口		Class
[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); // 该类实现了InvocationHandler接口,所以传this return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }

InvocationHandler传递的就是JdkDynamicAopProxy本身,下面进入重点分析:

四 链路调度

当我们执行:testBean.test();时,首先会进入到JdkDynamicAopProxy中的invoke方法中,

@Override	@Nullable	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {		MethodInvocation invocation;		Object oldProxy = null;		boolean setProxyContext = false;		// TargetSource中包含了原始类对象信息		TargetSource targetSource = this.advised.targetSource;		Object target = null;		try {			// 代码略。。。。对于equals,hashcode,方法进行处理						// Class类的isAssignableFrom(Class cls)方法:如果调用这个方法的class或接口 与 参数cls表示的类或接口相同,			// 或者是参数cls表示的类或接口的父类,则返回true。例如:			// System.out.println(ArrayList.class.isAssignableFrom(Object.class));   --> false			// System.out.println(Object.class.isAssignableFrom(ArrayList.class));  --> true			// 如果method所在类是Advised父类,则直接调用切点方法			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&					method.getDeclaringClass().isAssignableFrom(Advised.class)) {				// Service invocations on ProxyConfig with the proxy config...				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);			}			Object retVal;			// 对exposeProxy			if (this.advised.exposeProxy) {				// Make invocation available if necessary.				// 将代理类对象proxy保存到ThreadLocal中,同时获取之前存储的oldProxy				oldProxy = AopContext.setCurrentProxy(proxy);				setProxyContext = true;			}			// Get as late as possible to minimize the time we "own" the target,			// in case it comes from a pool.			// 获取目标对象及类型			target = targetSource.getTarget();			Class
targetClass = (target != null ? target.getClass() : null); // 获取当前方法的拦截器链(之前我们找的增强器统一封装成了拦截器链) List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. // 如果没有发现任何拦截器那么直接调用切点方法 Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... // 将拦截器封装在ReflectiveMethodInvocation,以便于使用其proceed进行链接调用拦截器 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. // 执行拦截器链中每个拦截器的invoke方法 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())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }

我们重点关注一下拦截器链的执行过程,首先,会将适配该方法对应的增强器封装成ReflectiveMethodInvocation,ReflectiveMethodInvocation中定义了一个currentInterceptorIndex 属性,该属性用于确定当前链路器执行到那个,

/**	 * 实现拦截器的逐一调用	 * @return	 * @throws Throwable	 */	@Override	@Nullable	public Object proceed() throws Throwable {		//	We start with an index of -1 and increment early.		// 执行完成所有增强方法后执行切点方法		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {			return invokeJoinpoint();		}		// 获取下一个要执行的拦截器		Object interceptorOrInterceptionAdvice =				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {			// Evaluate dynamic method matcher here: static part will already have been evaluated and found to match.			// 动态匹配			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 { // Dynamic matching failed. Skip this interceptor and invoke the next in the chain. // 不匹配不执行 return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. /* 普通拦截器,直接调用拦截器,比如:ExposeInvocationInterceptor、DelegatePerTargetObjectIntroductionInterceptor、 MethodBeforeAdviceInterceptor、AspectJAroundAdvice、AspectJAfterAdvice */ // 将this作为参数传递以保证当前实例中调用链的执行 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }

注意:在每一个增强器链路中,Spring都是会将链路的第一个链路器设置为ExposeInvocationInterceptor,该链路器的作用是存储对应MethodInvocation

我们会发现,在执行@After的AspectJAfterAdvice链路器的时候,首先会执行mi.proceed()方法,最终执行当前增前器对应的方法

@Override	public Object invoke(MethodInvocation mi) throws Throwable {		try {			return mi.proceed();		}		finally {			invokeAdviceMethod(getJoinPointMatch(), null, null);		}	}

在执行@Around对应的AspectJAroundAdvice链路器时,其内部是回去对应的ProceedingJoinPoint,并执行当前增前器对应的增强方法

@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);		return invokeAdviceMethod(pjp, jpm, null, null);	}

在执行@Before对应的AspectJMethodBeforeAdvice 链路器时,系统直接执行对应的增强方法

@Override	public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {		// 调用通知方法		invokeAdviceMethod(getJoinPointMatch(), null, null);	}

五 调度顺序

我们在上面的一顿源码分析了之后,实际上,我们还是没有细聊关于@After,@Around,@Before,@AfterReturning等这几个切面的调度顺序,

实际上,在同时去使用@After,@Around,@Before这几个切面的时候,Spring首先是查询容器内的所有增强类(含有@AspectJ注解或者XML注入的增强器),并且,遍历所有的增强类中的增强方法(含有@After,@Around,@Before,@AfterReturning注解,这里以注解形似讲解),其中,在这里,Spring是对于查询当前类的增强方法进行了排序,排序的顺序关系正是:Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class

/**	 * 获取增强方法	 * @param aspectClass	 * @return	 */	private List
getAdvisorMethods(Class
aspectClass) { final List
methods = new ArrayList<>(); ReflectionUtils.doWithMethods(aspectClass, method -> { // 声明为Pointcut的方法不处理 if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) { methods.add(method); } }); // 增强方法排序 methods.sort(METHOD_COMPARATOR); return methods; }
private static final Comparator
METHOD_COMPARATOR; static { Comparator
adviceKindComparator = new ConvertingComparator<>( new InstanceComparator<>( Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class), (Converter
) method -> { AspectJAnnotation
annotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); return (annotation != null ? annotation.getAnnotation() : null); }); Comparator
methodNameComparator = new ConvertingComparator<>(Method::getName); METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator); }

因此:对于单个增前器内部的增强方法而言,AOP首先会优先执行环绕通知的处理方法,环绕通知中执行了对应的方法体本身的时候触发执行@Before方法,然后再执行环绕通知的下面的方法,最后再执行@After方法 最后再执行相关的AfterReturning和AfterThrowing 相关的增强方法

但是,对于多个增强器而言,如果A增强器(含有@AspectJ)和B增强器,对于方法a而言,A增强器中的@Before 增强器 的解析是优先于 B增强器的@Around的,情况就不一样了

例如:我们在ioc中只配置一个增强器(AspectJTest)

/** * 假设是A增强器 。@Before * * @description: * @project: spring */@Aspectpublic class AspectJTest {	@Pointcut("execution(* com.zcs.aop.usedemo.TestBean.test(..))")	public void test() {	}	@Pointcut("execution(* com.zcs.aop.usedemo.TestBean.subTest(..))")	public void subTest() {	}	@Before("test()")	public void beforeTest() {		System.out.println("A增强器 beforeTest");	}	@After("test()")	public void afterTest() {		System.out.println("A增强器 afterTest");	}	@Around("test() || subTest()")	public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {		System.out.println("A增强器 =========beforeAroundTest==========");		Object o = null;		try {			o = proceedingJoinPoint.proceed();		} catch (Throwable throwable) {			throwable.printStackTrace();		}		System.out.println("A增强器 =========afterAroundTest==========");		return o;	}	/**	 * AfterReturning 增强处理可以访问到方法的返回值,但它不能改变目标方法的返回值	 * @param rvt	 * @return	 */	@AfterReturning(returning="rvt", pointcut="execution(* com.zcs.aop.usedemo.TestBean.afterReturn(..))")	public String afterRet(String rvt) {		return rvt+" wl";	}}

此时,内部的执行逻辑正如我们上面所说的:

A增强器 =========beforeAroundTest==========A增强器 beforeTestTestBean内部自己执行====testA增强器 =========afterAroundTest==========A增强器 afterTest

现在我们尝试让A 增强器只含有一个@Before的增强方法,再增加一个B增强器,让B含有@Around的增强方法

A:

/** * 假设是A增强器 。@Before * * @description: * @project: spring */@Aspectpublic class AspectJTest {	@Pointcut("execution(* com.zcs.aop.usedemo.TestBean.test(..))")	public void test() {	}	@Pointcut("execution(* com.zcs.aop.usedemo.TestBean.subTest(..))")	public void subTest() {	}	@Before("test()")	public void beforeTest() {		System.out.println("A增强器 beforeTest");	}//	@After("test()")//	public void afterTest() {//		System.out.println("A增强器 afterTest");//	}////	@Around("test() || subTest()")//	public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {//		System.out.println("A增强器 =========beforeAroundTest==========");//		Object o = null;//		try {//			o = proceedingJoinPoint.proceed();//		} catch (Throwable throwable) {//			throwable.printStackTrace();//		}//		System.out.println("A增强器 =========afterAroundTest==========");//		return o;//	}////	/**//	 * AfterReturning 增强处理可以访问到方法的返回值,但它不能改变目标方法的返回值//	 * @param rvt//	 * @return//	 *///	@AfterReturning(returning="rvt", pointcut="execution(* com.zcs.aop.usedemo.TestBean.afterReturn(..))")//	public String afterRet(String rvt) {//		return rvt+" wl";//	}}

B:

/** * B增强器 @Around 增强 * * @author zhoucg * @date 2020-03-18 14:15 */@Aspectpublic class AspectJTestB {	@Pointcut("execution(* com.zcs.aop.usedemo.TestBean.test(..))")	public void test() {	}	@Around("test()")	public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {		System.out.println("B增强器=========beforeAroundTest==========");		Object o = null;		try {			o = proceedingJoinPoint.proceed();		} catch (Throwable throwable) {			throwable.printStackTrace();		}		System.out.println("B增强器=========afterAroundTest==========");		return o;	}}

控制A增强器的加载优先于B增强器的加载,我们可以使用xml配置的方式:

此时,我们会发现,执行的结果是先执行了A增强器的@Before的增强方法,再执行了B增强器中的@Around方法

A增强器 beforeTestB增强器=========beforeAroundTest==========TestBean内部自己执行====testB增强器=========afterAroundTest==========

对于这个问题,我在Spring的源码中并没有发现多个增强器的增强方法的收集最终同样按照这个进行排序

private static final Comparator
METHOD_COMPARATOR; static { Comparator
adviceKindComparator = new ConvertingComparator<>( new InstanceComparator<>( Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class), (Converter
) method -> { AspectJAnnotation
annotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); return (annotation != null ? annotation.getAnnotation() : null); }); Comparator
methodNameComparator = new ConvertingComparator<>(Method::getName); METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator); }

所以出现了这个问题,不知道这个算不算Spring的一个小小的特别之处,或者是一个bug的呢,我觉得应该不是BUG,可能是提醒我们对于同一个方法的增强方法最好是定义到一个类上吧

转载地址:https://blog.csdn.net/zcswl7961/article/details/104923792 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Spring事务的传播行为特性详解
下一篇:RPC服务调度架构图

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月17日 16时23分40秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

Atitit 2000drmmr v2 t05.docx Atitit 2000drmmr v1 t99 2000.1.26 hs,yweywe jla n dashw ner kl .na ni 2019-04-29
常见方案 目录 1. 发现目前 WEB 上主流的视频直播方案有 HLS 和 RTMP, 1 2. 实现直播的方法有很多,但是常用的,就这几个。 3个直播协议:rtmp、rtsp、hls。 和三个端: 2019-04-29
Atitit 直播问题总结ffmpeg 目录 1.1. 屏幕太大,可以使用-s调整分辨率 1 1.2. Full size 1 1.3. 流畅度调整 1 2. 1 2.1. 没有录音 1 2.2. 2019-04-29
paip.索引优化---sql distict—order by 法 2019-04-29
paip.输入法编程---带ord gudin去重复- 2019-04-29
paip.输入法编程---增加码表类型 2019-04-29
paip.cpu占用高解决方案---ThreadMast 跟Process Lasso的使用 2019-04-29
paip.提升性能--- mysql 建立索引 删除索引 很慢的解决. 2019-04-29
paip.输入法编程---智能动态上屏码儿长调整--.txt 2019-04-29
Atitit sumdoc t0 final index D:\BaiduNetdiskDownload\sumdoc t0 final\sumdoc t0 wps cld bek D:\Baid 2019-04-29
Atitit sumdoc t0 final index D:\BaiduNetdiskDownload\sumdoc t0 final\sumdoc t0 wps cld bek D:\Baid 2019-04-29
Atitit sumdoc t0 final index 2019-04-29
atitit 编程语言选型知识点体系.docx 编程语言选型时,你需要考虑的几个方面 目录 1. 1.2. 类型系统 5 1 2. 1.5. 语言规范 25 1 3. 1.6. 编程范式 52 2019-04-29
Atitit 编程语言语言规范总结 目录 1. 语言规范 3 2. Types 3 2.1.1. Primitive types 3 2.1.2. Compound types 4 3. State 2019-04-29
Atitit QL查询语言总结 目录 1. QL = Query Language, 是查询语言的简称 1 2. 具体实现 1 2.1. Apcl 流程控制语言 1 2.2. 脚本流程控制 2 2. 2019-04-29
Atitit 开发效率大法 v0 t025.docx Atitit 提升开发效率几大策略 目录 1. 提升效率三原则 3 1.1. 更少的代码量简化 3 1.2. 优化配置减少等待 3 1.3. 2019-04-29
Atitit mybatis的扩展使用sql udf,js java等语言 目录 1.1. 默认,mybatis使用xml,sql等语言来书写业务流程 1 2. 使用ognl调用java函数 1 3 2019-04-29
Atitit if else 选择决策流程ast对比 sql java 表达式类型 binaryExpression hase left and rit expr 目录 1.1. Sql 1 2019-04-29
Atitit 数据库存储引擎 目录 1.1. BLACKHOLE 黑洞引擎 1 1.2. Myisam innodb 1 1.3. Archive 档案类 1 1.4. Fed 连接引擎 2 1. 2019-04-29
Atitit sql注入的防范 目录 1.1. 检查数据类型 1 2. 有限操作DML 1 2.1. 限制执行函数黑名单机制 2 2.2. 限制执行系统sp 2 2.3. 限制数据查询语句类型,只能 2019-04-29