【问题标题】:How i can change method annotations value in RunTime?如何更改 RunTime 中的方法注释值?
【发布时间】:2019-08-30 22:48:32
【问题描述】:

我有类似的控制器

@MessageMapping("/room.register")
@SendTo("#{sendTo}")
public Message addUser(@Payload Message message,
                       SimpMessageHeaderAccessor headerAccessor) {
    headerAccessor.getSessionAttributes().put("username", 
    message.getSender());
    return message;

}

我想在运行时更改 SendTo 注释的值。

我尝试如下:

@Aspect
public class SendToAspect {
@Autowired
private WebSocketConfigurationProperties webSocketConfigurationProperties;


@Around("execution (public * *(..)) && @annotation(ann)")
public Object execute(final ProceedingJoinPoint point, final SendTo ann) 
throws Throwable {

    MethodSignature signature = (MethodSignature) point.getSignature();
    Method method = signature.getMethod();
    method.setAccessible(true);

    Annotation[] annotations = method.getDeclaredAnnotations();
    for (int i = 0; i < annotations.length; i++) {
        if (annotations[i].annotationType().equals(SendTo.class)) {
            annotations[i] = new SendTo() {

                @Override
                public Class<? extends Annotation> annotationType() {
                    return SendTo.class;
                }

                @Override
                public String[] value() {
                    return new String[] 
                            {webSocketConfigurationProperties.getTopic()};
                }
            };
        }
    }
    return point.proceed();
}

}

但是这只在注解数组(Annotation[]注解)中改变,在方法注解(method.getDeclaredAnnotations())中没有改变。

请告诉我该怎么做,有可能吗?

【问题讨论】:

  • 注解是在编译时确定的,这就是为什么它们只能包含常量。至于你的问题,你踏入了一个叫做XY problem的陷阱。不要试图解释如何您想解决您的问题,而是告诉我们您想要实现的什么。为什么心智正常的人会想要更改注释?
  • @kriegaex 嗨!我只想从属性值-apllication.yaml 中读取注释值(SendTo (value = ".."))。我之前问过这个问题,但没有找到解决方案stackoverflow.com/questions/57677433/…
  • 您可能想查看destination variable placeholders 以及我对how to evaluate SpEL (Spring Expression Language) 的回答。也许这两种方法中的一种对您来说是可行的。不过,我不是 Spring 用户,只是 AOP 专家。可能有更好的车载方式来实现您想要的。

标签: java reflection annotations aop


【解决方案1】:

首先引用我自己(稍作修改)的评论,因为我仍然认为这两种方法是你应该首先尝试的:

你可能想看看

也许这两种方法中的一种对您来说是可行的。

话虽如此,你也可以转向力量的阴暗面,真正尝试操纵注释值。这是 AspectJ 中的一个小概念证明(不是 Spring AOP,但切入点语法是相同的):

驱动程序应用示例:

package de.scrum_master.app;

import org.springframework.messaging.handler.annotation.SendTo;

public class Application {
  @SendTo("original")
  public void doSomething() throws NoSuchMethodException, SecurityException {
    SendTo sendTo = Application.class
      .getDeclaredMethod("doSomething")
      .getAnnotationsByType(SendTo.class)[0];
    System.out.println(sendTo.value()[0]);
  }

  public static void main(String[] args) throws NoSuchMethodException, SecurityException {
    new Application().doSomething();
    new Application().doSomething();
    new Application().doSomething();
  }
}

如果没有方面,这将打印:

original
original
original

这里没有惊喜。现在使用这个方面(在 Spring AOP 中你还应该添加一个 @Component 注释):

package de.scrum_master.aspect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Map;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.messaging.handler.annotation.SendTo;

@Aspect
public class SendToAspect {

  @Before("execution(* *(..)) && @annotation(sendTo)")
  public void changeAnnotation(JoinPoint thisJoinPoint, SendTo sendTo) {
    System.out.println(thisJoinPoint + "\n  [BEFORE] " + sendTo);
    changeAnnotationValue(sendTo, "value", new String[] { "changed" });
    System.out.println("  [AFTER]  " + sendTo);
  }

  @SuppressWarnings("unchecked")
  public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue) {
    Object handler = Proxy.getInvocationHandler(annotation);
    Field f;
    try {
      f = handler.getClass().getDeclaredField("memberValues");
    } catch (NoSuchFieldException | SecurityException e) {
      throw new IllegalStateException(e);
    }
    f.setAccessible(true);
    Map<String, Object> memberValues;
    try {
      memberValues = (Map<String, Object>) f.get(handler);
    } catch (IllegalArgumentException | IllegalAccessException e) {
      throw new IllegalStateException(e);
    }
    Object oldValue = memberValues.get(key);
    if (oldValue == null || oldValue.getClass() != newValue.getClass()) {
      throw new IllegalArgumentException();
    }
    memberValues.put(key, newValue);
    return oldValue;
  }

}

现在控制台日志是:

execution(void de.scrum_master.app.Application.doSomething())
  [BEFORE] @org.springframework.messaging.handler.annotation.SendTo(value=[original])
  [AFTER]  @org.springframework.messaging.handler.annotation.SendTo(value=[changed])
changed
execution(void de.scrum_master.app.Application.doSomething())
  [BEFORE] @org.springframework.messaging.handler.annotation.SendTo(value=[changed])
  [AFTER]  @org.springframework.messaging.handler.annotation.SendTo(value=[changed])
changed
execution(void de.scrum_master.app.Application.doSomething())
  [BEFORE] @org.springframework.messaging.handler.annotation.SendTo(value=[changed])
  [AFTER]  @org.springframework.messaging.handler.annotation.SendTo(value=[changed])
changed

正如你在上面的日志中看到的,如果你只需要改变一次值并且它的值不是动态的,那么每次调用方法时执行方面建议是低效的。相反,您还可以从方面之外的其他位置操作注释,或向方面添加静态布尔成员,以便仅操作一次注释:

  static boolean done = false;

  @Before("execution(* *(..)) && @annotation(sendTo)")
  public void changeAnnotation(JoinPoint thisJoinPoint, SendTo sendTo) {
    if (done)
      return;
    System.out.println(thisJoinPoint + "\n  [BEFORE] " + sendTo);
    changeAnnotationValue(sendTo, "value", new String[] { "changed" });
    System.out.println("  [AFTER]  " + sendTo);
    done = true;
  }

那么输出将是:

execution(void de.scrum_master.app.Application.doSomething())
  [BEFORE] @org.springframework.messaging.handler.annotation.SendTo(value=[original])
  [AFTER]  @org.springframework.messaging.handler.annotation.SendTo(value=[changed])
changed
changed
changed

另见:

  • Java-EX 项目,我上面的辅助方法取自 there
  • 顺便说一下,Java-EX 也受到了 SO question 的启发。

【讨论】:

  • 谢谢你,非常匹配!)不幸的是我不能投票,因为我的声誉不被允许((
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-12
  • 1970-01-01
相关资源
最近更新 更多