【问题标题】:Spring: HandlerMethodArgumentResolved not working with custom annotation parameterSpring:HandlerMethodArgumentResolved 不使用自定义注释参数
【发布时间】:2020-01-31 06:14:18
【问题描述】:

我在互联网上搜索了几个小时,并尝试了我找到的解决方案,以便在控制器方法上使用自定义参数注释。

这背后的想法是,我想练习在使用 spring 时如何使用自定义注释映射请求、响应和各种事物。

所以我想要创建一个注释参数,它应该创建一个Map 实例,我的界面是这样编码的:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SearchCriteria {
    String value() default "";
}

解析器:

public class SearchCriteriaResolver implements HandlerMethodArgumentResolver {
    private Logger log = LoggerFactory.getLogger(SearchCriteriaResolver.class);

    private Map<String, Object> parameters = new HashMap<>() {{
        put("name", "");
        put("limit", 10);
    }};

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(SearchCriteria.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();

        log.info("Parameter test: " + request.getParameter("test"));

        return this.parameters;
    }
}

还有配置者:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new SearchCriteriaResolver());
    }
}

我在互联网上多次发现处理程序是以这种方式创建的。所以在控制器中,我正在使用这样的注释:

@RestController
@RequestMapping("/series")
public class SeriesController {
    private static final Logger log = LoggerFactory.getLogger(SeriesController.class);

    @Autowired
    SeriesService seriesService;

    @GetMapping
    public ResponseEntity<List<SeriesBatch>> getSeriesList(@SearchCriteria Map<String, Object> parameters) {
        log.info("GET /series/ -> getSeriesList");
        log.info(parameters.toString());

        List<SeriesBatch> seriesList = this.seriesService.findAll();

        return new ResponseEntity<>(seriesList, HttpStatus.OK);
    }

    ...
}

所以每次触发这个端点时我一直在检查日志,但是解析器上的日志没有被触发,并且控制器方法中的日志只显示一个空对象。我已经调试了应用程序开始查看是否正在调用resolvers.add,它是,但由于某种原因我不知道,这个注释的逻辑没有被执行。

注意:我正在学习 spring 并在很长一段时间后收回 JAVA,所以如果能解释为什么它必须是这样的答案,我将不胜感激。

【问题讨论】:

    标签: java spring


    【解决方案1】:

    重构您的代码,使您之前存储在 java.util.Map 实例中的数据现在将存储在其他对象中,例如自定义类SearchParams 的实例。您甚至可以将您的地图包装为该类中的成员,以使事情变得简单:

    class SearchParams {
    
       private Map<String, Object> values = new HashMap<>(); 
    
       public void setValue(String key, Object value) {
           this.values.put(key, value);
       }
    
       public Object getValue(String key) {
           this.values.get(key);
       }
    
       public Map<String, Object> list() {
           return new HashMap<>(this.values);
       }
    }
    

    现在更改您的控制器方法以接受 SearchParams 对象而不是 Map&lt;String, Object&gt;

        public ResponseEntity<List<SeriesBatch>> getSeriesList(@SearchCriteria SearchParams parameters) { ... }
    

    最后但并非最不重要的一点是,您必须更改 @SearchCriteria 注释实现,例如如下:

    public class SearchCriteriaResolver implements HandlerMethodArgumentResolver {
    
        @Override
        public boolean supportsParameter(MethodParameter methodParameter) {
            return methodParameter.hasParameterAnnotation(SearchCriteria.class);
        }
    
        @Override
        public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
    
            // somehow determine search params
    
            // setup + return your new SearchParams object to encapsulate your determined search params
            SearchParams searchParams = new SearchParams();
            searchParams.add("somekey", "somevalue");
            return searchParams;
        }
    }
    

    现在,请尝试一下,如果效果好,请告诉我;-)

    详细解释:

    看看 Spring 的 org.springframework.web.method.support.HandlerMethodArgumentResolverComposite 类,尤其是 getArgumentResolver(MethodParameter parameter) 方法。在底层,Spring 维护着一个不同HandlerMethodArgumentResolver 实例的列表,并在它们上循环以找到一个与给定方法参数匹配的实例(然后将方法参数的值映射到其他对象!)。已注册的 HandlerMethodArgumentResolvers 之一的类型为 org.springframework.web.method.annotation.MapMethodProcessor。如果 java.util.Map 类型的参数 if 并且首先匹配,则 MapMethodProcessor 也匹配(请参见下面的附加屏幕截图)。这就是为什么您的自定义 HandlerMethodArgumentResolver 从未被调用的原因......

    [[https://reversecoding.net/spring-mvc-requestparam-binding-request-parameters/]] 显示了一个示例,其中java.util.Map 用作控制器方法的参数-> 搜索'7。 @RequestParam with Map'

    WebConfig 配置类中配置自定义任何HandlerMethodArgumentResolver 的顺序/更改优先级的可能方式:

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        // add custom resolver to beginning of resolver list
        resolvers.add(0, new SearchCriteriaResolver());
    }
    

    【讨论】:

    • 感谢您的解释。我确实尝试了你的方法,它工作到一半,但控制器现在抛出一个错误:IllegalArgumentException: argument type mismatch。它确实显示了我在解析器中的日志,但现在没有调用控制器方法。是否会因为我有请求过滤器而发生此错误?
    • 没关系,我已经找到了解决办法。谢谢你的回答。
    • 但是,是否有可能像我最初尝试的那样使用自定义注释将数据输入地图?
    • @OscarReyes 我编辑了我的答案。随意尝试一下 - 以前没有这样做过;-) 也许您使用传递给控制器​​方法的 Map 的旧实现会以这种方式工作,不知道。让我知道它是否有效。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-15
    • 1970-01-01
    • 1970-01-01
    • 2017-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多