Spring请求映射自定义注释 - 模糊映射

时间:2017-04-20 12:55:12

标签: java spring spring-mvc spring-boot annotations

我有以下Spring MVC控制器:

@RestController
@RequestMapping(value = "my-rest-endpoint")
public class MyController {

    @GetMapping
    public List<MyStuff> defaultGet() {
        ...
    }

    @GetMapping(params = {"param1=value1", "param2=value2"})
    public MySpecificStuff getSpecific() {
        ...
    }

    @GetMapping(params = {"param1=value1", "param2=value3"})
    public MySpecificStuff getSpecific2() {
        return uiSchemas.getRelatedPartyUi();
    }
}

我需要的是使用自定义注释使其更通用:

@RestController
@RequestMapping(value = "my-rest-endpoint")
public class MyController {

    @GetMapping
    public List<MyStuff> defaultGet() {
        ...
    }

    @MySpecificMapping(param2 = "value2")
    public MySpecificStuff getSpecific() {
        ...
    }

    @MySpecificMapping(param2 = "value3")
    public MySpecificStuff getSpecific2() {
        return uiSchemas.getRelatedPartyUi();
    }
}

我知道Spring meta annotations可以帮助我。

所以我定义了注释:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@RequestMapping(method = RequestMethod.GET, params = {"param1=value1"})
public @interface MySpecificMapping {

    String param2() default "";

}

仅凭这一点就不会做到这一点。

所以我添加了一个拦截器来处理这个&#34; param2&#34;:

public class MyInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // get annotation of the method
            MySpecificMapping mySpecificMapping = handlerMethod.getMethodAnnotation(MySpecificMapping.class);
            if (mySpecificMapping != null) {
                // get the param2 value from the annotation
                String param2 = mySpecificMapping.param2();
                if (StringUtils.isNotEmpty(param2)) {
                    // match the query string with annotation
                    String actualParam2 = request.getParameter("param2");
                    return param2 .equals(actualParam2);
                }
            }
        }

        return true;
    }

}

当然,将它包含在Spring配置中。

这很好但只有每个控制器都有一个这样的自定义映射。

如果我添加两个用@MySpecificMapping注释的方法,即使具有不同的&#34; param2&#34;然后我得到一个&#34;模糊的映射&#34;应用程序启动错误:

java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'myController' method 
public com.nailgun.MySpecificStuff com.nailgun.MyController.getSpecific2()
to {[/my-rest-endpoint],methods=[GET],params=[param1=value1]}: There is already 'myController' bean method
public com.nailgun.MySpecificStuff com.nailgun.MyController.getSpecific() mapped.
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.assertUniqueMethodMapping(AbstractHandlerMethodMapping.java:576)
 - Application startup failed

我理解为什么会这样。

但是,你能不能给我一个暗示,那就是两个不同的映射?

我在Spring Web 4.3.5中使用Spring Boot 1.4.3

3 个答案:

答案 0 :(得分:2)

@AliasFor是做这样的事情的注释。 以下是使用@RequestMapping

的自定义注释示例
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public @interface JsonGetMapping {
    @AliasFor(annotation = RequestMapping.class, attribute = "value")
    String value() default "";
}

以及使用示例

@JsonGetMapping("/category/{categoryName}/books")
public List<Book> getBooksByCategory(@PathVariable("categoryName") String categoryName){
    return bookRepository.getBooksByCategory(categoryName);
}

答案 1 :(得分:1)

您无法将堆栈中的注释与其params绑定,Spring会将这两个方法视为具有相等@RequestMapping的方法。

但你可以尝试制作一个技巧:在映射构建器之前以某种方式嵌入自定义注释增强器并执行注释替换:

  1. 获取带注释@MySpecificMapping的所有方法:

    MySpecificMapping myMapping = ...;

  2. 阅读每个此类方法的@RequestMapping注释,假设它将是

    RequestMapping oldMapping = ...;

  3. 创建@RequestMapping类的新实例:

    RequestMapping newMapping = new RequestMapping() { // ... rest methods @Override public String[] params() { // here merge params from old and MySpecificMapping: String[] params = new String[oldMapping.params().length + 1]; // todo: copy old one // ... params[params.length-1] = myMapping.param2(); return params; } }

  4. 强制将新的newMapping相应地分配给每个方法而不是oldMapping

  5. 这是相当棘手和复杂的,但我认为这只是实现你想要的一种方式。

答案 2 :(得分:0)

我认为解决此问题的最佳方法是将@RequestMapping注释移动到方法级别而不是类级别。

Spring给你的错误是因为Spring在一条无效的路径上绑定了多个处理程序。也许请举例说明您要公开的网址,以便我们更好地了解您尝试构建的内容。