同一个方法上多个自定义注解的执行顺序问题分析

需求背景

在社交化电商项目中,除了需要对用户提交的敏感词进行拦截,也需要对黑名单用户进行拦截。因此,项目通过自定义注解的方式实现了两个注解@TextBlock和@BlackBlock,并实现了对应的拦截处理。

当笔者将两个注解注释在同一个方法(比如:用户发布帖子)上时,突然意识到一个问题:这两个注解的处理逻辑谁先谁后?笔者项目中的业务逻辑:黑名单拦截必须在敏感词拦截之前。

问题分析

针对上述问题,有两种观点: (1)按照注解标注的顺序先后执行对应的拦截逻辑; (2)按照注解名称的大小(以字符串形式进行比较),小的先执行,大的后执行;

接下来我们一起来看实践一下,实践出真知。

先定义两个注解和对应的切面类

less 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface A {
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface B {
}

@Slf4j
@Aspect
@Component
public class AAspect {
    @SneakyThrows
    @Around("@annotation(a)")
    public Object around(ProceedingJoinPoint point, A a) {
        log.info("注解A拦截处理");
        return point.proceed();
    }
}

@Slf4j
@Aspect
@Component
public class BAspect {
    @SneakyThrows
    @Around("@annotation(b)")
    public Object around(ProceedingJoinPoint point, B b) {
        log.info("注解B拦截处理");
        return point.proceed();
    }
}

再定义一个Service类来使用这些注解,代码如下:

less 复制代码
@Component
@Slf4j
public class AbService {

	@B
	@A
	public void ab() {
		log.info("======ab======");
	}
}

请注意:上述代码中@B标注在@A之前,执行结果如下:

css 复制代码
注解A拦截处理
注解B拦截处理
======ab======

由此,我们可以断定第1个观点时错误的。

接下来,我们将@A和@B交换注释位置之后,执行结果仍然不变。

css 复制代码
注解A拦截处理
注解B拦截处理
======ab======

由此,我们可以推出第2个观点是正确的。

那如果想让@B先执行呢?就如电商中实际的需求,黑名单拦截必须先于敏感词拦截执行。

在注解对应的Aspect类上使用@Order注解指定执行顺序,指定值越小越先执行。如未指定,默认值为Integer.MAX_VALUE,即2147483647。

less 复制代码
@Slf4j
@Aspect
@Component
@Order(10000)
public class BAspect {
    @SneakyThrows
    @Around("@annotation(b)")
    public Object around(ProceedingJoinPoint point, B b) {
        log.info("注解B拦截处理");
        return point.proceed();
    }
}

在BAspect类上加上注解@Order(10000),由于10000小于AAspect默认的顺序Integer.MAX_VALUE,因此该逻辑会先执行。执行结果如下:

css 复制代码
注解B拦截处理
注解A拦截处理
======ab======

问题结论

  • 同一个方法上多个自定义注解,如果对应的切面类顺序相同,则注解名称小的先执行。默认情况注解A的逻辑优先注解B的逻辑;
  • 如果通过@Order指定了切面类顺序,则顺序值越小越先执行。

参考文档

  • Java项目中实现敏感词过滤功能\]([Java项目中实现敏感词过滤功能 - 掘金 (juejin.cn)](https://juejin.cn/post/7357382699630690341 "https://juejin.cn/post/7357382699630690341"))

相关推荐
minh_coo8 小时前
Spring框架事件驱动架构核心注解之@EventListener
java·后端·spring·架构·intellij-idea
sanggou8 小时前
License 集成 Spring Gateway:解决 WebFlux 非阻塞与 Spring MVC Servlet 阻塞兼容问题
spring·gateway·mvc
再睡亿分钟!10 小时前
Spring MVC 的常用注解
java·开发语言·spring boot·spring
霸道流氓气质13 小时前
Java开发中常用CollectionUtils方式,以及Spring中CollectionUtils常用方法示例
java·spring
optimistic_chen16 小时前
【Java EE进阶 --- SpringBoot】Spring DI详解
spring boot·笔记·后端·spring·java-ee·mvc·di
麦兜*18 小时前
MongoDB 6.0 新特性解读:时间序列集合与加密查询
数据库·spring boot·mongodb·spring·spring cloud·系统架构
Chan1618 小时前
【智能协同云图库】基于统一接口架构构建多维度分析功能、结合 ECharts 可视化与权限校验实现用户 / 管理员图库统计、通过 SQL 优化与流式处理提升数据
java·spring boot·后端·sql·spring·intellij-idea·echarts
ponnylv20 小时前
深入剖析Spring Boot自动配置原理
spring boot·spring
金色天际线-1 天前
Nginx 优化与防盗链配置指南
java·后端·spring
cyforkk1 天前
Spring 异常处理器:从混乱到有序,优雅处理所有异常
java·后端·spring·mvc