SpringBoot2.X 遭遇 No converter found for return value of type: class java.util.LinkedHashMap

Love The Way You Lie 2022-05-11 13:36 255阅读 0赞

前几天,在项目上线时遭遇了 No converter found for return value of type: class java.util.LinkedHashMap 异常,异常的糟心唉。。。

场景重现
return 基本类型木有问题,但是return 对象的时候就报出了该问题,
网上答案汇总
1、缺失Jackson 依赖,如果有 spring-boot-starter-web依赖可排除该问题
2、是否是 POJO (get set 方法是否有)
: 我使用lombok插件,同样排除了此项可能性
3、注解是否是 @RestController


以上可能性完全排除,由于上线紧急,暂时采用了
HttpServletReponse.getWriter().print() 的方式

昨天在朋友帮助下,推测出是Swagger2 的问题,便没有在继续进行下去,然而一小时前采用一个demo进行测试排查是,发现并不是,继续昨天的思路,问题可能出现在拦截器那里,于是乎,开始从拦截器分析,就在刚刚,终于分析出了问题所在…


— 2018/11/07 凌晨 00:30 重新改写,(上周,今晚抽空改写)在一位网友(微信昵称 rookie_cc )的帮助下发现之前的文章存在巨大漏洞

问题出现原因
extends WebMvcConfigurationSupport,这种方式会屏蔽springboot @EnableAutoConfiguration中的设置

我的源码是这样的:

  1. /**
  2. * 国际化默认语言配置及编码方式修改
  3. * @author fxbin
  4. * @version v1.0
  5. * @since 2018/10/23 14:38
  6. */
  7. @Configuration
  8. public class I18nConfig extends WebMvcConfigurationSupport {
  9. @Bean
  10. public LocaleResolver localeResolver() {
  11. CookieLocaleResolver localeResolver = new CookieLocaleResolver();
  12. //localeResolver.setCookieName("lang");
  13. //设置默认区域
  14. localeResolver.setDefaultLocale(new Locale("en", "US"));
  15. //设置cookie有效期.
  16. localeResolver.setCookieMaxAge(-1);
  17. return localeResolver;
  18. }
  19. @Bean
  20. public HttpMessageConverter<String> responseBodyConverter() {
  21. StringHttpMessageConverter converter = new StringHttpMessageConverter(
  22. Charset.forName("UTF-8"));
  23. return converter;
  24. }
  25. @Override
  26. protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  27. super.configureMessageConverters(converters);
  28. converters.add(responseBodyConverter());
  29. }
  30. /**
  31. * 解决SpringBoot2.X 加入拦截器后swagger2不能访问问题
  32. * @author fxbin
  33. * @param registry
  34. */
  35. @Override
  36. public void addInterceptors(InterceptorRegistry registry) {
  37. registry.addInterceptor(new LocaleChangeInterceptor())
  38. .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
  39. }
  40. @Override
  41. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  42. registry.addResourceHandler("swagger-ui.html")
  43. .addResourceLocations("classpath:/META-INF/resources/");
  44. registry.addResourceHandler("/webjars/**")
  45. .addResourceLocations("classpath:/META-INF/resources/webjars/");
  46. }
  47. }

查看Spring Boot启动WebMVC自动配置的条件发现 :

  1. @Configuration
  2. @ConditionalOnWebApplication(type = Type.SERVLET)
  3. @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
  4. @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
  5. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
  6. @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
  7. ValidationAutoConfiguration.class })
  8. public class WebMvcAutoConfiguration {

@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),只有当容器中没有WebMvcConfigurationSupport这个类型的组件的时候,才会启动自动配置。

所以当我们继承WebMvcConfigurationSupport之后,除非在继承类中重写了一系列有关WebMVC的配置,否则可能就会遇到静态资源访问不到,返回数据不成功这些一系列问题了。

鉴于SpringBoot2.X 中 WebMvcConfigurerAdapter 已经过时,所以可以实现 WebMvcConfigurer 接口来达到相同的目的
在这里插入图片描述

我的实现代码如下

  1. /**
  2. * 国际化默认语言配置及编码方式修改
  3. * @author fxbin
  4. * @version v1.1
  5. * @since 2018/8/23 14:38
  6. * @see org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
  7. * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
  8. */
  9. @Configuration
  10. public class I18nConfig implements WebMvcConfigurer {
  11. @Bean
  12. public LocaleResolver localeResolver() {
  13. CookieLocaleResolver localeResolver = new CookieLocaleResolver();
  14. //localeResolver.setCookieName("lang");
  15. //设置默认区域
  16. localeResolver.setDefaultLocale(new Locale("en", "US"));
  17. //设置cookie有效期.
  18. localeResolver.setCookieMaxAge(-1);
  19. return localeResolver;
  20. }
  21. @Bean
  22. public HttpMessageConverter<String> responseBodyConverter() {
  23. StringHttpMessageConverter converter = new StringHttpMessageConverter(
  24. Charset.forName("UTF-8"));
  25. return converter;
  26. }
  27. @Override
  28. public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  29. converters.add(responseBodyConverter());
  30. }
  31. }

总结:
1、@EnableWebMvc=WebMvcConfigurationSupport,使用了@EnableWebMvc注解等于扩展了WebMvcConfigurationSupport但是没有重写任何方法。@EnableWebMvcWebMvcConfigurationSupport 二者只能选一个,如果同时实现,则会失效 (感谢一位网友热心帮助,开始忽略了这个问题)
2、继承WebMvcConfigurationSupport, 需要重写一系列有关WebMVC的配置,否则可能就会遇到静态资源访问不到,返回数据不成功这些一系列问题了(我就是掉在此坑),
因为springboot已经实例化了WebMvcConfigurationSupport, 如果使用@EnableWebMvc 注解或者继承WebMvcConfigurationSupport会覆盖默认实现, 一般建议还是不覆盖默认的好
3、实现WebMvcConfigurer 接口
示例:

  1. import com.alibaba.fastjson.serializer.SerializerFeature;
  2. import com.alibaba.fastjson.support.config.FastJsonConfig;
  3. import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.format.FormatterRegistry;
  6. import org.springframework.format.datetime.DateFormatter;
  7. import org.springframework.http.MediaType;
  8. import org.springframework.http.converter.HttpMessageConverter;
  9. import org.springframework.web.servlet.ModelAndView;
  10. import org.springframework.web.servlet.config.annotation.CorsRegistry;
  11. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  12. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  13. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  14. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  15. import javax.servlet.http.HttpServletRequest;
  16. import javax.servlet.http.HttpServletResponse;
  17. import java.time.LocalDate;
  18. import java.util.ArrayList;
  19. import java.util.List;
  20. /**
  21. * 实现WebMvcConfigurer接口的部分示例...
  22. *
  23. * @author fxbin
  24. * @version v1.0
  25. * @since 2018/11/7 1:21
  26. */
  27. @Configuration
  28. public class WebConfig implements WebMvcConfigurer {
  29. /**
  30. * 添加类型转换器和格式化器
  31. * @author fxbin
  32. * @param registry
  33. */
  34. @Override
  35. public void addFormatters(FormatterRegistry registry) {
  36. registry.addFormatterForFieldType(LocalDate.class, new DateFormatter());
  37. }
  38. /**
  39. * 跨域支持
  40. * @author fxbin
  41. * @param registry
  42. */
  43. @Override
  44. public void addCorsMappings(CorsRegistry registry) {
  45. registry.addMapping("/**")
  46. .allowedOrigins("*")
  47. .allowCredentials(true)
  48. .allowedMethods("GET", "POST", "DELETE", "PUT")
  49. .maxAge(3600 * 24);
  50. }
  51. /**
  52. * 添加静态资源--过滤swagger-api (开源的在线API文档)
  53. * @author fxbin
  54. * @param registry
  55. */
  56. @Override
  57. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  58. //过滤swagger
  59. registry.addResourceHandler("swagger-ui.html")
  60. .addResourceLocations("classpath:/META-INF/resources/");
  61. registry.addResourceHandler("/webjars/**")
  62. .addResourceLocations("classpath:/META-INF/resources/webjars/");
  63. registry.addResourceHandler("/swagger-resources/**")
  64. .addResourceLocations("classpath:/META-INF/resources/swagger-resources/");
  65. registry.addResourceHandler("/swagger/**")
  66. .addResourceLocations("classpath:/META-INF/resources/swagger*");
  67. registry.addResourceHandler("/v2/api-docs/**")
  68. .addResourceLocations("classpath:/META-INF/resources/v2/api-docs/");
  69. }
  70. /**
  71. * 配置消息转换器--这里我用的是alibaba 开源的 fastjson
  72. * @author fxbin
  73. * @param converters
  74. */
  75. @Override
  76. public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  77. //1.需要定义一个convert转换消息的对象;
  78. FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
  79. //2.添加fastJson的配置信息,比如:是否要格式化返回的json数据;
  80. FastJsonConfig fastJsonConfig = new FastJsonConfig();
  81. fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat,
  82. SerializerFeature.WriteMapNullValue,
  83. SerializerFeature.WriteNullStringAsEmpty,
  84. SerializerFeature.DisableCircularReferenceDetect,
  85. SerializerFeature.WriteNullListAsEmpty,
  86. SerializerFeature.WriteDateUseDateFormat);
  87. //3处理中文乱码问题
  88. List<MediaType> fastMediaTypes = new ArrayList<>();
  89. fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
  90. //4.在convert中添加配置信息.
  91. fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
  92. fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
  93. //5.将convert添加到converters当中.
  94. converters.add(fastJsonHttpMessageConverter);
  95. }
  96. /**
  97. * 添加自定义的拦截器
  98. * @author fxbin
  99. * @param registry
  100. */
  101. @Override
  102. public void addInterceptors(InterceptorRegistry registry) {
  103. registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
  104. }
  105. }
  106. /**
  107. * 拦截器
  108. * @author fxbin
  109. * @version v1.0
  110. * @since 2018/11/7 1:25
  111. */
  112. class MyInterceptor extends HandlerInterceptorAdapter {
  113. @Override
  114. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  115. System.out.println("Interceptor preHandler method is running !");
  116. return super.preHandle(request, response, handler);
  117. }
  118. @Override
  119. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  120. System.out.println("Interceptor postHandler method is running !");
  121. super.postHandle(request, response, handler, modelAndView);
  122. }
  123. @Override
  124. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  125. System.out.println("Interceptor afterCompletion method is running !");
  126. super.afterCompletion(request, response, handler, ex);
  127. }
  128. @Override
  129. public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  130. System.out.println("Interceptor afterConcurrentHandlingStarted method is running !");
  131. super.afterConcurrentHandlingStarted(request, response, handler);
  132. }
  133. }

— end —

如有问题,请在评论区留言交流,谢谢

发表评论

表情:
评论列表 (有 0 条评论,255人围观)

还没有评论,来说两句吧...

相关阅读