转载自:http://www.cnblogs.com/shanrengo/p/6429291.html

问题背景

  本文并不是介绍@ResponseBody注解,也不是中文乱码问题的大汇总笔记,这些网上都有很多内容了。这边仅对几年前,一个卡壳了挺久时间的问题的解决过程做一个记录,以警惕自己,达到自醒得目的。  

  @ReponseBody 注解不用多介绍了,用过SpringMVC的同学都很熟了,@ResponseBody 将内容或对象作为 HTTP 响应正文返回,使用@ResponseBody将会跳过视图处理部分,而是调用适合的HttpMessageConverter,将返回值写入输出流。在日常工作中,通常使用封装好的ViewModel进行后台数据的返回,一切正常。但一次在使用@ReponseBody进行返回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>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

  直接返回String会乱码,而返回ViewModel的那个不会乱码,这是为什么?

  其实也可以说是SpringMVC的一个bug,SpringMVC有一系列HttpMessageConverter去处理用@ResponseBody注解的返回值,如返回VM则使用MappingJacksonHttpMessageConverter,若返回String,则使用StringHttpMessageConverter,这个convert使用的是字符集是iso-8859-1,而且是final的,如下:

public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1"); 

  既然是String有问题,那自然就直接从适配器AnnotationMethodHandlerAdapter 的字符串解析器 StringHttpMessageConverter 入手,设置编码类型即可?

<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>
...

  NO!

那万金油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,是spring MVC为@Controllers分发请求所必须的,并且提供了其他一些支持。。。略

  上面使用为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>

扩展:其他解决方案

  除了使用上面那种方案之外,还可以使用下面的: 

  1、完全不使用String返回,直接都通过统一的ViewModel去返回,如ResultModel等(正常也是这么干的,总还要错误信息等吧);
  2、通过@RequestMapping的属性处理,该注解的属性produces用于指定返回的内容类型,这算肯定可以了,代码如下:

@RequestMapping(value="/test", method=RequestMethod.POST, produces="text/html;charset=UTF-8")

  注意:既然使用了配置<mvc:annotation-driven>,还是建议在该配置内部进行处理。

编后语

   上面的问题记录是很多多年前的了,现在翻到博客上,只是为了告诫自己:

  天下文章一大抄,但是抄来抄去,不管是写的人,还是看的人,自己最好都能理解或者亲自去尝试一下;这个乱码问题网上总结太多太多了,不能说是错误的,但是又有几个正确得说清楚呢?

  如果放在现在来解决这个问题,最好的方案还是直接跟一下SpringMVC的源码,简单明了。String类型的返回值处理会进入StringHttpMessageConverter解析器,观察getContentTypeCharset方法的参数,可以看到当前解析编码是text/plain;charset=ISO-8859-1,即设置不成功,设置不成功可以继续看看适配器AnnotationMethodHandlerAdapter的属性设置,一层层反推,总会发现问题的。

  这里奉劝各位兄弟姐妹,网上的东西还是要仔细研究的,不要转来转去的。

《经久不衰的Spring框架:@ResponseBody 中文乱码》(转)的更多相关文章

  1. 转《在浏览器中使用tensorflow.js进行人脸识别的JavaScript API》

    作者 | Vincent Mühle 编译 | 姗姗 出品 | 人工智能头条(公众号ID:AI_Thinker) [导读]随着深度学习方法的应用,浏览器调用人脸识别技术已经得到了更广泛的应用与提升.在 ...

  2. face-api.js:一个在浏览器中进行人脸识别的 JavaScript 接口

    Mark! 本文将为大家介绍一个建立在「tensorflow.js」内核上的 javascript API——「face-api.js」,它实现了三种卷积神经网络架构,用于完成人脸检测.识别和特征点检 ...

  3. TensorFlow.js之安装与核心概念

    TensorFlow.js是通过WebGL加速.基于浏览器的机器学习js框架.通过tensorflow.js,我们可以在浏览器中开发机器学习.运行现有的模型或者重新训练现有的模型. 一.安装     ...

  4. 在Java中直接调用js代码(转载)

    http://blog.csdn.net/xzyxuanyuan/article/details/8062887 JDK1.6版添加了新的ScriptEngine类,允许用户直接执行js代码. 在Ja ...

  5. 第十一章:WEB浏览器中的javascript

    客户端javascript涵盖在本系列的第二部分第10章,主要讲解javascript是如何在web浏览器中实现的,这些章节介绍了大量的脚本宿主对象,这些对象可以表示浏览器窗口.文档树的内容.这些章节 ...

  6. 在Java中直接调用js代码

    JDK1.6版添加了新的ScriptEngine类,允许用户直接执行js代码. 在Java中直接调用js代码 不能调用浏览器中定义的js函数,会抛出异常提示ReferenceError: “alert ...

  7. TensorFlow.js入门(一)一维向量的学习

    TensorFlow的介绍   TensorFlow是谷歌基于DistBelief进行研发的第二代人工智能学习系统,其命名来源于本身的运行原理.Tensor(张量)意味着N维数组,Flow(流)意味着 ...

  8. JavaScript权威指南--WEB浏览器中的javascript

    知识要点 1.客户端javascript window对象是所有客户端javascript特性和API的主要接入点.它表示web浏览器的一个窗口或窗体,并且可以用window表示来引用它.window ...

  9. 解决webkit浏览器中js方法中使用window.event提示未定义的问题

    这实际上是一个浏览器兼容性问题,根源百度中一大堆,简要说就是ie中event对象是全局变量,所以哪里都能使用到,但是webkit内核的浏览器中却不存在这个全局变量event,而是以一个隐式的局部变量的 ...

  10. JS Date当前时间:获取日期时间方法在各浏览器中的差异

    转自:http://www.feiesoft.com/00047/<script type="text/javascript"> // JS Date当前时间获取方法在 ...

随机推荐

  1. LVS之NAT和DR服务脚本

    NAT服务控制脚本 #!/bin/bash # # chkconfig: - 88 12 # description: LVS script for VS/NAT # . /etc/rc.d/init ...

  2. maven 使用记录之修改 maven默认jdk版本

    maven package执行的时候会遇到jdk版本不对的问题 :原因是 maven所指定的jdk版本与项目使用的jdk版本不一致 1.项目属性的 java compiler可以设置 2.直接修改 m ...

  3. faster rcnn流程

    1.执行流程 数据准备 train_net.py中combined_roidb函数会调用get_imdb得到datasets中factory.py生成的imdb 然后调用fast_rcnn下的trai ...

  4. 关于正则表达式的“\b”

    今天刚刚开始看正则表达式就遇到一个十分头疼的问题,原文是这样的: “不幸的是,很多单词里包含hi这两个连续的字符,比如him,history,high等等.用hi来查找的话,这里边的hi也会被找出来. ...

  5. OpenFileDialog对话框Filter属性

    OpenFileDialog对话框的Filter属性说明: 首先说明一个示例,分析一下Filter属性的构成:“ Excel文件|*.xls ”,前面的“Excel文件”成为标签,是一个可读的字符串, ...

  6. 【Android】接入有米广告SDK

    测试:接入有米广告SDK(测试广告). 步骤: 1.注册并登录有米广告. 2.下载相应的SDK,这里我选了第一个[Android广告SDK ],如下图: 3.下好后,根据doc文档步骤进行操作,包括: ...

  7. 微信JSSDK接口,previewImage

    原文:https://www.hackhp.com/801.html 在微信里看过文章的应该知道,文章里的图片点击后可以放大.分享和保存. 然而自己在微信里开发的网页,里面的图片点击后没办法实现这个效 ...

  8. [转]Linux 命令行快捷键

    群里有人问"问个问题,Linux 命令行有没有快捷键一下从行末会到行头?经常敲了很多命令发现忘加 sudo 了,然后把命令删了重新敲一遍". 自己还真不知道怎么操作,只知道历史命令 ...

  9. nfs挂载hdfs,实现云存储

    本来不知道nfs是啥,因为群里的Harry童鞋有个问题,如何把本地目录挂载到hdfs上,搞什么云存储,说那么巧就是那么巧,HDP支持nfs,然后我就照着文档的说明去做,最后弄出来了. 1.修改机器上的 ...

  10. python datetime unix时间戳以及字符串时间戳转换

    将python的datetime转换为unix时间戳 import time import datetime dtime = datetime.datetime.now() ans_time = ti ...