JavaWeb 亂碼問題終極解決方案!
對於一些老司機而言,其實並不太容易遇到亂碼問題,但是對於一些新手來說,亂碼幾乎是家常便飯,而且每當亂碼時,網上搜了一大堆解決方案,發現自己的問題還是沒能解決,其實這就是平時研究代碼不求甚解導致的,亂碼問題,也要去分析,然後才能對癥下藥,才能藥到病除。
整體思路
首先出現亂碼之後,要先去確認亂碼的地方,當一個網頁上出現亂碼,有可能是瀏覽器顯示問題,也有可能是 Java 編碼問題,也有可能數據庫中的數據本身就是亂碼的,所以我們要做的第一件事就是確認亂碼發生的位置,縮小 bug 範圍,通過打印日誌或者 debug 首先去確認亂碼發生的位置,然後再去進一步解決,一般來說,亂碼的原因大致上可以分為兩類:
- 請求亂碼
- 響應亂碼
請求亂碼,可能是因為參數放在 URL 地址中亂碼,也有可能是參數放在請求體中亂碼,不同傳參方案也對應了不同的亂碼解決方案。如果是響應亂碼,那麽原因就會比較多了,一般來說,有如下幾種可能的原因:
- 數據庫本身亂碼
- 數據在 Java 代碼中亂碼
- 數據在瀏覽器顯示的時候亂碼
- 數據在從 Java 應用傳到數據庫的過程中亂碼
對於不同的亂碼原因,會有不同的解決方案,對癥下藥,才能藥到病除,所以當出現亂碼時,大家要做的第一件事就是分析亂碼發生的原因,找到原因了,才能找到解決方案。
基本原則
發生亂碼是因為各自編碼不同導致的,所以,大家首先要有一個良好的開發習慣,項目編碼,文件編碼都要統一起來,松哥有個同事就因為 Freemarker 亂碼,找了半天沒找到原因,後來在松哥建議下修改了項目編碼,亂碼問題才解決了,一般來說,公司制度稍微成熟一些,都會對項目編碼,文件編碼有硬性規定的。在Eclipse 中,設置項目編碼方式如下(工程的編碼要提前設置,如果項目已經開發一半再去設置,已有的中文就會亂碼):
Window->Preferences->General
然後對於 JSP 文件也需要提前設置好編碼方式,如下:
這是在 Eclipse 中設置文件編碼,如果是在 IntelliJ IDEA中,則不需要設置JSP文件編碼,因為默認就是 UTF-8,只需要提前設置下工程編碼即可:
除了開發工具的編碼,數據庫的編碼也要統一,一般來說,主要是設置一下數據庫的編碼和數據表的編碼,如下:
設置數據庫編碼:
CREATE DATABASE `vhr` DEFAULT CHARACTER SET utf8;
設置數據表編碼:
DROP TABLE IF EXISTS `adjustsalary`; CREATE TABLE `adjustsalary` ( `id` int(11) NOT NULL AUTO_INCREMENT, `eid` int(11) DEFAULT NULL, PRIMARY KEY (`id`), ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
這些是準備工作,這些工作做好了,還是有可能會遇到亂碼問題,接下來我們就具體問題具體分析。
請求亂碼
請求亂碼,就是說數據在瀏覽器中顯示是正常的,但是傳到 Java 後端之後,就亂碼了,這種亂碼一般來說,分為兩種:
- 參數放在 URL 地址中導致的亂碼
- 參數放在請求體中導致的亂碼
兩種亂碼原因,對應了兩種不同的解決方案。分別來看。
URL 地址中的參數亂碼
這種亂碼主要發生在 GET 請求中,因為在 GET 請求中我們一般通過 URL 來傳遞參數,這個問題可以在代碼中解決,但是太過於麻煩,因此一般我們直接在Tomcat配置中解決,修改 Tomcat的conf/server.xml 文件,修改 URL 編碼格式,如下:
這樣就可以搞定 URL 地址中的參數亂碼。
請求體中的參數亂碼
請求體中的參數亂碼,我們可以在解析參數之前通過設置 HttpServletRequest 的編碼來解決,如下:
request.setCharacterEncoding("UTF-8");
但是一樣也太過於麻煩,所以如果是普通的 Servlet/JSP 項目,我們就可以直接定義一個過濾器來處理,如下:
public class EncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
}
過濾器配置:
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.sang.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在工程編碼和JSP/HTML編碼都沒問題的情況下,請求亂碼基本上就是這兩種情況。
響應亂碼
如果在瀏覽器上加載頁面看到了亂碼,大家首先要確認在從服務端往瀏覽器寫數據的前一刻,這個數據還沒有亂碼(即數據庫中查詢出來的數據是OK的,沒有發生亂碼的問題),那麽對於這種亂碼,我們只需要設置響應數據的 ContentType 就可以了,如下:
response.setContentType("text/html;charset=UTF-8");
如果從數據庫中查詢出來的數據就是亂碼的,那麽就需要去確認數據庫中的編碼是否 OK 。
框架處理
前面提到的方案,都是在 Servlet/JSP 項目中我們可以采用的方案,在 SSM 框架中當然也可以使用,但是,SpringMVC 框架本身也提供了一個過濾器,我們可以借用這個過濾器更加高效的解決響應亂碼問題,如下:
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
當然,上面這段配置並不能代替 Tomcat 中 conf/server.xml 中的編碼配置,如果是在 Spring Boot 中,配置可以更加簡單,只需要在 application.properties 中添加如下配置即可:
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.force-request=true
spring.http.encoding.force-response=true
其他亂碼
其他亂碼主要是指使用一些第三方框架導致的亂碼,例如使用 Alibaba 的 fastjson,開發者就需要在配置 HttpMessageConverter 時指定編碼格式,否則就有可能出現亂碼,這種第三方框架的亂碼松哥沒法窮舉,大夥在使用時需要註意看官方文檔,fastjson 的 HttpMessageConverter 配置如下:
@Bean
FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setCharset(Charset.forName("UTF-8"));
converter.setFastJsonConfig(config);
converter.setDefaultCharset(Charset.forName("UTF-8"));
return converter;
}
一個隱蔽的亂碼
除了前面介紹的這幾種亂碼之外,還有一個比較隱蔽的亂碼,容易被很多初學者忽略的地方,就是數據在從 Java 應用傳遞到 MySQL 的過程中,發生了亂碼,這種問題一般在 Windows 上不易發生,如果數據庫裝在 Linux 上,則這個問題就很容易發生,數據在代碼中命名沒有亂碼,存到 MySQL 上就亂碼了,但是如果直接使用 Navicat 等工具往 MySQL 上存儲數據,又不會亂碼,或者 MySQL 中數據沒有亂碼,但是用 Java 查詢出來就亂碼了,這種都是數據在 應用 和 數據庫 之間傳遞時發生了亂碼,解決方式很簡單,在數據庫連接地址上指定編碼即可,如下:
db.url=jdbc:mysql:///yuetong?useUnicode=true&characterEncoding=UTF-8
大致就這些,還有一些非常偶爾的情況可能會用到 @RequestMapping 註解中的 produces 屬性,在這裏指定數據類型即可。
好了,差不多就這些,下次有人問你為啥我的又亂碼了,直接把這篇文章甩給他。大夥有什麽解決亂碼的獨門密器也可以一起來討論。
JavaWeb 亂碼問題終極解決方案!