本文作者:忧郁的马赛克
本文地址:http://1t.click/cDY
在这篇文章的最后,松哥提了一句,“还有一些非常偶尔的情况可能会用到 @RequestMapping 注解中的 produces 属性”,但是对于这个问题并没有展开讲,有的小伙伴可能还是会遇到这个问题,因此今天就来说一说这个话题。
本文并不是介绍 @ResponseBody 注解,也不是中文乱码问题的大汇总笔记,这些网上都有很多内容了。这边仅对几年前,一个卡壳了挺久时间的问题的解决过程做一个记录,以警惕自己,达到自醒得目的。
@ResponseBody 注解不用多介绍了,用过 SpringMVC 的同学都很熟了, @ResponseBody 将内容或对象作为 HTTP 响应正文返回,使用 @ResponseBody 将会跳过视图处理部分,而是调用适合的 HttpMessageConverter ,将返回值写入输出流。在日常工作中,通常使用封装好的 ViewModel 进行后台数据的返回,一切正常。但一次在使用 @ResponseBody 进行返回 String 数据的时候,竟会出现中文乱码。
编程的过程免不了遇到各种问题,而遇到问题然后解决问题的这个过程我认为是最让人兴奋的事情。越棘手的问题,解决以后带来的快感也越大(PS:当然解决不了的话,就会越烦躁。。),还是言归正传,谈一下解决错误的过程。
最早我一直以为 Spring 配置一下编码过滤器就可以解决任何中文乱码问题,恩,确实一直是这样设置着,然并卵,代码如下
<filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param></filter><filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>直接返回 String 会乱码,而返回 ViewAndModel 的那个不会乱码,这是为什么?
其实也可以说是 SpringMVC 的一个 bug ,SpringMVC 有一系列 HttpMessageConverter 去处理用 @ResponseBody 注解的返回值,如返回 VM 则使用 MappingJacksonHttpMessageConverter 或者其他的 HttpMessageConverter ,若返回 String ,则使用 StringHttpMessageConverter ,这个 convert 使用的是字符集是 iso-8859-1 ,而且是 final 的,部分源码如下:
public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");既然是 String 有问题,那自然就直接从适配器 AnnotationMethodHandlerAdapter 的字符串解析器 StringHttpMessageConverter 入手,设置编码类型即可?NO!
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/plain;charset=UTF-8</value> </list> </property> </bean>那万金油 response.setContentType("text/html; charset=UTF-8"); 呢?NO!
那么重写 StringHttpMessageConverter 应该可以了吗?NO!
上面方法都不行,就尝试着各种百度,说法多种多样,但答案还是:NO!
看来我们得从源头开始再理一遍,既然问题在解析器,那么可以从配置文件配置解析器入手。mvc-config.xml 文件从上到下:控制层扫描、国际化配置、文件上传表单解析器、自定义拦截器、视图配置。好像都不是,继续往下,一些 HandlerMapping 和 HandlerAdapter ,还有一句 <mvc:annotation-driven/>。
网上查阅了一下资料,果然发现问题其实就在这句 <mvc:annotation-driven/>。
<mvc:annotation-driven/> 是一种简写模式,它会自动注册 DefaultAnnotationHandlerMapping 与 AnnotationMethodHandlerAdapter 两个 bean ,是 SpringMVC 为 @Controller 分发请求所必须的,并且提供了其他一些支持。
上面使用为 StringHttpMessageConverter 设置编码模式其实正常是有效的,但是在使用了 <mvc:annotation-driven/> 语句后,再次显示声明其他 bean ,可能就无效了。
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8" /> </bean> </mvc:message-converters></mvc:annotation-driven>除了使用上面那种方案之外,还可以使用下面的:
@RequestMapping(value="/test", method=RequestMethod.POST, produces="text/html;charset=UTF-8")注意:既然使用了配置 <mvc:annotation-driven> ,还是建议在该配置内部进行处理。
好了,大家有没有 Get 到知识点呢?