1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > No converter found for return value of type: class xxx(自定义的class对象)

No converter found for return value of type: class xxx(自定义的class对象)

时间:2021-07-30 01:31:52

相关推荐

No converter found for return value of type: class xxx(自定义的class对象)

先贴一份异常信息:

org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class org.jackson.mvc.web.po.RequestInfoorg.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:220)org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:181)org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82)org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:123)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)javax.servlet.http.HttpServlet.service(HttpServlet.java:647)org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)javax.servlet.http.HttpServlet.service(HttpServlet.java:728)org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)

对应的代码如下:

package org.jackson.mvc.web.po;import java.io.Serializable;import java.math.BigDecimal;import java.util.Date;public class RequestInfo implements Serializable {private String username;private Long userId;private int userAge;private double userHeight; // 身高private Date birthday;private BigDecimal balance; // 余额}

package org.jackson.mvc.web.controllers.hello;import org.jackson.mvc.web.po.RequestInfo;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping(path = "v1/hello/")public class HelloController {@PostMapping(path = "showRequestInfo")public RequestInfo showRequestInfo(@RequestBody RequestInfo requestInfo) {return requestInfo;}}

请求参数如下:

### Send POST request with json bodyPOST http://localhost:9090/v1/hello/showRequestInfoContent-Type: application/json{"username" : "java","userId" : "123455","userAge" : "23","userHeight" : "170.5","birthday" : "1990-12-31 23:59:59","balance" : "20.55"}

其实看到我定义的实体对象,基本上就可以看出一些端倪来了,我定义的实体类没有对应属性的get和set方法。而这正是问题的根源所在。解决方法就是将对应的属性添加get和set方法。下面来看看是什么原因导致的?

根据异常信息,查看Spring源码,抛出异常的代码如下:

#org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters######HttpServletRequest request = inputMessage.getServletRequest();List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);if (body != null && producibleTypes.isEmpty()) {throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);}

通过调试发现返回的body是一个空的对象(这里的空对象不是null,是指一个已经创建的对象,只是属性都是初始值,因为没有set方法,无法将请求中的参数进行赋值)。那就是另一个条件满足了。查看这个获取producibleTypes的方法,发现是通过注册的HttpMessageConverter和传递的参数类型查找可用的MediaType。

List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType)

通常默认的Spring容器中注册的MessageConverter有如下九种:

ByteArrayHttpMessageConverterStringHttpMessageConverterResourceHttpMessageConverterResourceRegionHttpMessageConverterAllEncompassingFormHttpMessageConverterJaxb2RootElementHttpMessageConverterMappingJackson2HttpMessageConverterMappingJackson2CborHttpMessageConverter

当我们的参数一个POJO对象时,正常而言会匹配到MappingJackson2HttpMessageConverter,但是当定义的POJO对象属性都没有get和set方法时,该对象的canWrite方法的判断就会返回false。其源码如下:

org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#canWrite

public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {if (!canWrite(mediaType)) {return false;}if (mediaType != null && mediaType.getCharset() != null) {Charset charset = mediaType.getCharset();if (!ENCODINGS.containsKey(charset.name())) {return false;}}AtomicReference<Throwable> causeRef = new AtomicReference<>();if (this.objectMapper.canSerialize(clazz, causeRef)) {return true;}logWarningIfNecessary(clazz, causeRef.get());return false;}

继续跟踪该方法中this.objectMapper.canSerialize(clazz, causeRef),最后发现在下述方法中返回一个ReadOnlyClassToSerializerMap.Bucket

com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap#untypedValueSerializer(java.lang.Class<?>)

public JsonSerializer<Object> untypedValueSerializer(Class<?> type) {ReadOnlyClassToSerializerMap.Bucket bucket = this._buckets[TypeKey.untypedHash(type) & this._mask];if (bucket == null) {return null;} else if (bucket.matchesUntyped(type)) {return bucket.value;} else {do {if ((bucket = bucket.next) == null) {return null;}} while(!bucket.matchesUntyped(type));return bucket.value;}}

看到这里其在往下底层的源码我就没有深入挖掘了,各位有兴趣可以去看看。我试了一下当我将定义的POJO对象中至少一个属性设置get和set方法后,请求可正常返回,但是只返回有get和set方法的属性。

例如修改为如下代码:

package org.jackson.mvc.web.po;import java.io.Serializable;import java.math.BigDecimal;import java.util.Date;public class RequestInfo implements Serializable {private String username;private Long userId;private int userAge;private double userHeight; // 身高private Date birthday;private BigDecimal balance; // 余额public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}}

请求响应结果如下:

POST http://localhost:9090/v1/hello/showRequestInfoHTTP/1.1 200 Content-Type: application/jsonTransfer-Encoding: chunkedDate: Thu, 31 Dec 19:32:59 GMTKeep-Alive: timeout=20Connection: keep-alive{"username": "java"}Response code: 200; Time: 235ms; Content length: 19 bytes

注意。以上情况均基于你的应用引入jackson相关的依赖。我使用的jackson的版本依赖信息如下:

<jackson.version>2.11.2</jackson.version><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>${jackson.version}</version></dependency>

缺少以上三个jar包时,我这边遇到的提示如下信息:

<body><h1>HTTP状态 415 - 不支持的媒体类型</h1><hr class="line"/><p><b>类型</b> 状态报告</p><p><b>描述</b> 源服务器拒绝服务请求,因为有效负载的格式在目标资源上此方法不支持。</p><hr class="line"/><h3>Apache Tomcat/9.0.41</h3></body>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。