SpringMVC响应数据中文乱码解决方案详解

SpringMVC响应数据中文乱码解决方案详解

一、中文乱码起源?

先看看乱码效果图,如下图所示:

在这里插入图片描述

上图中“???”就是中文不能正常显示,出现乱码了。为什么会导致中文乱码?

打开浏览器的开发者工具,按F12也可以打开;查看response header(响应头) 的Content-Type属性值,如下图所示:

在这里插入图片描述
原来在springMVC中Content-Type编码值默认是ISO-8859-1,而浏览器一般以UTF-8编码渲染页面的。所以中文乱码就发生了。

二、怎样解决中文编码?

这里就提出几种方案,包括最常见的错误方案。

1. (错误方案)在Controller中修改响应头Content-Type的值为"text/html;charset=utf-8"。如下图所示:

在这里插入图片描述
修改之后,乱码问题还是没有解决;主要是因为SpringMVC内部会调用MessageConverter转换器将你设置的编码覆盖成默认的编码。覆盖大致流程:当在controller中使用return时,会触发returnValueHandlers方法,源码如下:

/**
*类RequestMappingHandlerAdapter中295-303行
**/
public void setReturnValueHandlers(@Nullable List<HandlerMethodReturnValueHandler> returnValueHandlers) {
		if (returnValueHandlers == null) {
			this.returnValueHandlers = null;
		}
		else {
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
			this.returnValueHandlers.addHandlers(returnValueHandlers);
		}
	}

以上源码可知,当return非null是会触发HandlerMethodReturnValueHandlerComposite类的addHandlers。在这就不深入其他类了,继续分析类RequestMappingHandlerAdapter中的构造函数。源码如下:

/**
*类RequestMappingHandlerAdapter中195-209行
**/
public RequestMappingHandlerAdapter() {
		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
		stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

		this.messageConverters = new ArrayList<>(4);
		this.messageConverters.add(new ByteArrayHttpMessageConverter());
		this.messageConverters.add(stringHttpMessageConverter);
		try {
			this.messageConverters.add(new SourceHttpMessageConverter<>());
		}
		catch (Error err) {
			// Ignore when no TransformerFactory implementation is available
		}
		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
	}

以上代码可以看到初始化了类StringHttpMessageConverter,而Content-Type的默认编码值。在类StringHttpMessageConverter中默认设置。源码如图所示:
在这里插入图片描述

总结:所以直接在Controller中设置响应头的Content-Type是没用的,因为还是会被MessageConverter的编码默认值给覆盖;

2. 在Controller中的@RequestMapping注解添加produces = “text/html;charset=utf-8”。代码如下所示:
@RequestMapping(value = "test",produces = "text/html;charset=utf-8")
    public String testUser(HttpServletRequest req, HttpServletResponse res){
//        res.setContentType("text/html;charset=utf-8");
        long time=System.currentTimeMillis();
        return String.format("<div><h2>测试成功。</h2><p>System.currentTimeMillis:%s</p></div>",time);
    }

实现效果如下图所示:

在这里插入图片描述

通过以上设置,可以看到中文乱码不存在了。问题倒是解决了,但是不可能每个Controller都给这样设置吧,如果编码换成了其他呢,那改也很麻烦。

3. 自定义类去实现接口BeanPostProcessor,并重写接口方法postProcessBeforeInitialization、postProcessAfterInitialization;然后将自定义类通过bean标签装配到IOC容器中。源码如下:
//自定义类DefineCharSet
package com.comm;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.lang.Nullable;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

public class DefineCharSet implements BeanPostProcessor {
    //实例化之前调用
    @Nullable
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    //实例化之后调用
    @Nullable
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof StringHttpMessageConverter){
            MediaType mediaType = new MediaType("text", "html", Charset.forName("UTF-8"));
            List<MediaType> types = new ArrayList<MediaType>();
            types.add(mediaType);
            ((StringHttpMessageConverter) bean).setSupportedMediaTypes(types);
        }
        return bean;
    }
}
<!--装配自定义类DefineCharSet-->
<bean class="com.comm.DefineCharSet"/>

OK,现在万事大吉,再也不用通过第2种方式去解决乱码了。

猜你喜欢

转载自blog.csdn.net/u012475786/article/details/89501318
今日推荐