JSP頁(yè)面編碼問(wèn)題研究
作者:
日期: 2005年11月01日
讀者評(píng)分: 9.3
Motivition
曾經(jīng)有一個(gè)網(wǎng)友問(wèn)過(guò)我這樣一個(gè)問(wèn)題:
<%@page contentType="text/html; charset=UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
中國(guó)
</body>
</html>
這個(gè)頁(yè)面在為什么在運(yùn)行的時(shí)候“中國(guó)”會(huì)變成亂碼?
Analysis
Key Step
對(duì)于上面問(wèn)題的分析需要從整個(gè)JSP頁(yè)面請(qǐng)求的生命周期來(lái)看,一般的都需要經(jīng)歷下面幾個(gè)階段:
1。應(yīng)用服務(wù)器根據(jù)JSP頁(yè)面生成一個(gè)Java文件
2。應(yīng)用服務(wù)器調(diào)用java.exe將Java文件編譯成一個(gè)Servlet對(duì)應(yīng)的class文件
3。用戶的瀏覽器請(qǐng)求JSP對(duì)應(yīng)的Servlet,Web容器起一個(gè)線程執(zhí)行Servlet,將數(shù)據(jù)返回給客戶端瀏覽器
4。用戶的IE根據(jù)返回的數(shù)據(jù),將結(jié)果顯示給用戶。
Key Step Analysis
為了更好的了解編碼問(wèn)題,我們暫時(shí)先從上面的四個(gè)環(huán)節(jié)一步步來(lái)分析,根據(jù)分析的結(jié)果,來(lái)得到最終的解決辦法。
1. 在應(yīng)用服務(wù)器根據(jù)JSP頁(yè)面生成Java文件階段。
應(yīng)用服務(wù)器會(huì)將整個(gè)JSP頁(yè)面的代碼讀取出來(lái),然后寫到一個(gè)新的JAVA文件中,在讀文件和寫文件的時(shí)候都牽涉到一個(gè)編碼問(wèn)題,這個(gè)編碼問(wèn)題應(yīng)用服務(wù)器是如何解決的呢?我研究Tomcat應(yīng)用服務(wù)器的源代碼,發(fā)現(xiàn)Tomcat中有一個(gè)pageEncoding參數(shù)非常重要,在ParserController會(huì)從JSP文件中讀出這個(gè)參數(shù)(如果沒(méi)有讀到,就從第一行的contentType中讀取charset),然后保存起來(lái),如果沒(méi)有讀取到這個(gè)參數(shù),會(huì)從JspConfig中讀出一個(gè)默認(rèn)的PageEncoding參數(shù),如果這兩個(gè)參數(shù)都沒(méi)有的設(shè)置,系統(tǒng)會(huì)默認(rèn)成ISO8859-1的編碼來(lái)讀取原來(lái)的JSP文件。
從上面的分析出,我們已經(jīng)基本了解了應(yīng)用服務(wù)器讀取JSP文件的編碼方式,由于Java底層都是基于Unicode編碼來(lái)存儲(chǔ)字符的,所以在寫文件的時(shí)候,都輸出成Unicode編碼的形式。
2。在JDK將Java文件編譯成Class文件的時(shí)候
可以利用-encoding參數(shù)指定源文件的編碼,這在手動(dòng)編譯的時(shí)候非常重要,因?yàn)檫@決定了Java虛擬機(jī)讀取Java文件時(shí)采用的編碼方式,但是在Web應(yīng)用中這個(gè)環(huán)節(jié)我們可以忽略,因?yàn)閼?yīng)用服務(wù)器可以很好的解決這個(gè)編碼。以Tomcat為例,由于生成的java文件是固定的UTF-8編碼,所以Tomcat也固定的采用UTF-8編碼來(lái)讀取,通過(guò)瀏覽AbstractCatalinaTask可以看到reader = new InputStreamReader(hconn.getInputStream(), CHARSET);其中的CHARSET=utf-8。所以在這個(gè)環(huán)節(jié)中應(yīng)用服務(wù)器都可以很好的把握,不會(huì)帶來(lái)編碼問(wèn)題。
3. 用戶的瀏覽器請(qǐng)求JSP對(duì)應(yīng)的Servlet階段。
如果前面的環(huán)節(jié)中不會(huì)帶來(lái)編碼問(wèn)題,也就是說(shuō)在Java虛擬機(jī)中運(yùn)行的時(shí)候,能正常的獲取到“中國(guó)”,那么在執(zhí)行servlet的環(huán)節(jié)中不會(huì)“中國(guó)”始終是以Unicode存儲(chǔ)的中國(guó),那么在第三個(gè)環(huán)節(jié)中需要關(guān)注的是JspWriter如何將數(shù)據(jù)返回給客戶端瀏覽器。大家可以試驗(yàn)一下,在java中如果用new String(str.getBytes("encoding"),"encoding")執(zhí)行的時(shí)候,始終不會(huì)出現(xiàn)亂碼問(wèn)題,也就是說(shuō),一個(gè)字符串可以用不同的代碼來(lái)getBytes()生成字節(jié)數(shù)組(底層I18N.jar所作的工作,提供Byte2Char和Char2Byte的轉(zhuǎn)換)。
如果大家可以理解這一點(diǎn),那么下面大家就需要了解JspWriter輸出字符串時(shí)采用的編碼方式是什么?通過(guò)瀏覽Response.java類可以了解到Tomcat應(yīng)用服務(wù)器是根據(jù)contentType來(lái)獲取的writer的編碼方式,也就是說(shuō),最后返回客戶端的字節(jié)流是contentType對(duì)應(yīng)的charset中獲取出來(lái)的字節(jié)數(shù)組。
4. IE根據(jù)返回的數(shù)據(jù)處理顯示階段
通過(guò)前面的分析可以了解到,應(yīng)用服務(wù)器返回的“中國(guó)”是根據(jù)ContentType中的charset來(lái)顯示的,只要IE知道該用這個(gè)編碼來(lái)接收字節(jié)流并轉(zhuǎn)成字符串,并將用戶的瀏覽器推薦合適的編碼來(lái)查看結(jié)果,用戶就可以瀏覽到正確的“中國(guó)”兩個(gè)字??梢愿吲d得是,目前的IE等瀏覽器正式這樣處理的。
Conclusion
通過(guò)上面的分析,我們可以看到,在整個(gè)JSP頁(yè)面的編碼過(guò)程中,我們真正要解決的是JSP文件到Java文件這個(gè)過(guò)程中的編碼問(wèn)題,也就是PageEncoding參數(shù)的設(shè)置問(wèn)題。由于pageEncoding參數(shù)是servlet2.3規(guī)范中規(guī)定的參數(shù),所以下面的方法在很多應(yīng)用服務(wù)器下面都通用,這方面的設(shè)置本人在工作中基本上得到了下面的一些方法:
1。在JSP頁(yè)面的中加上pageEncoding參數(shù),比如:<%@ page contentType="text/html; charset=UTF-8" pageEncoding="GBK"%>,這樣就可以將頁(yè)面可以用ANSI來(lái)存儲(chǔ)。也就是說(shuō)當(dāng)頁(yè)面存儲(chǔ)的編碼方式和chtentType中的charset不一樣的時(shí)候,可以考慮加上pageEncoding參數(shù)。
2。有些應(yīng)用服務(wù)器(如weblogic),在沒(méi)有獲取到pageEncoding參數(shù)的時(shí)候,不是先從charset中獲取編碼類型,而是從另外的一些配置文件,如weblogic.xml文件中加上下面的代碼:
<jsp-descriptor>
<jsp-param>
<param-name>compilerSupports</param-name>
<param-value>true</param-value>
</jsp-param>
<jsp-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</jsp-param>
</jsp-descriptor>
(在Tomcat5X種也有類似的處理,在應(yīng)用的web.xml文件中加上類似下面的配置項(xiàng))
</jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<el-ignored>true</el-ignored>
</jsp-property-group>
</jsp-config>
以上是對(duì)JSP頁(yè)面編碼的一些分析和處理方法,希望能對(duì)大家今后的學(xué)習(xí)和工作中有幫助!