posts - 64,  comments - 9,  trackbacks - 0

          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。用戶(hù)的瀏覽器請(qǐng)求JSP對(duì)應(yīng)的Servlet,Web容器起一個(gè)線(xiàn)程執(zhí)行Servlet,將數(shù)據(jù)返回給客戶(hù)端瀏覽器
          4。用戶(hù)的IE根據(jù)返回的數(shù)據(jù),將結(jié)果顯示給用戶(hù)。

                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),然后寫(xiě)到一個(gè)新的JAVA文件中,在讀文件和寫(xiě)文件的時(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ǔ)字符的,所以在寫(xiě)文件的時(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. 用戶(hù)的瀏覽器請(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ù)返回給客戶(hù)端瀏覽器。大家可以試驗(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類(lèi)可以了解到Tomcat應(yīng)用服務(wù)器是根據(jù)contentType來(lái)獲取的writer的編碼方式,也就是說(shuō),最后返回客戶(hù)端的字節(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)成字符串,并將用戶(hù)的瀏覽器推薦合適的編碼來(lái)查看結(jié)果,用戶(hù)就可以瀏覽到正確的“中國(guó)”兩個(gè)字。可以高興得是,目前的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中獲取編碼類(lèi)型,而是從另外的一些配置文件,如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種也有類(lèi)似的處理,在應(yīng)用的web.xml文件中加上類(lèi)似下面的配置項(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í)和工作中有幫助!

          posted on 2009-07-23 14:41 super_nini 閱讀(345) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          <2009年7月>
          2829301234
          567891011
          12131415161718
          19202122232425
          2627282930311
          2345678

          常用鏈接

          留言簿

          隨筆檔案

          文章檔案

          相冊(cè)

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 武乡县| 平乐县| 兴安盟| 仲巴县| 大方县| 黄石市| 民权县| 鄢陵县| 休宁县| 自治县| 边坝县| 山阴县| 德化县| 唐山市| 高平市| 蓬莱市| 蒙城县| 重庆市| 武义县| 丹寨县| 湄潭县| 永济市| 新田县| 广西| 阳原县| 麻阳| 岑溪市| 九江县| 岢岚县| 银川市| 河南省| 承德县| 水城县| 瓦房店市| 巴楚县| 阜宁县| 和田县| 镇江市| 徐闻县| 临夏市| 宣武区|