SpringCloud基础篇AOP之拦截优先级详解

相关文章可以查看: http://spring.hhui.top

前面两篇分别介绍了AOP的基本使用姿势和一些高级特性,当时还遗留了一个问题没有说明,即不同的advice,拦截同一个目标方法时,优先级是怎样的,本篇博文将进行详细分析

  • 同一个切面中,不同类型的advice的优先级
  • 同一个切面中,同一种类型的advice优先级
  • 不同切面中,同一类型的advice优先级
  • 不同切面中,不同类型的advice优先级

<!-- more -->

I. 统一切面,不同类型ddvice优先级

在不分析源码的前提下,也只能通过实际的case来看优先级问题了,我们现在设计一下使用实例,通过输出结果来看对应的优先级

1. case设计

首先创建被拦截的bean: com.git.hui.boot.aop.order.InnerDemoBean

@Component
public class InnerDemoBean {

    public String print() {
        try {
            System.out.println("in innerDemoBean start!");
            String rans = System.currentTimeMillis() + "|" + UUID.randomUUID();
            System.out.println(rans);
            return rans;
        } finally {
            System.out.println("in innerDemoBean over!");
        }
    }
}

接下来写一个切面,里面定义我们常见的各种advice

对于aop的使用,有疑问的可以参考: 190301-SpringBoot基础篇AOP之基本使用姿势小结

@Component
@Aspect
public class OrderAspect {

    @Pointcut("execution(public * com.git.hui.boot.aop.order.*.*())")
    public void point() {
    }

    @Before(value = "point()")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("do before!");
    }

    @After(value = "point()")
    public void doAfter(JoinPoint joinPoint) {
        System.out.println("do after!");
    }

    @AfterReturning(value = "point()", returning = "ans")
    public void doAfterReturning(JoinPoint joinPoint, String ans) {
        System.out.println("do after return: " + ans);
    }

    @Around("point()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            System.out.println("do in around before");
            return joinPoint.proceed();
        } finally {
            System.out.println("do in around over!");
        }
    }
}

2. 测试

使用SpringBoot的项目进行测试aop,使用还是比较简单的

@SpringBootApplication
public class Application {
    private InnerDemoBean innerDemoBean;

    public Application(InnerDemoBean innerDemoBean) {
        this.innerDemoBean = innerDemoBean;
        this.innerDemoBean();
    }

    private void innerDemoBean() {
        System.out.println("result: " + innerDemoBean.print());
    }
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

看下上面执行的输出结果

do in around before
do before!
in innerDemoBean start!
1552219604035|e9a31f44-6a31-4485-806a-834361842ce1
in innerDemoBean over!
do in around over!
do after!
do after return: 1552219604035|e9a31f44-6a31-4485-806a-834361842ce1
result: 1552219604035|e9a31f44-6a31-4485-806a-834361842ce1

从输出结果进行反推,我们可以知道统一切面中,advice执行的先后顺序如下

II. 同一切面,同一类型切面

正常来讲,拦截一个方法时,统一类型的切面逻辑都会写在一起,那这个case有什么分析的必要呢?

在我们实际的使用中,同一类型的advice拦截同一个方法的可能性还是很高的,why? 因为多个advice有自己定义的拦截规则,它们之间并不相同,但可能存在交集,比如我们在上面的切面中,再加一个拦截注解的before advice

1. case设计

依然是上面的InnerDemoBean,方法上加一个自定义注解

@AnoDot
public String print() {
    try {
        System.out.println("in innerDemoBean start!");
        String rans = System.currentTimeMillis() + "|" + UUID.randomUUID();
        System.out.println(rans);
        return rans;
    } finally {
        System.out.println("in innerDemoBean over!");
    }
}

然后加一个拦截注解的advice

@Before("@annotation(AnoDot)")
public void doAnoBefore(JoinPoint joinPoint) {
    System.out.println("dp AnoBefore");
}

2. 测试

再次执行前面的case,然后看下输出结果如下

In NetAspect doAround before!
do in around before
dp AnoBefore
do before!
in innerDemoBean start!
1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0
in innerDemoBean over!
do in around over!
do after!
do after return: 1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0
In NetAspect doAround over! ans: 1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0
result: 1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0

我们主要看下两个before,发现 AnoBefore 在前面; 因此这里的一个猜测,顺序就是根据方法命名的顺序来的,比如我们再加一个 doXBefore,然后我们预估输出结果应该是

do AnoBefore > doBefore > doXBefore

额外添加一个

@Before("@annotation(AnoDot)")
public void doXBefore(JoinPoint joinPoint) {
    System.out.println("dp XBefore");
}

接着就是输出结果如下,和我们预期一致

3. Order注解尝试

我们知道有个Order注解可以来定义一些优先级,那么把这个注解放在advice方法上,有效么?实际尝试一下

@Order(1)
@Before(value = "point()")
public void doBefore(JoinPoint joinPoint) {
    System.out.println("do before!");
}

@Order(2)
@Before("@annotation(AnoDot)")
public void doAnoBefore(JoinPoint joinPoint) {
    System.out.println("dp AnoBefore");
}

@Order(3)
@Before("@annotation(AnoDot)")
public void doXBefore(JoinPoint joinPoint) {
    System.out.println("dp XBefore");
}

如果注解有效,我们预期输出结果如下

do Before > do AnoBefore > do XBefore

然后再次执行,看下输出结果是否和我们预期一样

4. 小结

同一个切面中,相同的类型的advice,优先级是根据方法命名来的,加@Order注解是没有什么鸟用的,目前也没有搜索到可以调整优先级的方式

III. 不同切面,相同类型的advice

如果说上面这种case不太好理解为啥会出现的话,那么这个可能就容易理解多了;毕竟一个切面完成一件事情,出现相同的advice就比较常见了;

比如spring mvc中,我们通常会实现的几个切面

  • 一个before advice的切面,实现输出请求日志
  • 一个before advice的切面,实现安全校验(这种其实更常见的是放在filter/intercept中)

1. case设计

现在就需要再加一个切面,依然以before advice作为case

@Aspect
@Component
public class AnotherOrderAspect {
    @Before("@annotation(AnoDot)")
    public void doBefore() {
        System.out.println("in AnotherOrderAspect before!");
    }
}

2. 测试

接下来看测试输出结果如下图

发现了一个有意思的事情了,AnotherOrderAspect切面的输出,完全在OrderAspect切面中所有的advice之前,接着我们再次尝试使用@Order注解来试试,看下会怎样

@Order(0)
@Component
@Aspect
public class OrderAspect {
}

@Aspect
@Order(10)
@Component
public class AnotherOrderAspect {
}

如果顺序有关,我们预期的输出结果应该是

do AnoBefore > do Before > doXBefore > do AnotherOrderAspect before!

实际测试输出如下,和我们预期一致

3. 小结

从上面的测试来看,不同的切面,默认顺序实际上是根据切面的命令来的;

  • A切面中的advice会优先B切面中同类型的advice
  • 我们可以通过 Order 注解来解决不同切面的优先级问题,依然是值越小,优先级越高

IV. 不同切面,不同advice顺序

其实前面的case已经可以说明这个问题了,现在稍稍丰富一下AnotherOrderAspect,看下结果

1. case设计

@Aspect
@Order(10)
@Component
public class AnotherOrderAspect {

    @Before("@annotation(AnoDot)")
    public void doBefore() {
        System.out.println("in AnotherOrderAspect before!");
    }

    @After("@annotation(AnoDot)")
    public void doAfter(JoinPoint joinPoint) {
        System.out.println("do AnotherOrderAspect after!");
    }

    @AfterReturning(value = "@annotation(AnoDot)", returning = "ans")
    public void doAfterReturning(JoinPoint joinPoint, String ans) {
        System.out.println("do AnotherOrderAspect after return: " + ans);
    }

    @Around("@annotation(AnoDot)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            System.out.println("do AnotherOrderAspect in around before");
            return joinPoint.proceed();
        } finally {
            System.out.println("do AnotherOrderAspect in around over!");
        }
    }
}

2. 测试

看下执行后的输出结果

假设A切面优先级高于B切面,那么我们执行先后顺序如下

V. 小结

本篇内容有点多,针对前面的测试以及结果分析,给出一个小结,方便直接获取最终的答案

1. 不同advice之间的优先级顺序

around 方法执行前代码  >  before > 方法执行 > around方法执行后代码 > after > afterReturning/@AfterThrowing

2. 统一切面中相同advice

统一切面中,同类型的advice的优先级根据方法名决定,暂未找到可以控制优先级的使用方式

3. 不同切面优先级

不同切面优先级,推荐使用 @Order注解来指定,数字越低,优先级越高

4. 不同切面advice执行顺序

优先级高的切面中的advice执行顺序会呈现包围优先级低的advice的情况,更直观的先后顺序,推荐看第四节的顺序图,更加清晰明了

VI. 其他

0. 项目

1. 一灰灰Blog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java那些事

跳槽的必要条件是有一份好的简历

写简历首先要有一个好的模板,我们做技术的不同于 UX,UED,我们不需要那么花哨,但是也需要整洁干净。好的模板能让你的简历给面试官一个好的印象,有一句话说得好,...

12440
来自专栏Java技术栈

Spring Boot 注册 Servlet 的三种方法,真是太有用了!

本文栈长教你如何在 Spring Boot 注册 Servlet、Filter、Listener。

14350
来自专栏JavaQ

深入理解Spring系列之十五:@Async实现原理

对于异步方法调用,从Spring3开始提供了@Async注解,该注解可以被标注在方法上,以便异步地调用该方法。调用者将在调用时立即返回,方法的实际执行将提交给S...

1.6K60
来自专栏纯洁的微笑

Spring Boot(六):如何优雅的使用 Mybatis

这两天启动了一个新项目因为项目组成员一直都使用的是 Mybatis,虽然个人比较喜欢 Jpa 这种极简的模式,但是为了项目保持统一性技术选型还是定了 Mybat...

11430
来自专栏IT云清

SpringCloud教程-03:负载均衡(Ribbon)

摘要:本文主要讲解,在SpringCloud体系的微服务架构中,如何使用Ribbon来实现客户端的负载均衡。

11630
来自专栏美码师

一份 SpringBoot 学习清单

12540
来自专栏SpringCloud专栏

Springboot集成sentinel实现接口限流入门

版权声明:本文为博主原创文章,转载请注明地址http://blog.csdn.net/tianyaleixiaowu。 https://blog.csd...

2.3K50
来自专栏java思维导图

为什么大部分码农做不了软件架构师?

小团队一般 10 人左右,其中常常是技术最牛的人做架构师(或TL)。所以,架构师在广大码农中的占比大概平均不到 10%。而架构师也可以分为初级、中级、高级三档,...

14660
来自专栏芋道源码1024

为什么大部分码农做不了软件架构师?

小团队一般 10 人左右,其中常常是技术最牛的人做架构师(或TL)。所以,架构师在广大码农中的占比大概平均不到 10%。而架构师也可以分为初级、中级、高级三档,...

14930
来自专栏用户1337634的专栏

Java Bean Validation分组校验

81220

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励