`

SpringMVC使用@ResponseBody输出字符串时遇到的乱码问题及解决办法

 
阅读更多

我们需要保持编码或者解码两头,所使用的字符集转换方向需要正好相反:使用 GBK --> UTF-8 与 UTF-8 --> GBK。由于Java采用了UTF-8编码,所以编码解码均以UTF-8为中介。 
对于翻译而言,就是先相当于: 先 中译英,对应的解码,反过来就是 英译中。 

遇到乱码问题,通常的检查项包括: 
1. 编辑器保存文件的字符集; 
2. 数据库的字符集; 
3. 应用服务器或者Web服务器处理字符串采用的字符集 
4. JSP对于字符集声明 
5. Servlet过滤器,以及MVC框架拦截器对于字符集的处理 
6. 其它涉及字符集处理的环节 

检查各个环节,统一按UTF-8设置。推断我这次碰到的问题属于上述第6中情况。 

因为是通过SpringMVC提供的注解@ResponseBody来返回一个JSON字符串,然后在客户端上解析JSON(现如今以JSON作为数据交换格式貌似越来越时髦了,客户端我用的比较多的是jqGrid或者ExtJS)。 

Controller代码如下: 

Java代码  收藏代码
  1. @Controller  
  2. @RequestMapping("/*")  
  3. public class HelloController {  
  4.     private transient final Log log = LogFactory.getLog(HelloController.class);  
  5.       
  6.     @Autowired  
  7.     private UserManager mgr = null;  
  8.       
  9.     @RequestMapping(value="hello_list.do", method = RequestMethod.POST)  
  10.     @ResponseBody  
  11.     public String helloList() {  
  12.         StringBuilder str = new StringBuilder("{totalProperty:100,root:[");  
  13.           
  14.         List<User> users = mgr.getUsers();  
  15.         for (User user : users) {  
  16.             str.append("{id: ").append(user.getId());  
  17.             str.append(", name:'").append(user.getLastName());  
  18.             str.append("', descn:'").append(user.getFullName()).append("'},");  
  19.         }  
  20.         str.append("{id:4, name:'생활', descn:'Китай'},");  
  21.         str.append("{id:5, name:'tchen8', descn:'中文'}]}");  
  22.           
  23.         log.info(str.toString());  
  24.           
  25.         return str.toString();  
  26.     }  
  27.   
  28. }  



在Spring配置文件里,默认如下: 

Xml代码  收藏代码
  1. <!-- Enables the Spring MVC @Controller programming model -->  
  2. <mvc:annotation-driven />  



调试程序,控制台输出日志看到是中文,但是在firebug中看到的服务器端送过来的字符串是???? (如果是 "口口口"这样的输出,需要先排除是否为系统的字体缺失),于是判断是服务器最后往端口写字符串流的时侯字符集不对。 

通过调试跟踪Spring的源码,声明@ResponseBody时,Spring会通过AnnotationMethodHandlerAdapter去寻找对应的HttpMessageConverter, 我们这里声明返回的类型是String,于是对应StringHttpMessageConverter。通过实验,猜测这个StringHttpMessageConverter也就是<mvc:annotation-driven />触发的默认的字符串转换工作类。 

比较不幸的是,StringHttpMessageConverter所使用的默认字符集是ISO-8859-1 

Java代码  收藏代码
  1. ......  
  2. public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {  
  3.   
  4.     public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");  
  5. ......  



这里不得不提的是与StringHttpMessageConverter 同级的类MappingJacksonHttpMessageConverter,天知道是什么原因:同一个作者,对于这两个类,默认字符集一个是ISO-8859-1,一个是UTF-8。 

既然事已如此,那就想办法把这个地方用到的ISO-8859-1也改成UTF-8了。有两个思路: 
1. 替换默认字符集; 
2. 替换StringHttpMessageConverter 

搜索了一下,先看到这个解决办法: 
http://forum.springsource.org/showthread.php?t=81858 
这里提供的是使用一个所谓的ConfigurableStringHttpMessageConverter来替代StringHttpMessageConverter,基本的思路技术是:由于StringHttpMessageConverter中的默认字符集变量声明为final,无法直接通过继承去覆盖,那就把StringHttpMessageConverter照抄一遍,构造函数中新增一个代表字符集的输入参数,然后在配置文件里面通过构造方法注入UTF-8。在配置文件中,将这个Bean声明在<mvc:annotation-driven />前面,从而能够先于StringHttpMessageConverter被Spring识别和注入。 

但是这个方法多少有些蛮干的味道,基于它简化的一个版本可以如下,即通过继承StringHttpMessageConverter,然后在子类中注入我们想要的字符集配置: 

Java代码  收藏代码
  1. public class MyStringHttpMessageConverter extends StringHttpMessageConverter {  
  2.   
  3.     public MyStringHttpMessageConverter(Charset defaultCharset) {  
  4.         List<MediaType> mediaTypeList = new ArrayList<MediaType>();  
  5.         mediaTypeList.add(new MediaType("text", "plain", defaultCharset));  
  6.         mediaTypeList.add(MediaType.ALL);  
  7.         super.setSupportedMediaTypes(mediaTypeList);  
  8.     }  
  9.       
  10. }  



Bean的配置依然类似: 

Xml代码  收藏代码
  1. ...  
  2. ...  
  3.     <beans:bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">  
  4.         <beans:property name="messageConverters">  
  5.             <util:list>  
  6.                 <beans:bean id="stringHttpMessageConverter" class="org.tchen8.myapp.common.ConfigurableStringHttpMessageConverter">  
  7.                     <beans:constructor-arg value="UTF-8" />  
  8.                 </beans:bean>  
  9.             </util:list>  
  10.         </beans:property>  
  11.     </beans:bean>  
  12.   
  13.     <!-- Enables the Spring MVC @Controller programming model -->  
  14.     <mvc:annotation-driven />  
  15. ...  
  16. ...  



上面的办法是以属性注入的方式,替换了默认的字符集,但为此也需要把converter替换。 



另外一个比较简洁的办法,则不需要自己写converter类,而是直接通过属性注入,修改StringHttpMessageConverter的默认配置。 

Xml代码  收藏代码
  1. ...  
  2.     <beans:bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">    
  3.         <beans:property name="messageConverters">    
  4.             <util:list>    
  5.                 <beans:bean class="org.springframework.http.converter.StringHttpMessageConverter">    
  6.                     <beans:property name="supportedMediaTypes">    
  7.                         <util:list>  
  8.                             <beans:value>text/html;charset=UTF-8</beans:value>  
  9.                         </util:list>    
  10.                     </beans:property>    
  11.                 </beans:bean>    
  12.             </util:list>    
  13.         </beans:property>    
  14.     </beans:bean>  
  15. ...  


上面的这个办法,实际上通过setSupportedMediaTypes方法,其实也就是StringHttpMessageConverter在类注释中所提到的办法: 



如果再多看一下StringHttpMessageConverter的源码,可以到它的父类中AbstractHttpMessageConverter有这么个方法: 

Java代码  收藏代码
  1. ...  
  2.     /** 
  3.      * Returns the default content type for the given type. Called when {@link #write} 
  4.      * is invoked without a specified content type parameter. 
  5.      * <p>By default, this returns the first element of the 
  6.      * {@link #setSupportedMediaTypes(List) supportedMediaTypes} property, if any. 
  7.      * Can be overridden in subclasses. 
  8.      * @param t the type to return the content type for 
  9.      * @return the content type, or <code>null</code> if not known 
  10.      */  
  11.     protected MediaType getDefaultContentType(T t) {  
  12.         List<MediaType> mediaTypes = getSupportedMediaTypes();  
  13.         return (!mediaTypes.isEmpty() ? mediaTypes.get(0) : null);  
  14.     }  
  15. ...  



注释中写的明白:"Can be overridden in subclasses." 那就不必客气了。于是我们大概也能有如下的做法: 

Java代码  收藏代码
  1. ...  
  2. public class MyStringHttpMessageConverter2 extends StringHttpMessageConverter {  
  3.       
  4.     private static final MediaType utf8 = new MediaType("text", "plain", Charset.forName("UTF-8"));   
  5.   
  6.     @Override  
  7.     protected MediaType getDefaultContentType(String dumy) {  
  8.         return utf8;  
  9.     }  
  10.       
  11. }  
  12. ...  


对应的配置: 

Xml代码  收藏代码
  1. <beans:bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">  
  2.     <beans:property name="messageConverters">  
  3.         <util:list>  
  4.             <beans:bean id="myStringHttpMessageConverter2" class="org.tchen8.myapp.common.MyStringHttpMessageConverter2" />  
  5.         </util:list>  
  6.     </beans:property>  
  7. </beans:bean>   
  8.   
  9. <!-- Enables the Spring MVC @Controller programming model -->  
  10. <mvc:annotation-driven />  



以上的几个方法,都能解决@ResponseBody导致的乱码问题,虽然StringHttpMessageConverter将来确实有可能把默认字符集修改成UTF-8,从而导致上述功夫最后变成白忙活。但也确实感谢有这么个小阻碍,迫使自己去分析问题寻找答案。收获不在于结果,而在过程吧 

分享到:
评论

相关推荐

    SpringMVC使用@ResponseBody.rar

    当`@ResponseBody`方法抛出异常时,Spring MVC会自动捕获并处理这些异常。默认情况下,它会将异常信息转换为HTTP状态码和错误消息。可以通过配置`@ExceptionHandler`方法或全局异常处理器来自定义异常处理逻辑。 5...

    彻底根治Spring @ResponseBody JavaScript HTML特殊字符

    在开发Web应用时,我们经常会遇到一个问题:当使用Spring MVC的`@ResponseBody`注解将后端处理结果直接转化为HTTP响应体时,如果这个结果中包含HTML特殊字符,如尖角号、引号、按位与符号等,浏览器可能会误解析,...

    springMVC3使用@ResponseBody向浏览器返回 json,注意区分jar包前缀

    --处理 @ResponseBody 中文乱码问题 --&gt; &lt;bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"&gt; &lt;property name="messageConverters"&gt; &lt;list&gt; &lt;!-- Support...

    SpringMVC中解决@ResponseBody注解返回中文乱码问题

    然而,当返回的字符串中包含中文字符时,如果没有正确设置编码,就可能导致乱码问题。本文将详细介绍如何解决SpringMVC中`@ResponseBody`注解返回中文乱码的问题。 首先,我们可以尝试使用`@RequestMapping`注解的`...

    SpringMVC中使用@RequestBody,@ResponseBody注解实现Java对象和XML/JSON数据自动转换(下)

    在Spring MVC框架中,`@RequestBody` 和 `@ResponseBody` 是两个非常重要的注解,它们在处理HTTP请求和响应时起到了关键作用。本篇文章将详细解释这两个注解的工作原理、使用场景以及如何实现Java对象与XML/JSON数据...

    Springmvc 4.x利用@ResponseBody返回Json数据的方法

    在Spring MVC 4.x版本中,开发者可以使用@ResponseBody注解来简化控制器层向客户端返回JSON格式数据的过程。 ### @ResponseBody注解使用方法 @ResponseBody注解可以作用于方法上,它会指示Spring MVC框架将该方法...

    SpringMVC中使用@RequestBody,@ResponseBody注解实现Java对象和XML/JSON数据自动转换(上)

    例如,如果请求体是JSON格式,Spring会使用默认的`HttpMessageConverter`(如`MappingJackson2HttpMessageConverter`)将JSON字符串转换为Java对象。 ```java @PostMapping("/saveUser") public ResponseEntity ...

    springMVC解决中文乱码

    解决 SpringMVC 中的中文乱码问题可以通过多种方法来实现,包括在 web.xml 文件中配置字符编码、使用注解配置字符编码、使用 CharacterEncodingFilter 或者自定义 Filter 等。这些方法都可以有效地解决中文乱码问题...

    springmvc+国际化i18N+springmvc验证+jetbrick-template使用+@responsebody+谷歌guava

    springmvc+国际化i18N+springmvc验证+jetbrick-template使用+@responsebody+谷歌guava: 1)围绕springmvc做的国际化 2)围绕springmvc做的验证 3)使用的jetbrick-template模板引擎 ……

    SpringMVC解决乱码

    然而,在使用SpringMVC时,可能会遇到乱码问题,例如在使用@ResponseBody注解时返回的数据出现乱码。在本文中,我们将讨论解决SpringMVC乱码问题的两种方法。 方法一:配置AnnotationMethodHandlerAdapter 在...

    springMvc注解之 ResponseBody和 RequestBody.docx

    在使用`@RequestBody`接收多层嵌套的JSON对象时,注意前端需要设置`contentType`为`"application/json"`,并且使用`JSON.stringify()`将数据转换为字符串。这是因为HTTP请求默认可能不包含正确的Content-Type头,...

    springMvc注解之@ResponseBody和@RequestBody详解

    Spring MVC注解之@ResponseBody和@RequestBody详解 在Spring MVC框架中,@ResponseBody和@RequestBody是两个非常重要的注解,它们分别用于处理HTTP请求响应体和请求体的序列化和反序列化。下面,我们将详细介绍这两...

    详解SpringMVC注解@initbinder解决类型转换问题

    在使用 SpringMVC 框架时,经常会遇到表单中的日期字符串和 JavaBean 的 Date 类型的转换问题。 SpringMVC 默认不支持这个格式的转换,因此需要手动配置,自定义数据的绑定才能解决这个问题。在需要日期转换的 ...

    Spring MVC – Easy REST-Based JSON Services with @ResponseBody

    标题中的“Spring MVC – Easy REST-Based JSON Services with @ResponseBody”是指使用Spring MVC框架构建基于REST的JSON服务,并通过使用`@ResponseBody`注解来简化这一过程。REST(Representational State ...

    SpringMVC解决GET请求时中文乱码的问题.rar

    这个问题主要源于请求参数在URL中编码时使用的是UTF-8以外的字符集,导致服务器接收到的参数解码后出现乱码。本文将深入探讨这个问题,并提供解决方案。 一、问题解析 1. GET请求原理:GET请求的参数通常会附加在...

    SpringMVC使用JSON的Jackson的jar包

    SpringMVC通过`@ResponseBody`注解支持将方法返回的对象直接序列化为JSON,然后发送到客户端。要实现这一功能,需要依赖Jackson库,特别是Jackson的两个核心组件:`jackson-mapper-asl`和`jackson-core-asl`。 1. ...

    SpringMVC Tomcat控制台乱码问题解决方案

    在使用SpringMVC框架开发Web应用程序时,经常会遇到乱码问题,例如在Tomcat控制台中输出的中文字符变成乱码。这是因为Tomcat的控制台默认使用的字符编码是GBK,而SpringMVC框架使用的字符编码是UTF-8。因此,当我们...

    对json格式的数据进行封装和转化,@responsebody注解所需的jar包JSON.rar

    描述中提到的"springmvc进行Jason数据封装转化时可以即使用也是@responsebody",指的是在Spring MVC的控制器(Controller)中,我们可以使用`@ResponseBody`注解配合Jackson库,实现将Java对象直接转换为JSON格式的...

    @responsebody+jetbrick-template-2.x+log+validator + springMVC 验证 注解 所需jar包

    1)spring MVC 中@ResponseBody需要的所有JAR包 2)性能还不错的模板引擎jetbrick-template-2.x 2.x的所需的所有jar包 3)日志jar包:slf4j和logback 所有 4)阿里 druid 连接池jar包 5)mysql数据库链接驱动jar包 6...

    SPRINGMVC 406问题解决方案

    SPRINGMVC 406 问题的解决方案主要包括添加 Jackson 依赖项、启用注解标签、使用 @ResponseBody 注解等。通过这些解决方案,可以有效地解决 SPRINGMVC 406 问题,并提高应用程序的稳定性和可靠性。

Global site tag (gtag.js) - Google Analytics