1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > No converter found for return value of type错误解决以及消息转化器简单分析

No converter found for return value of type错误解决以及消息转化器简单分析

时间:2022-12-26 12:11:53

相关推荐

No converter found for return value of type错误解决以及消息转化器简单分析

一、错误发生的背景

最近搭建一个springboot项目,在开发到全局异常处理的时候,运行报错。具体代码以及错误信息如下:

代码:GlobalExceptionHandler(异常处理类)

/*** 全局异常处理类* @author HXY* @version 1.0*/@ControllerAdvicepublic class GlobalExceptionHandler extends ResponseEntityExceptionHandler {//日志处理类private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);/*** Exception异常处理* @param e* @return HoneyResult*/@ExceptionHandler(value = Exception.class)@ResponseBodypublic HoneyResult handleException(Exception e) {logger.error(e.getMessage());logger.error(ExceptionUtil.getStackTrace(e));return HoneyResult.build(HoneyResult.ERR_STATE,e.getMessage());}/*** 参数无效的异常处理* @param e* @return*/@ExceptionHandler(value = InvalidException.class)@ResponseBodypublic HoneyResult handleInvalidException(InvalidException e) {logger.error("parameter is invalid");logger.error(e.getMessage());return HoneyResult.build(e.getCode(),e.getMessage());}}

在controller层抛出异常

@RestController@RequestMapping("/honeybee")public class UserController {@Autowiredprivate UserService userService;private final Logger logger = LoggerFactory.getLogger(UserController.class);@GetMapping("/select")public UserBean xxxx() {logger.info("开始查询");UserBean result = userService.select();if (1 ==1) {int n = 1 / 0;//throw new InvalidException(ResultCode.INVALID_PARAMETER.getCode(),ResultCode.INVALID_PARAMETER.getMessage());}logger.info("查询结束:" + result.toString());return result;}}

结果启动项目,除了期望的异常被正确抛出以外,还抛出了上面说到的异常。异常信息如下:

-01-12 12:16:52 |WARN |http-nio-8888-exec-1 |ExceptionHandlerExceptionResolver.java:392 |org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver |Failed to invoke @ExceptionHandler method: public mon.bean.HoneyResult mon.exception.GlobalExceptionHandler.handleException(java.lang.Exception)java.lang.IllegalArgumentException: No converter found for return value of type: class mon.bean.HoneyResultat org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:187)at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:174)at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81)at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:113)at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.doResolveHandlerMethodException(ExceptionHandlerExceptionResolver.java:385)at org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver.doResolveException(AbstractHandlerMethodExceptionResolver.java:59)at org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:135)at org.springframework.web.servlet.handler.HandlerExceptionResolverComposite.resolveException(HandlerExceptionResolverComposite.java:76)at org.springframework.web.servlet.DispatcherServlet.processHandlerException(DispatcherServlet.java:1222)at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1034)at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:984)at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)at org.apache..NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)at org.apache..SocketProcessorBase.run(SocketProcessorBase.java:49)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)at java.lang.Thread.run(Thread.java:745)

二、异常分析

这个异常你的意思大概是说没有转化器将我们自定义的返回类型转换成json到http响应。然后跟踪代码,发现最终报错的地方是这里:

AbstractMessageConverterMethodProcessor类的writeWithMessageConverters方法的186行。基于以上分析,解决方案就是需要我们自定义消息转换器。在解决这个问题之前我们不妨稍微花点时间研究一下springMvc的消息转换器的源码。

三、消息转换器

ServletInvocableHandlerMethod.java

这个类继承自InvocableHandlerMethod类,除了父类的方法外,还扩展了几个方法,实现了可以通过已经注册的HandlerMethodReturnValueHandler对程序的返回值进行处理,以及设置返回值的响应状态等。其中对返回值的处理定义invokeAndHandle方法中,源代码如下

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);//设置响应状态setResponseStatus(webRequest);//返回值为null的情况,设置请求处理完成if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {mavContainer.setRequestHandled(true);return;}}//返回值不为null,ResponseStatus有内容的情况,设置请求处理完成else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}//否则设置请求处理未完成,并使用returnValueHandlers进行处理,returnValueHandlers:HandlerMethodReturnValueHandlerComposite类对象mavContainer.setRequestHandled(false);try {this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);}throw ex;}}

2.HandlerMethodReturnValueHandlerComposite.java

在该类中的处理流程如下:

//迭代所有的HandlerMethodReturnValueHandler,选择一个支持返回值类型的处理器,然后调用它的handleReturnValue方法@Overridepublic void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {//选择合适的HandlerMethodReturnValueHandler HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}//调用对应的handleReturnValue方法处理handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}

3.HandlerMethodReturnValueHandler是一个接口,定义了一下两个方法

//该HandlerMethodReturnValueHandler是否支持对应的返回值类型boolean supportsReturnType(MethodParameter returnType);//处理返回值void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

这个接口有很多实现类,

今天我们用到的是AbstractMessageConverterMethodProcessor.java这个类,他是一个抽象类,没有实现接口中的方法,而是在他的子类RequestResponseBodyMethodProcessor中进行了实现。AbstractMessageConverterMethodProcessor中定义了writeWithMessageConverters方法,RequestResponseBodyMethodProcessor中实现了handleReturnValue方法,调用了父类的writeWithMessageConverters方法。类图以及主要代码如下:

public void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);// Try even with null return value. ResponseBodyAdvice could get involved.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}

protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {Object outputValue;Class<?> valueType;Type declaredType;if (value instanceof CharSequence) {outputValue = value.toString();valueType = String.class;declaredType = String.class;}else {outputValue = value;valueType = getReturnValueType(outputValue, returnType);declaredType = getGenericType(returnType);}HttpServletRequest request = inputMessage.getServletRequest();//获取客户端Accept字段接收的content-type,如果@RequestMapping中的produces配置了content-type,则返回此content-type,若果没有,//则获取所有HttpMessageConverter所支持的content-type,然后通过requestedMediaTypes和producibleMediaTypes 对比,选定一个最合适的content-typeList<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);//获取转换器支持的MediaTypeList<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);if (outputValue != null && producibleMediaTypes.isEmpty()) {throw new IllegalArgumentException("No converter found for return value of type: " + valueType);}//两次遍历,获取转换器支持的MediaType和请求的MediaType兼容的MediaType集合Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();for (MediaType requestedType : requestedMediaTypes) {for (MediaType producibleType : producibleMediaTypes) {//isCompatibleWith判断是否兼容if (requestedType.isCompatibleWith(producibleType)) {//getMostSpecificMediaType通过q值获取最具体的一个,q值可以指定compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));}}}if (compatibleMediaTypes.isEmpty()) {if (outputValue != null) {throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);}return;}List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);MediaType.sortBySpecificityAndQuality(mediaTypes);MediaType selectedMediaType = null;for (MediaType mediaType : mediaTypes) {if (mediaType.isConcrete()) {selectedMediaType = mediaType;break;}else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;break;}}if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();for (HttpMessageConverter<?> messageConverter : this.messageConverters) {if (messageConverter instanceof GenericHttpMessageConverter) {//是否可以写if (((GenericHttpMessageConverter) messageConverter).canWrite(declaredType, valueType, selectedMediaType)) {outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),inputMessage, outputMessage);if (outputValue != null) {addContentDispositionHeader(inputMessage, outputMessage);//写出到响应((GenericHttpMessageConverter) messageConverter).write(outputValue, declaredType, selectedMediaType, outputMessage);if (logger.isDebugEnabled()) {logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +"\" using [" + messageConverter + "]");}}return;}}else if (messageConverter.canWrite(valueType, selectedMediaType)) {outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),inputMessage, outputMessage);if (outputValue != null) {addContentDispositionHeader(inputMessage, outputMessage);((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage);if (logger.isDebugEnabled()) {logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +"\" using [" + messageConverter + "]");}}return;}}}if (outputValue != null) {throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);}}

整个处理过程到这里就结束了。分析了springMvc消息转换器对返回值的处理过程,也找到了错误原因,那么下面就开始谈谈怎么解决问题吧。

四、解决方案:自定义fastJson作为消息转换器,处理自定义类型。

引入fastJson的jar

<!-- fastjson json转换器 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency>

2.继承WebMvcConfigurerAdapter,配置fastJson消息转换器。

WebMvcConfigurerAdapter:是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制。代码如下:

/*** 自定义配置类实现JavaBean注解形式配置 WebMvcConfigurerAdapter在springboot2.0和spring5.0中已经废弃* @author HXY*/@Configurationpublic class HoneyWebMvcConfigure extends WebMvcConfigurerAdapter {/*** 配置消息内容转换器,定义请求返回的内容用fastjson进行转换* @param converters*/@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {super.configureMessageConverters(converters);//创建fastjson消息转换器FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();//创建配置类FastJsonConfig fastJsonConfig = new FastJsonConfig();//修改配置返回内容的过滤fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat,SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteMapNullValue,SerializerFeature.WriteNullStringAsEmpty,SerializerFeature.WriteNullListAsEmpty,SerializerFeature.WriteNullNumberAsZero,SerializerFeature.WriteNullBooleanAsFalse);fastJsonConverter.setFastJsonConfig(fastJsonConfig);//添加StringHttpMessageConverter,解决中文乱码问题StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));List<MediaType> mediaTypes = Collections.singletonList(MediaType.APPLICATION_JSON_UTF8);stringConverter.setSupportedMediaTypes(mediaTypes);//讲fastJson添加到消息转换列表converters.add(fastJsonConverter);converters.add(stringConverter);}}

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