在Spring MVC中使用@RequestBody的泛型类型时出现“参数类型不匹配”错误

时间:2015-03-07 15:42:54

标签: java spring spring-mvc generics payload

我试图为Spring MVC实现一个通用的REST控制器:

public abstract class GenericRestController<T extends GenericEntity> {

  protected final GenericService<T> service;

  public GenericRestController(GenericService<T> service) {
    this.service = service;
  }

  @RequestMapping(method = RequestMethod.GET)
  @ResponseStatus(HttpStatus.OK)
  public List<T> list() {
    return service.list();
  }

  @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
  @ResponseStatus(HttpStatus.CREATED)
  public T create(@RequestBody T entity) {
    return service.create(entity);
  }

  // other basic REST methods such as get(), delete(), etc.

}

然后,例如ArticleRestController

@RestController
@RequestMapping(value = "/rest/articles", produces = MediaType.APPLICATION_JSON_VALUE)
public class ArticleRestController extends GenericRestController<Article> {

  @Autowired
  public ArticleRestController(ArticleService articleService) {
    super(articleService);
  }

}

我们假设一篇文章的name字段只有String

调用GET /rest/articles时,我会按预期获得持久文章列表。但是在调用POST /rest/articles {name: "Any Article Name"}时,我会遇到以下异常:

java.lang.IllegalStateException: argument type mismatch

HandlerMethod details: 
Controller [com.basepackage.web.controllers.rest.ArticleRestController]
Method [public T com.basepackage.web.controllers.rest.support.GenericRestController.create(T)]
Resolved arguments: 
[0] [type=com.google.gson.internal.LinkedTreeMap] [value={name=Any Article Name}]

覆盖create中的ArticleRestController方法时,不再抛出任何异常:

  @Override
  @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
  @ResponseStatus(HttpStatus.CREATED)
  public Article create(@RequestBody Article article) {
    return articleService.create(article);
  }

知道为什么吗?可能是Spring在用作T时无法解决泛型类型@RequestBody吗?

1 个答案:

答案 0 :(得分:5)

从Spring 4.1开始,GsonHttpMessageConverter被添加到HttpMessageConverter实例列表中,Spring用它来序列化从@ResponseBody带注释的处理程序方法返回的对象。此当前GsonHttpMessageConverter不够复杂,无法反序列化泛型类型。因此,当它看到类型变量T时,它不会尝试使用Article子句中的类型参数extends连接点,而是使用默认的LinkedTreeMap

一种选择是将Jackson 2添加到您的类路径中。 Spring将在GsonHttpMessageConverter之前注册其MappingJackson2HttpMessageConverter。也就是说,它会先尝试使用它。杰克逊的转换器非常智能,可以执行通用的反序列化。