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

2021-05-27 16:32:56 +08:00
 persona5

需求上 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 中的不是同一个?

1193 次点击
所在节点    程序员
5 条回复
ChovyChu
2021-05-27 16:45:44 +08:00
知识盲区,坐等大神。

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

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

通过反射获取的 annotion 实例是你想要的,而注入的实例似乎好像大概差不多是在初始化时就缓存好了的。解决问题比较简单,背后的原理什么的,只有坐等大神解答一下了。
nulIptr
2021-05-27 18:59:04 +08:00
没想明白,什么场景下才运行时修改注解里面属性的值?这跟我理解的注解用途不太一样啊。。。
persona5
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
2021-05-28 10:15:29 +08:00
@persona5 我认为你说的这种场景就应该用虚方法 override,注解就是编译期添加的额外信息,你还在运行时给改了,我觉得是在挖坑。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/779605

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX