为什么在自定义注解中通过@AliasFor绑定两个属性却没有效果?

问题描述

​ 今日在写项目时遇到一个问题,我需要通过Spring AOP为某些业务添加日志功能。于是编写以下自定义注解,使用自定义注解作为切点,对所有标有该注解的连接点进行增强。自定义注解如下所示:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ControllerLog {
    @AliasFor("businessName")
    String value() default "";
    @AliasFor("value")
    String businessName() default "";
}

该注解中存在两个属性,通过@AliasFor绑定,互为对方别名。

​ 业务类如下所示:

@RestController
@RequestMapping("/user")
public class UserController {
    @ControllerLog("注册新用户")
    @PostMapping("/register")
    public ResponseResult addUser() {
        // Do somethings.
    }
}

当打印日志时,需要读取注解中的属性。切面类如下所示:

@Component
@Aspect
@Slf4j
public class LogAspect {
    @Pointcut("@annotation(com.yuewatch.annotation.ControllerLog)")
    public void controllerLogPointcut() {}
    
    @Around("controllerLogPointcut()")
    public Object printControllerAccessLog(ProceedingJoinPoint joinPoint) throws Throwable{
        // Do somethings.
        Signature signature = joinPoint.getSignature();
        if (!(signature instanceof MethodSignature methodSignature)) {
            throw new IllegalArgumentException("该注解只能使用在方法上");
        }
        Method method = methodSignature.getMethod();
        ControllerLog annotation = method.getAnnotation(ControllerLog.class);
        String businessName = annotation.businessName();
        // Do somethings.
    }
}

​ 但奇怪的是,通过这种方式获取到的businessName为空。并且通过debug也可以查看到,获取到的注解对象中,value属性为所设置的"注册新用户",但businessName也为空。但是明明在自定义注解的定义中,将value属性和businessName属性通过@AliasFor注解进行了绑定,两者的值应当一样才对。这个问题困扰了我许久,经过资料搜索后大致得出了结论。

问题解决

​ 实际上@AliasFor注解是Spring提供的一个注解,其作用就是将注解中两个不同的属性进行绑定(或者说设置别名)。通过这种方式,只需对两个不同属性中的一个进行赋值,从另一个绑定属性中也可以获取值。但是需要注意的是,该注解并不是java原生支持的,需要通过Spring中提供的一些工具类进行解析。例如:org.springframework.core.annotation.AnnotationUtilsorg.springframework.core.annotation.AnnotatedElementUtils

​ 现在,我们只需对LogAspect切面类中获取注解对象的方式进行一点点更改就可以正确获取绑定属性的值。

@Component
@Aspect
@Slf4j
public class LogAspect {
    @Pointcut("@annotation(com.yuewatch.annotation.ControllerLog)")
    public void controllerLogPointcut() {}
    
    @Around("controllerLogPointcut()")
    public Object printControllerAccessLog(ProceedingJoinPoint joinPoint) throws Throwable{
        // Do somethings.
        Signature signature = joinPoint.getSignature();
        if (!(signature instanceof MethodSignature methodSignature)) {
            throw new IllegalArgumentException("该注解只能使用在方法上");
        }
        Method method = methodSignature.getMethod();
        // 错误获取的方法,无法解析绑定变量的值
        // ControllerLog annotation = method.getAnnotation(ControllerLog.class);
        
        // 修改后的正确方式,通过Spring提供的工具类来获取解析注解
        ControllerLog annotation = AnnotationUtils.getAnnotation(method, ControllerLog.class);
        String businessName = annotation.businessName();
        // Do somethings.
    }
}
文章作者: Serendipity
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 闲人亭
Spring全家桶 Spring SpringBoot
喜欢就支持一下吧