"argument type mismatch" 使用带有 Spring MVC 的 @RequestBody 的泛型类型时出错
"argument type mismatch" error when using a generic type for @RequestBody with Spring MVC
我正在尝试为 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}]
重写ArticleRestController
中的create
方法时,不再抛出异常:
@Override
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public Article create(@RequestBody Article article) {
return articleService.create(article);
}
知道为什么吗?难道 Spring 在用作 @RequestBody
时无法解析通用类型 T
?
从 Spring 4.1 开始,GsonHttpMessageConverter
被添加到 HttpMessageConverter
实例列表中,Spring 用来序列化从 @ResponseBody
返回的对象带注释的处理程序方法。当前 GsonHttpMessageConverter
不够复杂,无法反序列化泛型类型。因此,当它看到类型变量 T
时,它不会尝试将点与 extends
子句中的类型参数 Article
连接起来,而是使用默认值 LinkedTreeMap
。
一种选择是将 Jackson 2 添加到您的类路径中。 Spring 将在 GsonHttpMessageConverter
之前注册其 MappingJackson2HttpMessageConverter
。也就是说,它将首先尝试使用它。 Jackson 的转换器足够智能,可以执行通用反序列化。
我正在尝试为 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}]
重写ArticleRestController
中的create
方法时,不再抛出异常:
@Override
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public Article create(@RequestBody Article article) {
return articleService.create(article);
}
知道为什么吗?难道 Spring 在用作 @RequestBody
时无法解析通用类型 T
?
从 Spring 4.1 开始,GsonHttpMessageConverter
被添加到 HttpMessageConverter
实例列表中,Spring 用来序列化从 @ResponseBody
返回的对象带注释的处理程序方法。当前 GsonHttpMessageConverter
不够复杂,无法反序列化泛型类型。因此,当它看到类型变量 T
时,它不会尝试将点与 extends
子句中的类型参数 Article
连接起来,而是使用默认值 LinkedTreeMap
。
一种选择是将 Jackson 2 添加到您的类路径中。 Spring 将在 GsonHttpMessageConverter
之前注册其 MappingJackson2HttpMessageConverter
。也就是说,它将首先尝试使用它。 Jackson 的转换器足够智能,可以执行通用反序列化。