文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring AOP中多切面运行顺序是什么

2023-06-29 04:40

关注

这篇文章将为大家详细讲解有关Spring AOP中多切面运行顺序是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

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返回。

图:

Spring AOP中多切面运行顺序是什么

这里的validate切面就是图中的VaAspect;

对啦,可以更改默认的切面顺序,要在将要更改的切面类上加入@order(int value)注解,value默认值很大,超级大,越大执行的优先级越低,所以如果把它调成1就是先执行这个切面的通知。

AOP的应用场景

回忆基于注解的AOC配置

不使用注解实现AOP配置。

切面类

public class LogUtil {    public void performance(){}    public void logStart(JoinPoint joinPoint)    {        //获取方法上的参数列表        Object[] args = joinPoint.getArgs();        //获取方法签名        Signature signature = joinPoint.getSignature();        String name = signature.getName();//获取方法名        System.out.println("前置通知:"+name+" 方法开始执行了....参数是:"+ Arrays.asList(args) +"");    }    public void logReturn(JoinPoint point,Object result)    {        String name = point.getSignature().getName();        Object[] args = point.getArgs();        System.out.println("返回通知: "+name+"方法正常执行,返回结果是:"+result+"");    }    public void logException(JoinPoint point,Exception e)    {        String name = point.getSignature().getName();        System.out.println("异常通知:"+name+" 方法出现了异常,异常是 "+e+"...");    }    public void logEnd(JoinPoint joinPoint)    {        String name = joinPoint.getSignature().getName();        System.out.println("后置通知:"+name+"方法结束了");    }    //环绕通知    public Object myAround(ProceedingJoinPoint proceedingJoinPoint){        Object proceed = null;            //获取方法名        String name = 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 (Throwable throwable) {            System.out.println("环绕异常通知:异常是"+throwable+"");            throwable.printStackTrace();        }finally {            System.out.println("环绕后置通知:"+name+"方法结束了");        }        return proceed;    }

被切入的类(这里是一个计算器类)

package main.java.cn.zixue.domain;public class MyCalculator{     public int add(int a,int b)    {         return a+b;    }     public int sub(int a,int b)  {         return a-b;     }     public int mul(int a,int b) {         return a*b;     }      public int dev(int a,int b)  {          return a/b;      }      public double add(double a,float b,int c)   {          return a+b+c;       } }

配置文件

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">    <context:component-scan base-package="main.java.cn"></context:component-scan>    <bean id="myCalculator" class="main.java.cn.zixue.domain.MyCalculator"></bean>    <bean id="logUtil" class="main.java.cn.zixue.utils.LogUtil"></bean>    <!--AOP名称空间-->    <aop:config>      <!--  制定切面的方法-->        <aop:pointcut id="performance" expression="execution(public * main.java.cn.zixue.domain.MyCalculator.*(..))"></aop:pointcut>        <!--指定切面-->        <aop:aspect ref="logUtil">            <aop:after method="logEnd" pointcut-ref="performance"></aop:after>            <aop:before method="logStart" pointcut-ref="performance"></aop:before>            <aop:after-returning method="logReturn" pointcut-ref="performance" returning="result"></aop:after-returning>            <aop:after-throwing method="logException" pointcut-ref="performance" throwing="e"></aop:after-throwing>            <aop:around method="myAround" pointcut-ref="performance"></aop:around>        </aop:aspect>    </aop:config></beans>

测试结果

@Testpublic void Test02(){    MyCalculator myCalculator = (MyCalculator) context.getBean("myCalculator");    myCalculator.add(1,10);    System.out.println("========================");}

前置通知:add 方法开始执行了&hellip;参数是:[1, 10]
环绕前置通知:add方法开始执行了,参数是[1, 10]
环绕返回通知:add方法返回结果是11;
环绕后置通知:add方法结束了
返回通知: add方法正常执行,返回结果是:11
后置通知:add方法结束了
====================**

普通前置通知->环绕通知->环绕返回->环绕后置->普通返回->普通后置

注解和配置文件在什么时候使用?该如何选择?

注解的优点:配置快速简洁。

配置文件的优点:功能丰富,注解有的他都可以实现,注解没有的他也有。

当遇到重要的切面时,用配置文件写,例如权限验证及管理。对于常用的普通的切面就用注解。

Spring AOP切面执行顺序和常见问题

切面注解的执行顺序

public Object aop(Method method,Object object) {    try {        try {                        doBefore();            method.invoke(object);                    } finally {            doAfter();        }        doAfterReturning();    } catch (Exception e) {        doAfterThrowing();    }}

切面间的执行顺序

切面之间使用older注解,区分调用顺序,Order值越小,那么切面越先执行(越后结束).

Spring AOP中多切面运行顺序是什么

不指定Order,那么Order是默认值->Integer.MAX_VALUE. 如果Order相同,则是按照切面字母的顺序来执行切面.比如@Transactional和@Cacheable->对应的切面是TransactionInterceptor和CacheInterceptor,则先执行@Cacheable的切面

@Transactional也是通过切面实现,Order值是Integer.MAX_VALUE。(如果在service方法上同时添加带older的日志注解,在日志切面after里面报错,不会回滚事务)

常见问题示例

方法A调用同类中的方法B,方法B上的切面不会生效

问题示例:

@Component public class StrategyService extends BaseStrategyService  {     public PricingResponse getFactor(Map<String, String> pricingParams) {         // 做一些参数校验,以及异常捕获相关的事情         return this.loadFactor(tieredPricingParams);     }     @Override     @StrategyCache(keyName = "key0001", expireTime = 60 * 60 * 2)     private PricingResponse loadFactor(Map<String, String> pricingParams) {         //代码执行     } }

原因:Spring的AOP是通过代理对象调用,只有这种调用方式,才能够在真正的对象的执行前后,能够让代理对象也执行相关代码,才能起到切面的作用。而对于上面使用this的方式调用,这种只是自调用,并不会使用代理对象进行调用,也就无法执行切面类。

 public static void main(String[] args) {         ProxyFactory factory = new ProxyFactory(new SimplePojo());         factory.addInterface(Pojo.class);         factory.addAdvice(new RetryAdvice());         Pojo pojo = (Pojo) factory.getProxy();         // this is a method call on the proxy!         pojo.foo();     }

解决方法:使用AopContext.currentProxy()获取到代理对象,然后通过代理对象调用对应的方法。还有个地方需要注意,以上方式还需要将Aspect的expose-proxy设置成true。

@Component public class StrategyService{     public PricingResponse getFactor(Map<String, String> pricingParams) {         // 做一些参数校验,以及异常捕获相关的事情         // 这里不使用this.loadFactor而是使用AopContext.currentProxy()调用,目的是解决AOP代理不支持方法自调用的问题         if (AopContext.currentProxy() instanceof StrategyService) {             return ((StrategyService)AopContext.currentProxy()).loadFactor(tieredPricingParams);         } else {             // 部分实现没有被代理过,则直接进行自调用即可             return loadFactor(tieredPricingParams);         }     }     @Override     @StrategyCache(keyName = "key0001", expireTime = 60 * 60 * 2)     private PricingResponse loadFactor(Map<String, String> oricingParams) {         //代码执行     } } //还有个地方需要注意,以上方式还需要将Aspect的expose-proxy设置成true。如果是配置文件修改: <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>//如果是SpringBoot,则修改应用启动入口类的注解:@EnableAspectJAutoProxy(exposeProxy = true) public class Application { }

关于“Spring AOP中多切面运行顺序是什么”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯