技术解析

控制器初始化时修改了自己以及父类控制器方法上的注解中的值,但是切面中获取时,父类方法上的注解中的值还是修改前的
0
2021-06-01 12:16:55
idczone

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

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

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

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

// 注解
@Retention(RetentionPolicy.RUNTIME)
@Ta抗投诉服务器rget({ElementType.METHOD})
public @interface MyAnnotation {
    String testField() default "test1";
}

// 抽象控制器
public abstract class AbstractController{
    
    @MyAnnotation
    @RequestMapping(value = "method1", method = RequestMethod.GET)
    public RestfulResponse method1() {
        return new RestfulResponse<>(true);
    }
    
    @MyAnnotation
    @RequestMapping(value = "method2", method = RequestMethod.GET)
    public RestfulResponse 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 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 中的不是同一个?


知识盲区,坐等大神。
个人感觉可能是顺序问题导致被 default 值覆盖了?

不太了解 spring aop 是个什么机制,debug 了一下,发现注入的 MyAnnotation 的实例和通过反射获取的实例是两个不同对象
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
通过反射获取的 annotion 实例是你想要的,而注入的实例似乎好像大概差不多是在初始化时就缓存好了的。解决问题比较简单,背后的原理什么的,只有坐等大神解答一下了。

没想明白,什么场景下才运行时修改注解里面属性的值?这跟我理解的注解用途不太一样啊。。。

感谢,我也测试一下看看。

原因我在开头说了,不想仅仅为了指定下 testField 而去重写多个方法。
MyAnnotation 中 testField 的默认值是 test1 对吧,我在 MyController 中需要只用 @MyAnnotation(testField = "test2") 的注解,既需要指定 testField 为 test2 。但是接口方法内的逻辑又没有需要修改的地方,不修改注解中 testField 的值的话。MyController 中就要重写 method1 、method2 了,重写仅仅为了去加上 @MyAnnotation(testField = "test2") 这个注解。

我认为你说的这种场景就应该用虚方法 override,注解就是编译期添加的额外信息,你还在运行时给改了,我觉得是在挖坑。

数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服