Spring AOP中多切面运行顺序是什么
这篇文章将为大家详细讲解有关SpringAOP中多切面运行顺序是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
Spring AOP多切面运行顺序多切面运行顺序当一个方法的执行被多个切面共同切的时候,环绕通知只影响当前切面的通知顺序,例如创建两个切面logUtil,validateUtil两个切面共同监视计算器类的加法运算,add(int a,int b);测试中,看切面工具类的名称首字母,默认情况下a-z执行顺序,所以这个时候logUtil切面通知比validateUtil先执行通知;
所以顺序是:L的前置通知 -->v的前置通知–>执行add方法,然后v的后置通知–>V的后置返回–>L的后置通知–>L的后置返回。
但是当logUtil中加入了环绕通知,所以环绕通知要比logUtil的普通通知先执行,环绕通知功能很强大,在通过反射执行方法的前面我们可以更改这个方法的参数,但是普通通知不能这么做。虽然在logUtil加了环绕通知,但是这个环绕通知只是比logUtil的普通通知先执行无论是进入切面前还是出切面时,他并不影响validateUtil这个切面的普通通知的执行顺序,所以加了环绕通知执行顺序是
环绕前置–> log前置–>va前置–>目标方法–>va后置–>va返回–>环绕返回通知–>环绕后置–>log后置–>log返回。
图:
这里的validate切面就是图中的VaAspect;
对啦,可以更改默认的切面顺序,要在将要更改的切面类上加入@order(int value)注解,value默认值很大,超级大,越大执行的优先级越低,所以如果把它调成1就是先执行这个切面的通知。
AOP的应用场景aop可以进行日志记录;
aop可以做权限验证
AOP可以做安全检查
AOP可以做事务控制
回忆基于注解的AOC配置
将目标类和切面类都加入到IOC容器中。@Component
告诉Spring哪个是切面类@Aspect
在切面类中使用五个通知注解来配置切面中的这些方法都什么时候在那运行
开启注解的aop功能。
不使用注解实现AOP配置。
1.切面类
publicclassLogUtil{publicvoidperformance(){}publicvoidlogStart(JoinPointjoinPoint){//获取方法上的参数列表Object[]args=joinPoint.getArgs();//获取方法签名Signaturesignature=joinPoint.getSignature();Stringname=signature.getName();//获取方法名System.out.println("前置通知:"+name+"方法开始执行了....参数是:"+Arrays.asList(args)+"");}publicvoidlogReturn(JoinPointpoint,Objectresult){Stringname=point.getSignature().getName();Object[]args=point.getArgs();System.out.println("返回通知:"+name+"方法正常执行,返回结果是:"+result+"");}publicvoidlogException(JoinPointpoint,Exceptione){Stringname=point.getSignature().getName();System.out.println("异常通知:"+name+"方法出现了异常,异常是"+e+"...");}publicvoidlogEnd(JoinPointjoinPoint){Stringname=joinPoint.getSignature().getName();System.out.println("后置通知:"+name+"方法结束了");}//环绕通知publicObjectmyAround(ProceedingJoinPointproceedingJoinPoint){Objectproceed=null;//获取方法名Stringname=proceedingJoinPoint.getSignature().getName();//获取执行方法的参数列表Object[]args=proceedingJoinPoint.getArgs();try{System.out.println("环绕前置通知:"+name+"方法开始执行了,参数是"+Arrays.asList(args)+"");//等于method.invoke();通过反射执行指定方法proceed=proceedingJoinPoint.proceed();System.out.println("环绕返回通知:"+name+"方法返回结果是"+proceed+";");}catch(Throwablethrowable){System.out.println("环绕异常通知:异常是"+throwable+"");throwable.printStackTrace();}finally{System.out.println("环绕后置通知:"+name+"方法结束了");}returnproceed;}
2.被切入的类(这里是一个计算器类)
packagemain.java.cn.zixue.domain;publicclassMyCalculator{publicintadd(inta,intb){returna+b;}publicintsub(inta,intb){returna-b;}publicintmul(inta,intb){returna*b;}publicintdev(inta,intb){returna/b;}publicdoubleadd(doublea,floatb,intc){returna+b+c;}}
3.配置文件
<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><context:component-scanbase-package="main.java.cn"></context:component-scan><beanid="myCalculator"class="main.java.cn.zixue.domain.MyCalculator"></bean><beanid="logUtil"class="main.java.cn.zixue.utils.LogUtil"></bean><!--AOP名称空间--><aop:config><!--制定切面的方法--><aop:pointcutid="performance"expression="execution(public*main.java.cn.zixue.domain.MyCalculator.*(..))"></aop:pointcut><!--指定切面--><aop:aspectref="logUtil"><aop:aftermethod="logEnd"pointcut-ref="performance"></aop:after><aop:beforemethod="logStart"pointcut-ref="performance"></aop:before><aop:after-returningmethod="logReturn"pointcut-ref="performance"returning="result"></aop:after-returning><aop:after-throwingmethod="logException"pointcut-ref="performance"throwing="e"></aop:after-throwing><aop:aroundmethod="myAround"pointcut-ref="performance"></aop:around></aop:aspect></aop:config></beans>
4.测试结果
@TestpublicvoidTest02(){MyCalculatormyCalculator=(MyCalculator)context.getBean("myCalculator");myCalculator.add(1,10);System.out.println("========================");}
前置通知:add 方法开始执行了…参数是:[1, 10]
环绕前置通知:add方法开始执行了,参数是[1, 10]
环绕返回通知:add方法返回结果是11;
环绕后置通知:add方法结束了
返回通知: add方法正常执行,返回结果是:11
后置通知:add方法结束了
====================**
普通前置通知->环绕通知->环绕返回->环绕后置->普通返回->普通后置
注解和配置文件在什么时候使用?该如何选择?
注解的优点:配置快速简洁。
配置文件的优点:功能丰富,注解有的他都可以实现,注解没有的他也有。
当遇到重要的切面时,用配置文件写,例如权限验证及管理。对于常用的普通的切面就用注解。
Spring AOP切面执行顺序和常见问题切面注解的执行顺序publicObjectaop(Methodmethod,Objectobject){try{try{/*doAroundstart*/doBefore();method.invoke(object);/*doAroundend*/}finally{doAfter();}doAfterReturning();}catch(Exceptione){doAfterThrowing();}}切面间的执行顺序
切面之间使用older注解,区分调用顺序,Order值越小,那么切面越先执行(越后结束).
不指定Order,那么Order是默认值->Integer.MAX_VALUE. 如果Order相同,则是按照切面字母的顺序来执行切面.比如@Transactional和@Cacheable->对应的切面是TransactionInterceptor和CacheInterceptor,则先执行@Cacheable的切面
@Transactional也是通过切面实现,Order值是Integer.MAX_VALUE。(如果在service方法上同时添加带older的日志注解,在日志切面after里面报错,不会回滚事务)
常见问题示例1.方法A调用同类中的方法B,方法B上的切面不会生效
问题示例:
@ComponentpublicclassStrategyServiceextendsBaseStrategyService{publicPricingResponsegetFactor(Map<String,String>pricingParams){//做一些参数校验,以及异常捕获相关的事情returnthis.loadFactor(tieredPricingParams);}@Override@StrategyCache(keyName="key0001",expireTime=60*60*2)privatePricingResponseloadFactor(Map<String,String>pricingParams){//代码执行}}
原因:Spring的AOP是通过代理对象调用,只有这种调用方式,才能够在真正的对象的执行前后,能够让代理对象也执行相关代码,才能起到切面的作用。而对于上面使用this的方式调用,这种只是自调用,并不会使用代理对象进行调用,也就无法执行切面类。
publicstaticvoidmain(String[]args){ProxyFactoryfactory=newProxyFactory(newSimplePojo());factory.addInterface(Pojo.class);factory.addAdvice(newRetryAdvice());Pojopojo=(Pojo)factory.getProxy();//thisisamethodcallontheproxy!pojo.foo();}
解决方法:使用AopContext.currentProxy()获取到代理对象,然后通过代理对象调用对应的方法。还有个地方需要注意,以上方式还需要将Aspect的expose-proxy设置成true。
@ComponentpublicclassStrategyService{publicPricingResponsegetFactor(Map<String,String>pricingParams){//做一些参数校验,以及异常捕获相关的事情//这里不使用this.loadFactor而是使用AopContext.currentProxy()调用,目的是解决AOP代理不支持方法自调用的问题if(AopContext.currentProxy()instanceofStrategyService){return((StrategyService)AopContext.currentProxy()).loadFactor(tieredPricingParams);}else{//部分实现没有被代理过,则直接进行自调用即可returnloadFactor(tieredPricingParams);}}@Override@StrategyCache(keyName="key0001",expireTime=60*60*2)privatePricingResponseloadFactor(Map<String,String>oricingParams){//代码执行}}//还有个地方需要注意,以上方式还需要将Aspect的expose-proxy设置成true。如果是配置文件修改:<aop:aspectj-autoproxyproxy-target-class="true"expose-proxy="true"/>//如果是SpringBoot,则修改应用启动入口类的注解:@EnableAspectJAutoProxy(exposeProxy=true)publicclassApplication{}
关于“SpringAOP中多切面运行顺序是什么”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。