aop注解执行顺序

@ControllerAdvice里的异常处理在Aop的@AfterThrowing之后执行。

转载地址:https://www.cnblogs.com/annofyf/p/9773614.html

try{
    try{
        //@Before
        method.invoke(..);
    }finally{
        //@After
    }
    //@AfterReturning
}catch(){
    //@AfterThrowing
}

转载地址:https://blog.csdn.net/rainbow702/article/details/52185827

用过spring框架进行开发的人,多多少少会使用过它的AOP功能,都知道有@Before、@Around和@After等advice。最近,为了实现项目中的输出日志和权限控制这两个需求,我也使用到了AOP功能。我使用到了@Before、@Around这两个advice。但在,使用过程中,却对它们的执行顺序并不清楚。为了弄清楚在不同情况下,这些advice到底是以怎么样的一个顺序进行执行的,我作了个测试,在此将其记录下来,以供以后查看。

 

前提


对于AOP相关类(aspect、pointcut等)的概念,本文不作说明。
对于如何让spring框架扫描到AOP,本文也不作说明。

 


情况一: 一个方法只被一个Aspect类拦截

当一个方法只被一个Aspect拦截时,这个Aspect中的不同advice是按照怎样的顺序进行执行的呢?请看:

 

添加 PointCut类

该pointcut用来拦截test包下的所有类中的所有方法。

 

package test;

import org.aspectj.lang.annotation.Pointcut;

public class PointCuts {
    @Pointcut(value = "within(test.*)")
    public void aopDemo() {

    }
}12345678910

 

添加Aspect类

该类中的advice将会用到上面的pointcut,使用方法请看各个advice的value属性。

 

package test;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class Aspect1 {

    @Before(value = "test.PointCuts.aopDemo()")
    public void before(JoinPoint joinPoint) {
        System.out.println("[Aspect1] before advise");
    }

    @Around(value = "test.PointCuts.aopDemo()")
    public void around(ProceedingJoinPoint pjp) throws  Throwable{
        System.out.println("[Aspect1] around advise 1");
        pjp.proceed();
        System.out.println("[Aspect1] around advise2");
    }

    @AfterReturning(value = "test.PointCuts.aopDemo()")
    public void afterReturning(JoinPoint joinPoint) {
        System.out.println("[Aspect1] afterReturning advise");
    }

    @AfterThrowing(value = "test.PointCuts.aopDemo()")
    public void afterThrowing(JoinPoint joinPoint) {
        System.out.println("[Aspect1] afterThrowing advise");
    }

    @After(value = "test.PointCuts.aopDemo()")
    public void after(JoinPoint joinPoint) {
        System.out.println("[Aspect1] after advise");
    }
}1234567891011121314151617181920212223242526272829303132333435363738

 

添加测试用Controller

添加一个用于测试的controller,这个controller中只有一个方法,但是它会根据参数值的不同,会作出不同的处理:一种是正常返回一个对象,一种是抛出异常(因为我们要测试@AfterThrowing这个advice)

 

package test;

import test.exception.TestException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/aop")
public class AopTestController {

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public Result test(@RequestParam boolean throwException) {
        // case 1
        if (throwException) {
            System.out.println("throw an exception");
            throw new TestException("mock a server exception");
        }

        // case 2
        System.out.println("test OK");
        return new Result() {{
            this.setId(111);
            this.setName("mock a Result");
        }};
    }

    public static class Result {
        private int id;
        private String name;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748

 

测试 正常情况

在浏览器直接输入以下的URL,回车:

 

http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=false1

我们会看到输出的结果是:

 

[Aspect1] around advise 1
[Aspect1] before advise
test OK
[Aspect1] around advise2
[Aspect1] after advise
[Aspect1] afterReturning advise123456

 

测试 异常情况

在浏览器中直接输入以下的URL,回车:

 

http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=true1

我们会看到输出的结果是:

 

[Aspect1] around advise 1
[Aspect1] before advise
throw an exception
[Aspect1] after advise
[Aspect1] afterThrowing advise12345

 

结论

在一个方法只被一个aspect类拦截时,aspect类内部的 advice 将按照以下的顺序进行执行:

正常情况: 

 


异常情况: 

 


情况二: 同一个方法被多个Aspect类拦截

此处举例为被两个aspect类拦截。 
有些情况下,对于两个不同的aspect类,不管它们的advice使用的是同一个pointcut,还是不同的pointcut,都有可能导致同一个方法被多个aspect类拦截。那么,在这种情况下,这多个Aspect类中的advice又是按照怎样的顺序进行执行的呢?请看:

pointcut类保持不变

添加一个新的aspect类

 

package test;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class Aspect2 {

    @Before(value = "test.PointCuts.aopDemo()")
    public void before(JoinPoint joinPoint) {
        System.out.println("[Aspect2] before advise");
    }

    @Around(value = "test.PointCuts.aopDemo()")
    public void around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("[Aspect2] around advise 1");
        pjp.proceed();
        System.out.println("[Aspect2] around advise2");
    }

    @AfterReturning(value = "test.PointCuts.aopDemo()")
    public void afterReturning(JoinPoint joinPoint) {
        System.out.println("[Aspect2] afterReturning advise");
    }

    @AfterThrowing(value = "test.PointCuts.aopDemo()")
    public void afterThrowing(JoinPoint joinPoint) {
        System.out.println("[Aspect2] afterThrowing advise");
    }

    @After(value = "test.PointCuts.aopDemo()")
    public void after(JoinPoint joinPoint) {
        System.out.println("[Aspect2] after advise");
    }
}1234567891011121314151617181920212223242526272829303132333435363738

 

测试用Controller也不变

还是使用上面的那个Controller。但是现在 aspect1 和 aspect2 都会拦截该controller中的方法。

下面继续进行测试!

测试 正常情况

在浏览器直接输入以下的URL,回车:

 

http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=false1

我们会看到输出的结果是:

 

[Aspect2] around advise 1
[Aspect2] before advise
[Aspect1] around advise 1
[Aspect1] before advise
test OK
[Aspect1] around advise2
[Aspect1] after advise
[Aspect1] afterReturning advise
[Aspect2] around advise2
[Aspect2] after advise
[Aspect2] afterReturning advise1234567891011

但是这个时候,我不能下定论说 aspect2 肯定就比 aspect1 先执行。 
不信?你把服务务器重新启动一下,再试试,说不定你就会看到如下的执行结果:

 

[Aspect1] around advise 1
[Aspect1] before advise
[Aspect2] around advise 1
[Aspect2] before advise
test OK
[Aspect2] around advise2
[Aspect2] after advise
[Aspect2] afterReturning advise
[Aspect1] around advise2
[Aspect1] after advise
[Aspect1] afterReturning advise1234567891011

也就是说,这种情况下, aspect1 和 aspect2 的执行顺序是未知的。那怎么解决呢?不急,下面会给出解决方案。

 

测试 异常情况

在浏览器中直接输入以下的URL,回车:

 

http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=true1

我们会看到输出的结果是:

 

[Aspect2] around advise 1
[Aspect2] before advise
[Aspect1] around advise 1
[Aspect1] before advise
throw an exception
[Aspect1] after advise
[Aspect1] afterThrowing advise
[Aspect2] after advise
[Aspect2] afterThrowing advise123456789

同样地,如果把服务器重启,然后再测试的话,就可能会看到如下的结果:

 

[Aspect1] around advise 1
[Aspect1] before advise
[Aspect2] around advise 1
[Aspect2] before advise
throw an exception
[Aspect2] after advise
[Aspect2] afterThrowing advise
[Aspect1] after advise
[Aspect1] afterThrowing advise123456789

也就是说,同样地,异常情况下, aspect1 和 aspect2 的执行顺序也是未定的。

那么在 情况二 下,如何指定每个 aspect 的执行顺序呢? 
方法有两种:


实现org.springframework.core.Ordered接口,实现它的getOrder()方法
给aspect添加@Order注解,该注解全称为:org.springframework.core.annotation.Order


不管采用上面的哪种方法,都是值越小的 aspect 越先执行。 
比如,我们为 apsect1 和 aspect2 分别添加 @Order 注解,如下:

 

@Order(5)
@Component
@Aspect
public class Aspect1 {
    // ...
}

@Order(6)
@Component
@Aspect
public class Aspect2 {
    // ...
}12345678910111213

这样修改之后,可保证不管在任何情况下, aspect1 中的 advice 总是比 aspect2 中的 advice 先执行。如下图所示: 

 


注意点


如果在同一个 aspect 类中,针对同一个 pointcut,定义了两个相同的 advice(比如,定义了两个 @Before),那么这两个 advice 的执行顺序是无法确定的,哪怕你给这两个 advice 添加了 @Order 这个注解,也不行。这点切记。
对于@Around这个advice,不管它有没有返回值,但是必须要方法内部,调用一下 pjp.proceed();否则,Controller 中的接口将没有机会被执行,从而也导致了 @Before这个advice不会被触发。比如,我们假设正常情况下,执行顺序为”aspect2 -> apsect1 -> controller”,如果,我们把 aspect1中的@Around中的 pjp.proceed();给删掉,那么,我们看到的输出结果将是:

 


[Aspect2] around advise 1
[Aspect2] before advise
[Aspect1] around advise 1
[Aspect1] around advise2
[Aspect1] after advise
[Aspect1] afterReturning advise
[Aspect2] around advise2
[Aspect2] after advise
[Aspect2] afterReturning advise123456789

从结果可以发现, Controller 中的 接口 未被执行,aspect1 中的 @Before advice 也未被执行。

 

参考资料


Spring 4.3.2.RELEASE 官方资料:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/
其中,AOP的执行顺序章节为:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#aop-ataspectj-advice-ordering

 


Advice ordering

What happens when multiple pieces of advice all want to run at the same join point? 
Spring AOP follows the same precedence rules as AspectJ to determine the order of advice execution. 
The highest precedence advice runs first "on the way in" (so given two pieces of before advice, the one with highest precedence runs first).
"On the way out" from a join point, the highest precedence advice runs last (so given two pieces of after advice, the one with the highest precedence will run second).

When two pieces of advice defined in different aspects both need to run at the same join point,
unless you specify otherwise the order of execution is undefined. 
You can control the order of execution by specifying precedence.
This is done in the normal Spring way by either implementing the org.springframework.core.Ordered interface in the aspect class or annotating it with the Order annotation. 
Given two aspects, the aspect returning the lower value from Ordered.getValue() (or the annotation value) has the higher precedence.

When two pieces of advice defined in the same aspect both need to run at the same join point, 
the ordering is undefined (since there is no way to retrieve the declaration order via reflection for javac-compiled classes). 
Consider collapsing such advice methods into one advice method per join point in each aspect class, 
or refactor the pieces of advice into separate aspect classes - which can be ordered at the aspect level.
--------------------- 
作者:rainbow702 
来源:CSDN 
原文:https://blog.csdn.net/rainbow702/article/details/52185827 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Android中,使用AOP注解时,可以通过设置注解执行顺序来控制注解执行顺序。一般情况下,注解执行顺是按照注解声明的顺序执行的,即先执行声明在前面的注解,后执行声明在后面的注解。但是,有时候需要改变注解执行顺序,可以通过设置注解执行顺序来实现。 在Java中,可以使用@Order注解来设置注解执行顺序。在Android中,也可以使用类似的方式来设置注解执行顺序。具体来说,可以使用@IntDef注解来定义一个注解执行顺序,然后在实际使用时,通过@IntDef注解来指定注解执行顺序。例如: ``` @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @IntDef({BEFORE, AFTER}) public @interface AspectOrder { int BEFORE = 0; int AFTER = 1; int value() default BEFORE; } ``` 在上面的例子中,定义了一个@AspectOrder注解,它有两个取值:BEFORE和AFTER,分别表示注解执行顺序为前置和后置。在实际使用时,可以在需要执行注解上加上@AspectOrder注解,并指定注解执行顺序: ``` @AspectOrder(AspectOrder.BEFORE) @OnClick(R.id.btn) public void onClickBtn() { // do something } @AspectOrder(AspectOrder.AFTER) @OnClick(R.id.btn) public void onClickBtnAfter() { // do something } ``` 在上面的例子中,定义了两个@OnClick注解,分别使用了@AspectOrder注解,并指定了注解执行顺序。这样,在执行时,就可以按照指定的顺序执行注解了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值