如何在 RunTime 中更改方法注释值? [英] How i can change method annotations value in RunTime?

查看:15
本文介绍了如何在 RunTime 中更改方法注释值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有像

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

}

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

And i want to change value of SendTo annotation in runtime.

我尝试这样做:

@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[] annotations)和方法注释(method.getDeclaredAnnotations())中没有变化.

but this only changes in the annotation array (Annotation[] annotations) and in the method annotations (method.getDeclaredAnnotations()) does not change.

请告诉我如何做到这一点,有可能吗?

Please tell me how to do this and is it possible at all?

推荐答案

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

Quoting my own (slightly modified) comment first because I still think these two approaches are what you should try first:

你可能想看看

  • destination variable placeholders for @SendTo or @SubscribeMapping and also
  • my answer about how to evaluate SpEL (Spring Expression Language).

也许这两种方法中的一种适合您.

Maybe one of these two approaches is viable for you.

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

Having said that, you can also turn to the dark side of the force and really try to manipulate annotation values. Here is a little proof of concept in AspectJ (not Spring AOP, but the pointcut syntax would be identical):

示例驱动程序应用:

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();
  }
}

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

Without an aspect this would print:

original
original
original

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

No surprises here. Now use this aspect (in Spring AOP you should also add a @Component annotation):

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

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

As you can see in the log above, executing the aspect advice every time the method is called is inefficient if you only need to change the value once and its value is not dynamic. Instead, you could also manipulate the annotation from another place outside of an aspect or add a static boolean member to the aspect in order to manipulate the annotation only once:

  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 项目,我上面的辅助方法取自 那里
  • 顺便说一下,Java-EX 也受到了SO 问题的启发.
  • Java-EX project, helper method in my aspect above taken from there
  • Java-EX was also inspired by a SO question, by the way.

这篇关于如何在 RunTime 中更改方法注释值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆