解决Spring MVC中文乱码的编码配置
作者:福
SpringMVC配置编码方式
SpringMVC的中文乱码问题其实已经不是什么问题了,无非就是配置编码方式->解决问题。
但是由于SpringMVC可以通过:xml方式配置、Servlet3.0方式配置,以及是否使用@EnableWebMvc等,不同配置方式下,解决中文乱码问题的方案有所不同。
xml配置的方式
xml配置方式的解决方案最简单:
<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>
在web.xml文件中加编码过滤器,并强制过滤器对Request和Response都生效,可以解决request请求、以及response返回参数中的中文乱码问题。
但是返回体,也就是response body中的中文乱码问题,以上过滤器方案无法解决。
Response body中的中文乱码问题需要在spring MVC中增加以下配置:
<mvc:annotation-driven > <!--设置响应输出字符集--> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=utf-8</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
前面一篇文章[Spring MVC 五:DispatcherServlet初始化之 mvc:annotation-driven] 分析过<mvc:annotation-driven />标签的解析过程,该标签在创建RequestMappingHandlerAdapter的过程中,会读取到xml文件中messageConverters的定义并设置到RequestMappingHandlerAdapter对象的messageConverters属性中并最终在DispatcherServlet处理请求的过程中生效。
Servlet3.0配置
Servlet3.0的配置方式,是指通过WebApplicationInitializer接口完成SpringMVC配置的方式。
可以通过接口方法getServletFilters增加编码过滤器:
public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { // return null; return new Class[] {RootConfiguration.class}; } @Override protected Class<?>[] getServletConfigClasses() { // return null; return new Class[] {MvcConfiguration.class,CommonConfiguration.class}; } @Override protected String[] getServletMappings() { return new String[] {"/"}; } @Override protected Filter[] getServletFilters() { // ShallowEtagHeaderFilter shallowEtagHeaderFilter = new ShallowEtagHeaderFilter(); // shallowEtagHeaderFilter.setWriteWeakETag(true); CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("utf-8"); characterEncodingFilter.setForceEncoding(true); return new Filter[]{characterEncodingFilter}; } }
以上方式增加过滤器后,可以解决request和response请求及返回参数中的中文编码问题。
但是无法解决response body的中文乱码问题。
由于WebApplicationInitializer接口并没有提供任何关于messageConverters的接口,看了很多遍源码也并没有找到可以配置的地方,网上也没有找到相关解决方案......所以解决这个问题还是费了很多周折。
由于我们已经知道,response body的中文乱码问题最终是通过RequestMappingHandlerAdapter对象的messageConverters解决的,所以还是想通过定制化RequestMappingHandlerAdapter的初始化过程、设置其messageConverters的方式解决问题。
如果没有定制化处理的话,DispatcherServlet在初始化的过程中是在initStrategies方法中创建DispatcherServlet.properties文件中默认的RequestMappingHandlerAdapter,是通过反射机制直接new出来的,最终会调用到RequestMappingHandlerAdapter的默认构造器:
public RequestMappingHandlerAdapter() { this.messageConverters = new ArrayList<>(4); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(new StringHttpMessageConverter()); try { this.messageConverters.add(new SourceHttpMessageConverter<>()); } catch (Error err) { // Ignore when no TransformerFactory implementation is available } this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); }
默认构造器会直接new一个StringHttpMessageConverter()加进来,不修改的话StringHttpMessageConverter的默认字符集是ISO_8859_1,一定会出现中文乱码问题:
public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
所以我们必须找到某种方式可以定制化RequestMappingHandlerAdapter的初始化过程。
继续分析源码:
private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; if (this.detectAllHandlerAdapters) { // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts. Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerAdapters = new ArrayList<>(matchingBeans.values()); // We keep HandlerAdapters in sorted order. AnnotationAwareOrderComparator.sort(this.handlerAdapters); } } else { try { HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); this.handlerAdapters = Collections.singletonList(ha); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerAdapter later. } } // Ensure we have at least some HandlerAdapters, by registering // default HandlerAdapters if no other adapters are found. if (this.handlerAdapters == null) { this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerAdapters declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
发现initHandlerAdapters方法首先会从Spring容器中获取HandlerAdapter!
我们是否有办法定制一个HandlerAdapter、加入到Spring容器中?Spring当然给我们提供了这种机会,回想一下@Configuration+@Bean注解,是否就可以解决?
在MvcConfig文件中增加如下代码:
@Configuration @ComponentScan({"org.example.controller"}) public class MvcConfiguration { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/pages/"); viewResolver.setSuffix(".jsp"); return viewResolver; } //定制RequestMappingHandlerAdapter @Bean public RequestMappingHandlerAdapter handlerAdapter(){ RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter(); List<HttpMessageConverter<?>> messageConverters; messageConverters = new ArrayList<>(4); messageConverters.add(new ByteArrayHttpMessageConverter()); StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8")); messageConverters.add(stringHttpMessageConverter); try { messageConverters.add(new SourceHttpMessageConverter<>()); } catch (Error err) { // Ignore when no TransformerFactory implementation is available } messageConverters.add(new AllEncompassingFormHttpMessageConverter()); handlerAdapter.setMessageConverters(messageConverters); return handlerAdapter; }
参考RequestMappingHandlerAdapter默认构造器的代码,修改其中StringHttpMessageConverter的创建过程、设置其默认字符集为UTF-8......验证后发现,问题已解决!
使用@EnableWebMvc
由于@EnableWebMvc是必须和@configuration配合使用的,所以,一定会存在配置类。这种情况下,配置类实现WebMvcConfigurer、通过扩展extendMessageConverters方法解决:
@Configuration @EnableWebMvc @ComponentScan({"org.example.controller"}) public class MvcConfiguration implements WebMvcConfigurer{ public MvcConfiguration(){ System.out.println("mvc configuration constructor..."); } // 通过@EnableWebMVC配置的时候起作用, @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { for(HttpMessageConverter httpMessageConverter:converters){ if(StringHttpMessageConverter.class.isAssignableFrom(httpMessageConverter.getClass())){ ((StringHttpMessageConverter)httpMessageConverter).setDefaultCharset(Charset.forName("UTF-8")); } } } }
以上就是解决Spring MVC中文乱码的编码配置的详细内容,更多关于Spring MVC中文乱码解决的资料请关注脚本之家其它相关文章!