Servlet詳解(轉載)

 Servlet詳解


http://localhost:9090/xxx.html?userName=xxc
問:爲何html靜態頁面也能接收參數?
答:xxc.html並不是一個真正的html頁面,而是在web.xml文件中經過了映射而已。
[html] view plaincopyprint?
<servlet>  
    <servlet-name>servletday1</servlet-name>  
    <servlet-class>cn.xxc.servletday1.FirstServlet</servlet-class>  
</servlet>  
    
<servlet-mapping>  
    <servlet-name>servletday1</servlet-name>  
    <url-pattern>/xxc.com</url-pattern>  
</servlet-mapping>  

問:servlet在容器中的實例對象可以被創建多次麼?
答:一般情況下當服務器收到要訪問這個servlet的請求後,這個servlet實例對象就會被創建,而且只創建一次,後面如果有同樣的請求,那麼就會訪問已經創建好的實例對象(這也說明了,爲什麼第一次訪問servlet的時候會比後面訪問的速度要慢)。但是如果在web.xml中多次註冊了這個servlet,那麼servlet在容器中的實例對象就可以被創建多次(註冊幾次就創建幾次)。
[html] view plaincopyprint?
<servlet>  
    <servlet-name>servletday1</servlet-name>  
    <servlet-class>cn.xxc.servletday1.FirstServlet</servlet-class>  
</servlet>  
<!-- 再次註冊同一個servlet,但是servletName必須不一樣 -->  
<servlet>  
    <servlet-name>servletday2</servlet-name>  
    <servlet-class>cn.xxc.servletday1.FirstServlet</servlet-class>  
</servlet>  
   
<servlet-mapping>  
    <servlet-name>servletday1</servlet-name>  
    <url-pattern>/pkq.com</url-pattern>  
</servlet-mapping>  


問:一個servlet可以被映射多次麼?
答:可以。如下創建就可以用兩種url來訪問。
(1)http://localhost:9090/項目名/xxc.com
(2)http://localhost:9090/項目名/pkq.com
[html] view plaincopyprint?
<servlet>  
    <servlet-name>servletday1</servlet-name>  
    <servlet-class>cn.xxc.servletday1.FirstServlet</servlet-class>  
</servlet>  
    
<servlet-mapping>  
    <servlet-name>servletday1</servlet-name>  
    <url-pattern>/xxc.com</url-pattern>  
    <url-pattern>/pkq.com</url-pattern>  
</servlet-mapping>  


servlet的*通配符:
在Servlet映射到的URL中也可以用*通配符,但是只能有兩種固定的格式:
(1)“*.擴展名” 
(2)以正斜槓(/)開頭並以“/*”結尾。
注意:正斜槓(/)後的*不能帶擴展名,即不能設置爲“/action/*.xx”形式


Servlet映射的最具體匹配規則:
對於如下的一些映射關係:
●/abc/*映射到Servlet1
●/*映射到Servlet2
●/abc映射到Servlet3
●*.do映射到Servlet4
將發生如下一些行爲:
●當請求URL爲“/abc/a.html”,“/abc/*”和“/*”都可以匹配這個URL,
Servlet引擎將調用Servlet1 
●當請求URL爲“/abc”,“/*”和“/abc”都可以匹配這個URL,
Servlet引擎將調用Servlet3
●當請求URL爲“/a.do”,“/*”和“*.do”都可以匹配這個URL,
Servlet引擎將調用Servlet2
●當請求URL爲“/xxx/yyy/a.do”時,“/*”和“*.do”都可以匹配這個URL
Servlet引擎將調用Servlet2
匹配原則總結:
目錄格式優先級大於後綴名格式。如果同是目錄格式或者後綴名格式,哪個更像哪個匹配。


問:爲何在訪問http://localhost:9090/項目名/a.html時,就能訪問到a.html這個頁面?
答:因爲在tomcat下的conf目錄下有一個web.xml文件,這個文件中有如下一段配置信息。這個默認的servlet會去硬盤上找a.html網頁返回給瀏覽器。
[html] view plaincopyprint?
<servlet>  
        <servlet-name>default</servlet-name>  
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>  
        <init-param>  
            <param-name>debug</param-name>  
            <param-value>0</param-value>  
        </init-param>  
        <init-param>  
            <param-name>listings</param-name>  
            <param-value>false</param-value>  
        </init-param>  
        <load-on-startup>1</load-on-startup>  
</servlet>  
  
  
<servlet-mapping>  
        <servlet-name>default</servlet-name>  
        <url-pattern>/</url-pattern>  
</servlet-mapping>  


Servlet的生命週期:
1.web容器檢查是否已經裝載並創建了該Servlet的實例對象。如果是,則執行第四步,否則,這行第二步。
2.裝載並創建該Servlet的一個實例對象。
3.調用Servlet實例對象的inint()方法。
4.創建一個用於封裝HTTP請求消息的HttpServletRequest對象和一個代表HTTP響應消息的HttpServletResponse對象,然後調用Servlet的service()方法,並將請求和響應對象作爲參數傳遞進去。
5.WEB應用程序被停止或重新啓動之前,WEB容器將卸載Servlet,並在卸載之前調用Servlet的destroy()方法。


特赦情況:
爲了防止第一個訪問Servlet的用戶看到生成後的網頁所需要的時間比第二個慢的情況。我們可以在WEB容器啓動這個WEB程序的時候就進行加載並創建實例對象(不需要發出請求)。
[html] view plaincopyprint?
<servlet>  
    <servlet-name>servletday1</servlet-name>  
    <servlet-class>cn.xxc.servletday1.FirstServlet</servlet-class>  
    <load-on-startup>1</load-on-startup><!--啓動時加載,數字是1至5,越小越優先,如果相同則按順序加載-->  
</servlet>  


Servlet的構造方法和init方法的區別:
1.構造方法是JVM調用,init方法是由tomcat調用。
2.構造方法要優先於init方法調用,原因也很好理解:因爲要先有Servlet這個實例對象,才能使WEB容器去調用。
3.init方法中有HttpServletRequest和HttpServletResponse兩個參數。但是構造方法裏卻沒有。所以不能說將init()裏的步驟可以放到構造方法裏去執行。

Tomcat將Servlet的一些配置信息都封裝在ServletConfig對象中
 java.lang.String getInitParameter(java.lang.String name)
          Returns a String containing the value of the named initialization parameter, ornull if the parameter does not exist.
 java.util.Enumeration getInitParameterNames() 
          Returns the names of the servlet's initialization parameters as an Enumeration of String objects, or an empty Enumeration if the servlet has no initialization parameters.
 ServletContext getServletContext() 
          Returns a reference to the ServletContext in which the caller is executing.    獲取上下文對象
 java.lang.String getServletName() 
          Returns the name of this servlet instance.
Web.xml中的全局和局部配置信息
[html] view plaincopyprint?
<context-param><!--全局初始化參數 -->  
    <param-name>userName</param-name>  
    <param-value>術士</param-value>  
  </context-param>  
  <servlet>  
    <servlet-name>servletday1</servlet-name>  
    <servlet-class>cn.xxc.servletday1.FirstServlet</servlet-class>  
    <init-param><!-- 注意這個標籤必須緊跟servlet-class標籤 -->  
        <param-name>name1</param-name>  
        <param-value>薩滿</param-value>  
    </init-param>  
    <init-param>  
        <param-name>name2</param-name>  
        <param-value>法師</param-value>  
    </init-param>  
    <load-on-startup>1</load-on-startup>  
  </servlet>  

獲取Web.xml中的配置信息
[java] view plaincopyprint?
private ServletConfig config;  
  
@Override  
public void init(ServletConfig config) throws ServletException {  
    this.config = config;  
}  
  
@Override  
public void service(ServletRequest request, ServletResponse response)  
        throws ServletException, IOException {  
    System.out.println("ServletName--->"+config.getServletName());//獲取Servlet的名字  
    Enumeration<String> configs = config.getInitParameterNames();//獲取Servlet的初始化信息 name值,是枚舉類型  
    while(configs.hasMoreElements()){  
        String name = configs.nextElement();  
        String value = config.getInitParameter(name);//根據name值,獲取value  
        System.out.println(name+"  --------------  "+value);  
    }  
  
    ServletContext context = config.getServletContext();  
    String value = context.getInitParameter("userName");//獲取全局初始化參數  
    System.out.println(value);  
}  


以下是一個實現了Servlet接口的自定義的Servlet   需要實現5個方法
[java] view plaincopyprint?
package cn.xxc.servletday1;  
  
import java.io.IOException;  
  
import javax.servlet.Servlet;  
import javax.servlet.ServletConfig;  
import javax.servlet.ServletException;  
import javax.servlet.ServletRequest;  
import javax.servlet.ServletResponse;  
  
public class FirstServlet implements Servlet {  
  
    @Override  
    public void init(ServletConfig paramServletConfig) throws ServletException {//tomcat在調用Servlet時,初始化會自動調用此方法,只會調用一次   
          
    }  
  
    @Override  
    public ServletConfig getServletConfig() {  
        return null;  
    }  
  
    @Override  
    public void service(ServletRequest paramServletRequest,  
            ServletResponse paramServletResponse) throws ServletException,  
            IOException {//當用戶發送一次請求到這個Servlet時,會調用這個方法  
          
    }  
  
    @Override  
    public String getServletInfo() {  
        return null;  
    }  
  
    @Override  
    public void destroy() {//Web應用程序在卸載或重啓前會調用此方法  
          
    }  
  
}  

我們發現每次寫一個Servlet都需要實現Servlet接口,並且要覆寫5個方法,而且這5個方法並不是全部會用到,這樣就有些不足。
這時我們可以讓自己寫的Servlet繼承GenericServlet類
[java] view plaincopyprint?
package cn.xxc.servletday1;  
  
import java.io.IOException;  
  
import javax.servlet.GenericServlet;  
import javax.servlet.ServletConfig;  
import javax.servlet.ServletException;  
import javax.servlet.ServletRequest;  
import javax.servlet.ServletResponse;  
  
public class FirstServlet extends GenericServlet {  
  
    @Override  
    public void service(ServletRequest paramServletRequest,  
            ServletResponse paramServletResponse) throws ServletException,  
            IOException {  
        /* 
         * 爲什麼這樣就能取到ServletConfig? 
         * 請看GenericServlet源碼。 
         */  
        ServletConfig config = this.getServletConfig();  
    }  
  
}  

GenericServlet類源碼
[java] view plaincopyprint?
package javax.servlet;  
   
 import java.io.IOException;  
import java.io.Serializable;  
import java.util.Enumeration;  
  
import javax.servlet.Servlet;  
import javax.servlet.ServletConfig;  
import javax.servlet.ServletContext;  
import javax.servlet.ServletException;  
import javax.servlet.ServletRequest;  
import javax.servlet.ServletResponse;  
   
 public abstract class GenericServlet  
   implements Servlet, ServletConfig, Serializable{  
   private transient ServletConfig config;  
   
   public void destroy(){//覆寫Servlet中的銷燬方法  
   }  
   
   public String getInitParameter(String name){  
       return getServletConfig().getInitParameter(name);  
   }  
   
   public Enumeration getInitParameterNames(){  
       return getServletConfig().getInitParameterNames();  
   }  
   
   public ServletConfig getServletConfig(){  
     //這裏就解釋了爲何實現了GenericServlet的子類可直接ServletConfig config = this.getServletConfig();  
       return this.config;  
   }  
   
   public ServletContext getServletContext(){  
       return getServletConfig().getServletContext();  
   }  
   
   public String getServletInfo(){  
       return "";  
   }  
   
   public void init(ServletConfig config)//覆寫Servlet接口的初始化方法  
     throws ServletException{  
        this.config = config;  
        init();  
        /* 
         * 這種函數稱爲鉤子函數 
         * 既然繼承了GenericServlet,它給我們提供了什麼樣的簡便呢? 
         *      GenericServlet覆寫了init(ServletConfig config)然後在其中又調用一個自定函數init() 
         *      在子類中只需要覆寫init()方法即可,即不需要做像this.config = config賦值操作【最主要的方便就是省略了這句話】 
         * 爲何在destroy函數中不定義這種鉤子函數 
         *      沒必要,因爲destroy函數是無參的,即不需要做像this.config = config賦值操作 
         * 在GenericServlet中init()方法爲空 
         * 當一個類繼承了GenericServlet,如果需要在初始化函數中加入一些操作,那麼就可以覆寫GenericServlet中的init()方法 
         * 這樣做的原因是因爲tomcat調用初始化方法必定是init(ServletConfig config)而不會去調用自定義的init() 
         */  
   }  
   
   public void init() throws ServletException{  
   }  
   
   public void log(String msg){  
       getServletContext().log(getServletName() + ": " + msg);  
   }  
   
   public void log(String message, Throwable t){  
       getServletContext().log(getServletName() + ": " + message, t);  
   }  
   
   public abstract void service(ServletRequest paramServletRequest, ServletResponse paramServletResponse)  
     throws ServletException, IOException;  
   
   public String getServletName(){  
       return this.config.getServletName();  
   }  
 }  

GenericServlet還不足以滿足我們的需求,比如獲取用戶的IP地址,是在HttpServletRequest類中才有的方法,需要把ServletRequest強轉。
但是每次強轉又顯得不足,這時我們可以繼承HttpServlet來解決這個問題。(HttpServlet繼承了GenericServlet)
而且Http請求方式又有好幾種,每種方式都對應不同的方法。如果使用ServletRequest,那麼就需要我們自己去判斷到底是get,post,put,delete,options,trace。這樣也很麻煩。
所以此時,我們可以用HttpServletRequest。
摘取HttpServletRequest源碼中的兩個方法就能明白它是怎麼運行的。
[java] view plaincopyprint?
public void service(ServletRequest req, ServletResponse res) //強轉,並且返回強轉後的結果  
        throws ServletException, IOException{  
    HttpServletRequest request;  
    HttpServletResponse response;  
    try{  
            request = (HttpServletRequest)req;  
            response = (HttpServletResponse)res;  
    } catch (ClassCastException e) {  
        throw new ServletException("non-HTTP request or response");  
  }  
        service(request, response);  
}  
    
protected void service(HttpServletRequest req, HttpServletResponse resp)//判斷請求方式是什麼,並且進行相應的方法調用  
     throws ServletException, IOException{  
     long lastModified;  
     String method = req.getMethod();  
     if (method.equals("GET")) {  
       lastModified = getLastModified(req);  
     if (lastModified == -1L){  
        doGet(req, resp);  
     } else {  
        long ifModifiedSince = req.getDateHeader("If-Modified-Since");  
        if (ifModifiedSince < lastModified / 1000L * 1000L){  
          maybeSetLastModified(resp, lastModified);  
           doGet(req, resp);  
        } else {  
           resp.setStatus(304);  
         }  
       }  
     }  
    else if (method.equals("HEAD")) {  
      lastModified = getLastModified(req);  
      maybeSetLastModified(resp, lastModified);  
     doHead(req, resp);  
     }  
   else if (method.equals("POST")) {  
      doPost(req, resp);  
     }  
     else if (method.equals("PUT")) {  
       doPut(req, resp);  
     }  
    else if (method.equals("DELETE")) {  
      doDelete(req, resp);  
     }  
     else if (method.equals("OPTIONS")) {  
      doOptions(req, resp);  
     }  
    else if (method.equals("TRACE")) {  
       doTrace(req, resp);  
     }  
     else{  
      String errMsg = lStrings.getString("http.method_not_implemented");  
       Object[] errArgs = new Object[1];  
       errArgs[0] = method;  
      errMsg = MessageFormat.format(errMsg, errArgs);  
       resp.sendError(501, errMsg);  
     }  
   }  

這也就是我們平時在開發時用的最多的。
[java] view plaincopyprint?
package cn.xxc.servletday1;  
  
import java.io.IOException;  
  
import javax.servlet.ServletException;  
import javax.servlet.http.HttpServlet;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
  
public class FirstServlet extends HttpServlet {  
  
    @Override  
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)  
            throws ServletException, IOException {  
        // TODO Auto-generated method stub  
        super.doGet(req, resp);  
    }  
  
    @Override  
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)  
            throws ServletException, IOException {  
        // TODO Auto-generated method stub  
        super.doPost(req, resp);  
    }  
      
  
}  

Response詳解:
1.首先要明確一點,確切的來說並不是response將服務器的響應送給了客戶端,而是web容器將response裏的內容取出後,生成響應的網頁返回給客戶端。
2.狀態碼詳解:
 void setStatus(int sc) 
          Sets the status code for this response.   設置狀態碼,但是有可能數字記不住,這時可以用靜態常量來表示  例如HttpServletResponse.SC_BAD_REQUEST  表示404的意思
 void setStatus(int sc, java.lang.String sm)
          Deprecated. As of version 2.1, due to ambiguous meaning of the message parameter. To set a status code usesetStatus(int), to send an error with a description usesendError(int, String). Sets the status code and message for this response.過時設置狀態碼,同時生成狀態碼的message信息,設置message信息的時候不要忘記在前面加response.setCharacterEncoding("UTF-8");否則亂碼如下圖:
void sendError(int sc) 
          Sends an error response to the client using the specified status code and clearing the buffer.//設置狀態碼,而且清空下面寫在response裏的內容,返回給瀏覽器與狀態碼對應的結果頁面,無論這個方法寫在setStatus前還是後,都是此方法生效


以下3種方式都是設置服務器相應數據的長度。
[java] view plaincopyprint?
response.setHeader("Content-Length", "6");  
response.setIntHeader("Content-Type", 6);  
response.setContentLength(6);  
例如:總共返回3個字節,但是消息頭卻說有6個字節,這時瀏覽器的進度條就一直會處於等待狀態。但最後進度條還是會讀完,原因是服務器和瀏覽器建立的時間超過限定時間就和瀏覽器斷開連接了。
[java] view plaincopyprint?
response.setHeader("Content-Length", "6");  
response.getOutputStream().write("aaa".getBytes());  
問:爲何消息頭寫的是5個字節,送回的數據是3個字節,進度條還是和正常一樣無需等待就能顯示?
答:因爲aaa後還有2個字節:\r\n。
[java] view plaincopyprint?
response.setContentLength(5);  
response.getOutputStream().write("aaa".getBytes());  


頁面編碼:
默認:
[java] view plaincopyprint?
response.setHeader("Content-Type", "text/html");//告訴瀏覽器服務器響應的數據要以字符顯示(默認爲上次在瀏覽器設置的編碼形式)  
response.getOutputStream().write("哈哈哈".getBytes());//響應的數據,以gbk(默認)編碼成二進制數據送給瀏覽器  
設置:
[java] view plaincopyprint?
response.setHeader("Content-Type", "text/html;charset=utf-8");//設置瀏覽器用UTF-8編碼來顯示接收到的二進制數據  
response.getOutputStream().write("哈哈哈".getBytes("utf-8"));//送給瀏覽器的二進制數據以UTF-8來進行編碼  
geiWriter:
[java] view plaincopyprint?
response.setHeader("Content-Type", "text/html");//告訴瀏覽器服務器響應的數據要以字符顯示(編碼默認爲gbk)  
response.getWriter().println("好久不見");//這個操作省略了字符編碼操作,根據上面設置的編碼來確定用什麼碼來進行二進制編碼,如果上面沒寫,則爲ISO-8859-1  
問:爲何getWriter()不是默認GBK編碼?
答:getWriter()是tomcat的方法,它是按照tomcat默認編碼iso-8859-1進行編碼的,而getBytes()是JVM的方法,他默認是按照操作系統的編碼。
[java] view plaincopyprint?
response.setHeader("Content-Type", "text/html;charset=utf-8");//下面這兩句和第一句是同一個意思  
response.setContentType("text/html");  
response.setCharacterEncoding("UTF-8");  
設置網頁不緩存的3個消息頭:
[java] view plaincopyprint?
response.setDateHeader("Expires", 0);  
response.setHeader("Cache-Control", "no-cache");  
response.setHeader("Pragma", "no-cache");  


由於HTML頁面是靜態頁面不能動態生成,所以HTML頁面的編碼採用如下格式定義:
http-equiv表示等同於哪個消息頭的意思 content表示消息頭的值
[html] view plaincopyprint?
<meta http-equiv="Content-Type" content="text/html;charset=gbk">  

如下例子可以證明:html加載頁面的時候是先加載所有meta標籤後,然後再加載正文的。
[html] view plaincopyprint?
<!DOCTYPE html>  
<html>  
  <head>  
    <title>gbk.html</title>  
      
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">  
    <meta http-equiv="description" content="this is my page">  
  </head>  
    
  <body>  
      哈哈哈哈<br>   <!--依舊不顯示亂碼-->  
  </body>  
    <meta http-equiv="Content-Type" content="text/html;charset=gbk"><!--meta標籤在下面也是有效的,meta會先於被加載-->  
</html>  


模擬自動刷新的消息頭:
[html] view plaincopyprint?
<meta http-equiv="Refresh" content="3">  

靜態網頁的取消緩存:(但是發現經過多次刷新後還是進行了緩存,狀態碼304)
[html] view plaincopyprint?
<meta http-equiv="Expires" content="0">  
<meta http-equiv="Cache-Control" content="no-cache">  
<meta http-equiv="Pragma" content="no-cache">  


用於文件下載的兩個消息頭:
[html] view plaincopyprint?
response.setContentType("application/octet-stream");//告訴瀏覽器服務器返回的是二進制數據  
response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode("皮卡丘.txt","UTF-8"));//如果URL裏有中文必須進行URL的編碼  


注意:
往respons裏寫數據的時候,response並不是馬上把寫入的數據交給服務器處理的,而是先寫到response的緩衝區裏的。


Request:
注意:
jsp中代碼
[html] view plaincopyprint?
<form action="servlet/ThirdServlet?userName=1&passWord=2" method="post">  
         姓名<input type="text" name="uesrName"><br>  
        密碼<input type="text" name="passWord"><br>  
        <input type="submit" value="提交">  
</form>  
servlet中代碼
[java] view plaincopyprint?
public void doPost(HttpServletRequest request, HttpServletResponse response)  
            throws ServletException, IOException {  
        String userName = request.getParameter("userName");  
        String passWord = request.getParameter("passWord");  
        response.getWriter().println("userName --->"+userName);  
        response.getWriter().println("passWord---->"+passWord);  
}  
經過測試發現,get請求時候,組件裏的值會覆蓋url後的參數,而post既可以獲取url後面的參數的值也可以獲取組件裏的值

關於瀏覽器發送給服務器亂碼問題:
在servlet中一直有這麼一個疑問:
request.setCharacterEncoding("UTF-8");這個方法裏的參數到底是GBK還是UTF-8或者是別的什麼編碼。
經過HTTP協議的學習後,勢必有這麼一個疑問,response送給瀏覽器顯示網頁的時候,會告訴瀏覽器要用什麼碼來顯示網頁,那麼瀏覽器傳遞數據給服務器的時候,是否也有這麼一個消息頭來告訴服務器應該以什麼碼來解碼瀏覽器送給服務器的中文數據呢?事實上,瀏覽器沒有這麼一個消息頭送給服務器。也就是說
[java] view plaincopyprint?
System.out.println(request.getCharacterEncoding());  
這句話的打印結果是Null。
那麼,我們該如何去設置request.setCharacterEncoding("UTF-8")方法裏的參數呢?
既然,我們不能獲取瀏覽器是用什麼碼編碼,那就規定好瀏覽器在進行中文數據編碼的時候用什麼碼來編碼的。
(瀏覽器將中文數據是按什麼碼進行編碼的,主要看瀏覽器當前顯示網頁的編碼是什麼碼。例如IE可以右鍵--->編碼進行查看和設置。)
這樣我們就可以把request.setCharacterEncoding("UTF-8");方法裏的參數寫成規定的編碼。來解決問題了。

request.setCharacterEncoding("UTF-8");只針對post提交有效,因爲這句話針對的是請求體裏的內容解碼。
對URL後面的中文參數是起不到解決亂碼的作用,即get方式提交時,此方法無效。
那麼改怎麼辦呢?
1.在tomcat的conf文件夾下的server.xml文件的改tomcat端口的標籤裏增加如下屬性,表示經過URL編碼的的中文都用UTF-8來解碼。
[html] view plaincopyprint?
<Connector port="9090" protocol="HTTP/1.1"   
              connectionTimeout="20000"   
              redirectPort="8443" <span style="font-family: Arial, Helvetica, sans-serif;">URIEncoding="UTF-8"</span>/>  


2.在tomcat的conf文件夾下的server.xml文件的改tomcat端口的標籤裏增加如下屬性,表示是否用post處理中文的方式來處理get的中文亂碼問題。這樣改還不夠,需要在post方法裏寫出用什麼碼解碼。也就是說需要兩步!
[html] view plaincopyprint?
<Connector port="9090" protocol="HTTP/1.1"   
              connectionTimeout="20000"   
              redirectPort="8443" useBodyEncodingForURI="true"/>  


[java] view plaincopyprint?
public void doPost(HttpServletRequest request, HttpServletResponse response)  
            throws ServletException, IOException {  
        request.setCharacterEncoding("UTF-8");  
}  


3.但是以上兩種解決get請求的中文亂碼問題的方式僅限於tomcat服務器,如果是別的web服務器該怎麼解決呢?
[java] view plaincopyprint?
String userName = request.getParameter("userName");  
userName = new String(userName.getBytes("iso-8859-1"),"UTF-8");  


request.getRequestDispatcher().include(request, response)和request.getRequestDispatcher().forward(request, response)方法的區別:
這個方法是跳轉到一個頁面上,並且把request和response都帶去下個頁面。
[java] view plaincopyprint?
request.getRequestDispatcher("/WEB-INF/success.jsp").forward(request, response);  
但是如下例子:
servlet代碼
[java] view plaincopyprint?
response.setCharacterEncoding("UTF-8");  
response.getWriter().println("跳轉前...");  
request.getRequestDispatcher("/index.jsp").forward(request, response);  
response.getWriter().println("跳轉後...");  
如果用request.getRequestDispatcher().forward(request, response);只能顯示index.jsp頁面的內容,不能顯示response用write方法往緩衝區裏寫的內容。
原因:在進行forward跳轉的時候,會將writer往緩衝區裏寫的內容全部清空。


[java] view plaincopyprint?
response.setCharacterEncoding("UTF-8");  
response.getWriter().println("跳轉前...");  
request.getRequestDispatcher("/index.jsp").include(request, response);  
response.getWriter().println("跳轉後...");  
如果用
request.getRequestDispatcher("/index.jsp").include(request, response);則不會清空write往緩衝區裏寫的內容。即,可以顯示   跳轉前和跳轉後字樣。
但是經過測試發現,如果用request.getRequestDispatcher("/index.jsp").include(request, response);進行頁面的跳轉,Chrome和Firefox會把index.jsp頁面的源碼原封不懂的顯示給瀏覽器,而IE可以正確顯示index,jso裏body裏的部分。


當使用request.getRequestDispatcher("/index.jsp").include(request, response);被引用的index.jsp頁面中有中文字符,這時index.jsp頁面裏的page指令都是無效的。所以會出現亂碼。
因爲這時是引用的,servlet只引用了jsp中的內容,那麼用什麼編碼顯示被引用的頁面是由引用方來決定的。所以,爲了防止出現亂碼,需要在servlet里加一句:response.setCharacterEncoding("UTF-8");
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章