java - 在运行时更改 Spring RequestMapping 注解值

标签 java spring-boot rest spring-annotations

我正在尝试在运行时更改 HTTP GET 方法的 RequestMapping 注释的值 - hello (返回一个简单的字符串)在休息服务类 - SpringRestController 中>.

hello方法的@RequestMapping注解中定义的uri的值为“/hello/{name}”。我可以使用 SpringRestController 类的构造函数中的反射在运行时将注释的值更改为 "hi/{name}"

我可以通过在用 @PostConstruct 注释注释的 init 方法内以及另一个 Controller 内打印注释的值来验证修改后的值。但是,当我尝试在浏览器中访问 GET 方法时:

修改后的值 - http://localhost:9090/spring-boot-rest/rest/hi/Pradeep (不起作用)

与原始值 - http://localhost:9090/spring-boot-rest/rest/hello/Pradeep (工作正常)


我希望可以使用运行时修改的路径值 - "/hi/{name}" 来访问 HTTP GET 方法 hello,而不是原始路径值 - "/hello/{name} “。 P.S - 这是我们的要求,需要以这种方式完成,以便可以在外部配置 @RequestMapping 的值,而无需更改源代码。
这是代码 - SpringRestController.java

package com.example.spring.rest.controller;
import javax.annotation.PostConstruct;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.spring.rest.custom.annotations.ConfigurableRequestMapping;
import com.example.spring.rest.reflection.ReflectionUtils;
@RestController
@RequestMapping("/rest")
public class SpringRestController {

    public SpringRestController() {
            RequestMapping rm = SpringRestController.class.getAnnotation(RequestMapping.class);
            System.out.println("Old annotation : " + rm.value()[0]);
            RequestMapping rmNew = new ConfigurableRequestMapping("/rest");
            ReflectionUtils.alterAnnotationValueJDK8_v2(SpringRestController.class, RequestMapping.class, rmNew);
            RequestMapping rmModified = SpringRestController.class.getAnnotation(RequestMapping.class);
            System.out.println("Constructor -> New annotation : " + rmModified.value()[0]);
    }

    @RequestMapping(value = "/hello/{name}")
    public String hello(@PathVariable String name) {
        System.out.println("Name : " + name);
        return "Hello " + name;
    }

    @PostConstruct
    private void init(){
        System.out.println("Annotations initialization post construct.");
        RequestMapping rmModified = SpringRestController.class.getAnnotation(RequestMapping.class);
        System.out.println("Init method -> New annotation : " + rmModified.value()[0]);
    }
}

更改注释值的代码-

ReflectionUtils.java

package com.example.spring.rest.reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.spring.rest.controller.SpringRestController;
import com.example.spring.rest.custom.annotations.ConfigurableRequestMapping;
import com.example.spring.rest.utils.PropertyReader;

public class ReflectionUtils {

    @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;
    }
}

最佳答案

不可能在运行时更改注释值,因为 Spring 已经注册了该值。除了对您真正想要实现的目标感到好奇之外,您还可以随意使用多个 @PathVariable 参数,并自行处理评估。

// Either hardcoded values or loaded from elsewhere
private static List<String> GREETINGS = Arrays.asList("Hello", "Hi");

...

@GetMapping(value = "/{greeting}/{name}")
public String greeting(@PathVariable String greeting, @PathVariable String name) {
    System.out.println("Name : " + name);
    if (GREETINGS.stream().anyMatch(greeting::equalsIgnoreCase)) {
        return greeting + " " + name;
    }
    throw new ResponseStatusException(HttpStatus.BAD_REQUEST, 
        "Unknown greeting " + greeting, e);
}

此外,REST API 端点的要点是可预测。你试图实现的目标似乎与它相矛盾。您可以有多个端点,例如 /hi/{name}/hello/{name},但是,在这种特殊情况下,多个参数的使用是正确的,或以下端点尊重资源并使用@RequestParam。我宁愿用这种方式设计它,因为问候语是资源。

  • 示例端点:/greeting?greeting={greeting}&name={name}
  • 示例调用:/greeting?greeting=Hello&name=Pradeep%20Prabhakaran

关于java - 在运行时更改 Spring RequestMapping 注解值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61383716/

相关文章:

java - 最短路径 Dijkstra Java

java - Spring RestController 在 Wildfly 上返回未找到 404

java - Spring请求映射自定义注解——二义性映射

java - Jakson 多态枚举案例

java - 使用tomcat服务器创建一个restful web服务

java - 为什么流这么快

java - 在 JavaFX 桌面应用程序中获取远程 IP 地址

java - Java 中的笛卡尔坐标

architecture - 本地接口(interface)的 REST?

node.js - 来自 Node JS 的 HTTPS REST API 调用类型错误 : First argument must be a string or Buffer