在运行时更改Spring RequestMapping注解值

问题描述 投票:0回答:1

我试图在运行时改变HTTP GET方法的RequestMapping注解的值--。你好 (它返回一个简单的字符串)在一个休息服务类中--。S.A.S.R.R.Controller.

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

我能够通过在一个用@PostConstruct注解的init方法中打印注解的值来验证修改后的值,也能够在另一个控制器中打印。然而,当我试图在浏览器中访问GET方法时:用修改后的值----。http:/localhost:9090spring-boot-resthiPradeep。 (行不通)

与原值 - http:/localhost:9090spring-boot-resthelloPradeep。 (行得通)

我希望HTTP GET方法hello能够在运行时使用修改后的路径值进行访问--------。"hi{name}" 而不是原来的路径值 - "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;
    }
}
java spring-boot rest spring-annotations
1个回答
0
投票

这是不可能在运行时改变注解值的,因为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
© www.soinside.com 2019 - 2024. All rights reserved.