V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
persona5
V2EX  ›  程序员

控制器初始化时修改了自己以及父类控制器方法上的注解中的值,但是切面中获取时,父类方法上的注解中的值还是修改前的

  •  
  •   persona5 · 2021-05-27 16:32:56 +08:00 · 1330 次点击
    这是一个创建于 1268 天前的主题,其中的信息可能已经有所发展或是发生改变。

    需求上 MyController 中不需要重写父类中方法的逻辑,仅仅注解中 testField 的值不是默认值。

    因为不想仅仅为了指定下 testField 而去重写多个方法,所以考虑字类初始化的时候,批量修改 testField 。

    但是请求接口的时候,通过观察切面中的日志输出,子类中非重写的方法 method3 的注解对象中的 testField 是被修改为了 test2 ;而请求接口 method1 、method2 的时候,testField 还是 test1,并且切面中输出的注解对象的地址与 static 代码块中输出的不同。

    Spring 的 AOP,注解、反射工具类是 hutool 的。

    // 注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface MyAnnotation {
        String testField() default "test1";
    }
    
    // 抽象控制器
    public abstract class AbstractController{
        
        @MyAnnotation
        @RequestMapping(value = "method1", method = RequestMethod.GET)
        public RestfulResponse<Boolean> method1() {
            return new RestfulResponse<>(true);
        }
        
        @MyAnnotation
        @RequestMapping(value = "method2", method = RequestMethod.GET)
        public RestfulResponse<Boolean> method2() {
            return new RestfulResponse<>(true);
        }
    }
    
    // 继承抽象控制器
    @RestController
    public class MyController extends AbstractController {
        
        static {
            for (Method method : ReflectUtil.getMethods(MyController.class)) {
                if (AnnotationUtil.hasAnnotation(method, MyAnnotation.class)) {
                    MyAnnotation annotation = AnnotationUtil.getAnnotation(method, MyAnnotation.class);
                    AnnotationUtil.setValue(annotation, "testField", "test2");
                    // 输出 annotation 地址
                }
            }
        }
        
        @MyAnnotation
        @RequestMapping(value = "method3", method = RequestMethod.GET)
        public RestfulResponse<Boolean> method3() {
            return new RestfulResponse<>(true);
        }
    }
    
    // 切面
    @Aspect
    public class MyAspect implements Ordered {
        
        @Around("@annotation(myAnnotation)")
        public Object around(ProceedingJoinPoint pjp, MyAnnotation myAnnotation) throws Throwable {
            // 输出 myAnnotation 地址、testField 值
            return pjp.proceed();
        }
    }
    

    请问是那里出了问题,为什么切面中获取到的注解对象和 static 中的不是同一个?

    5 条回复    2021-05-28 10:15:29 +08:00
    ChovyChu
        1
    ChovyChu  
       2021-05-27 16:45:44 +08:00
    知识盲区,坐等大神。

    个人感觉可能是顺序问题导致被 default 值覆盖了?
    MoHen9
        2
    MoHen9  
       2021-05-27 18:38:22 +08:00   ❤️ 1
    不太了解 spring aop 是个什么机制,debug 了一下,发现注入的 MyAnnotation 的实例和通过反射获取的实例是两个不同对象

    Method method = ((MethodSignature) pjp.getSignature()).getMethod();
    MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);

    通过反射获取的 annotion 实例是你想要的,而注入的实例似乎好像大概差不多是在初始化时就缓存好了的。解决问题比较简单,背后的原理什么的,只有坐等大神解答一下了。
    nulIptr
        3
    nulIptr  
       2021-05-27 18:59:04 +08:00
    没想明白,什么场景下才运行时修改注解里面属性的值?这跟我理解的注解用途不太一样啊。。。
    persona5
        4
    persona5  
    OP
       2021-05-27 21:46:01 +08:00
    @MoHen9 感谢,我也测试一下看看。


    @nulIptr 原因我在开头说了,不想仅仅为了指定下 testField 而去重写多个方法。
    MyAnnotation 中 testField 的默认值是 test1 对吧,我在 MyController 中需要只用 @MyAnnotation(testField = "test2") 的注解,既需要指定 testField 为 test2 。但是接口方法内的逻辑又没有需要修改的地方,不修改注解中 testField 的值的话。MyController 中就要重写 method1 、method2 了,重写仅仅为了去加上 @MyAnnotation(testField = "test2") 这个注解。
    nulIptr
        5
    nulIptr  
       2021-05-28 10:15:29 +08:00
    @persona5 我认为你说的这种场景就应该用虚方法 override,注解就是编译期添加的额外信息,你还在运行时给改了,我觉得是在挖坑。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2788 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 13:32 · PVG 21:32 · LAX 05:32 · JFK 08:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.