关于注解执行优先级、同优先级情况下执行顺序

如果想要自定义注解按照想要的顺序来执行,可以通过在注解实现类里面使用@Order注解来指定优先级。
优先级逻辑是:优先级值越小,优先级越大;优先级值越大,优先级越小。
不设置优先级值。会有一个默认值:2147483647,也就是最小优先级。

注解执行顺序是:

优先级大的的前置通知先执行,然后到优先级小的的前置通知; 接着到优先级小的的后置通知,再到优先级大的的后置通知。(也就是优先级大的注解逻辑会把优先级小的注解逻辑包裹在内部)

官方说法:
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

相同优先级值的情况下:我在网上查,GPT的回复是按照注解的声明顺序执行。

代码验证

创建三个优先级相同的注解:(三个注解的定义就只有打印的内容不同,只贴其中一个)

package org.jeecg.modules.test.annotation;

import java.lang.annotation.*;

@Target(ElementType.METHOD) //作用范围
@Retention(RetentionPolicy.RUNTIME) //生效时期
public @interface Test1 {

}

package org.jeecg.modules.test.annotation;

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

/**
 * @ClassName: Test1Aspect
 * @Author: 
 * @Date: 2023/11/1 10:40
 * @Description:
 **/
@Aspect
@Order(1)
@Component
public class Test1Aspect {
    @Pointcut("@annotation(org.jeecg.modules.test.annotation.Test1)")
	public void testPoint(){

    }
    @Before("testPoint()")
    public void around() throws Throwable {
	    System.out.println("测试1的注解执行前置通知");
    }
	@After("testPoint()")
	public void after() throws Throwable {
		System.out.println("测试1的注解执行后置通知");
	}
	@Around("testPoint()")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("测试1的注解执行环绕通知前置通知");
		Object proceed = joinPoint.proceed();
		System.out.println("测试1的注解执行环绕通知后置通知");
		return proceed;
	}
}


package org.jeecg.modules.test;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.test.annotation.Test1;
import org.jeecg.modules.test.annotation.Test2;
import org.jeecg.modules.test.annotation.Test3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName: TestController
 * @Author: 
 * @Date: 2023/8/21 14:28
 * @Description:
 **/
@Slf4j
@RestController
@Api("测试")
public class TestController {

	@RequestMapping("/test")
	@ApiOperation("测试")
	@Test1()
	@Test2()
	@Test3()
	public Object test(String a){
		System.out.println("进入测试方法了");
		return "ok";
	}
}

运行结果(测了很多组,结果均如下)

在这里插入图片描述

更改注解顺序
package org.jeecg.modules.test;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.test.annotation.Test1;
import org.jeecg.modules.test.annotation.Test2;
import org.jeecg.modules.test.annotation.Test3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName: TestController
 * @Author:
 * @Date: 2023/8/21 14:28
 * @Description:
 **/
@Slf4j
@RestController
@Api("测试")
public class TestController {
	@Autowired
	private AsyncService asyncService;
	@Autowired
	private TestService testService;
	@Autowired
	private RedisTemplate redisTemplate;

	@RequestMapping("/test")
	@ApiOperation("测试")
	@Test3()
	@Test2()
	@Test1()
	public Object test(String a){
		System.out.println("进入测试方法了");
		return "ok";
	}
}

运行结果(测了很多组,结果都是如下)

在这里插入图片描述

结论

两次验证,优先级均相同,颠倒了注解的先后顺序,但注解的执行顺序并没有改变。说明优先级相同情况下,注解的执行并不是按照写注解的先后顺序来的。而是按照注解的名字字符的顺序来的。字符对应的编码小,相当于优先级值小,优先级大,先执行。(感谢评论区老哥指正)
整体执行顺序:

  1. order值小的环绕通知joinPoint.proceed();前面部分
  2. order值小的前置通知
  3. order值大的环绕通知joinPoint.proceed();前面部分
  4. order值大的前置通知
  5. order值大的后置置通知
  6. order值大的环绕通知joinPoint.proceed();后面面部分
  7. order值小的后置置通知
  8. order值小的环绕通知joinPoint.proceed();后面面部分

如果order值相同则按照注解的字符顺序来执行,字符编码小的先执行。比如A的编码就比B小,相当于A的优先级值小于B,优先级大,先执行。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring AOP中,通知的执行顺序是根据AspectJ的优先级规则来确定的。具体来说,有两种情况需要考虑:同一切面中的通知执行顺序和不同切面中的通知执行顺序。在同一切面中,通知的执行顺序是按照它们在代码中的声明顺序来执行的。例如,如果在同一切面中定义了两个前置通知,那么它们将按照它们在代码中的声明顺序依次执行。\[1\] 在不同切面中,通知的执行顺序是根据切面的优先级来确定的。切面的优先级可以通过实现Ordered接口或使用@Order注解来指定。较小的值表示较高的优先级。如果没有指定优先级,默认情况下,切面的优先级是无序的,即执行顺序是不确定的。如果确实需要指定通知的执行顺序,可以将通知重构到不同的切面中,并通过指定切面的优先级来确定执行顺序。\[2\]\[3\] 总结起来,同一切面中的通知执行顺序是按照它们在代码中的声明顺序来执行的,而不同切面中的通知执行顺序是根据切面的优先级来确定的。 #### 引用[.reference_title] - *1* *3* [【第六章】 AOP 之 6.7 通知顺序 ——跟我学spring3 ](https://blog.csdn.net/jinnianshilongnian/article/details/84158583)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Spring Aop的使用和通知注解执行顺序](https://blog.csdn.net/JemeryShen/article/details/124767665)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值