天天看點

Servlet 技術全總結 (已完成,不定期增加内容)

Servlet是獨立于平台和協定的伺服器端的java應用程式,處理請求的資訊并将其發送到用戶端。

Servlet的用戶端可以提出請求并動态獲得響應。

Servlet動态生成web頁面,擔當浏覽器或其他用戶端發出的請求與HTTP伺服器上的資料庫或應用程式之間的中間層。

對于所有用戶端請求,都隻需要建立一次Servlet執行個體,是以,節省了大量記憶體。

Part 1     Servlet運作原理

web伺服器收到一個http請求後,判斷請求内容,若是靜态頁面資料,自行處理,若為動态資料,交給Servlet容器,Servlet容器找到相應Servlet執行個體處理;處理結果交給web伺服器,再轉交給用戶端。

針對同一個Servlet,Servlet容器會在第一次收到HTTP請求時建立一個Servlet執行個體,然後啟動一個線程,第二次收到http請求後,Servlet容器無需建立相同Servlet ,僅開啟第二個線程來處理請求。

----->多線程的方式有效提高執行效率,降低伺服器負擔。

Part 2 Servlet 優勢

     Servlet具有優良的跨平台性;

     可移植性良好:java語言編寫,Servlet API标準完善,企業編寫的Servlet程式可輕松移植到其他伺服器中;

     執行效率高:Servlet請求到來時激活Servlet,處理完成後等待新請求;新請求産生新線程,而不是程序;

     使用友善:可輕松處理HTML表單資料,并讀取和設定HTTP頭,處理cookie,跟蹤會話;

Part 3 基礎知識

      HttpServlet作為一個抽象類來建立使用者自己的Http Servlet. Http Servlet類擴充了GenericServlet類。HttpServlet類的子類必須至少重寫doGet()和doPost()方法其中之一。HttpServlet類提供doGet()方法處理GET請求,doPost()處理POST請求:

      doGet() :通過GenericServlet 類的service()方法來調用此方法;

      doPost():  通過GenericServlet 類的service()方法來調用此方法;

Part 4 Servlet生命周期     

      Servlet生命周期由Servlet容器控制,該容器建立Servlet的執行個體。Servlet的生命周期是指Servlet執行個體在建立後,響應用戶端請求直至銷毀的全過程;

      其建立取決于Servlet的首次調用。

       Servlet生命周期定義了它如何被加載,初始化,以及它如何接受請求,響應請求,提供服務;

       代碼中,Servlet生命周期由接口javax.servlet.Servlet定義,所有的Servlet必須直接或間接的實作該接口,這樣才能在Servlet容器中運作。

       Servlet接口定義了生命周期三種方法:

       1    init():建立執行個體後進行初始化。實作ServletConfig接口的對象作為參數進行傳遞。在初始化過程中,Servlet容器使用ServletConfig接口資訊(如Servlet的初始化參數的名稱,初始化參數的值,以及Servlet執行個體名稱等)傳遞給Servlet。

public void init(ServletConfig config)throws ServletException      

       2    service():響應用戶端發出的請求。service()方法接受ServletRequest接口和Servletresponse接口的對象來處理請求和發送響應;

public void service(ServleRequest request,ServletResponse response) throws ServletException,IOException      

       3    destroy():如果不再有需要請求的對象,則釋放Servlet對象;

public void destroy()      

Servlet生命周期各階段:

1.執行個體化:Servlet容器建立Servlet類的執行個體對象;

2.初始化:容器調用Servlet的init()方法,通常會申請資源以便後續使用;

3.服務:由容器使用以響應客戶對Servlet的請求;

4.破壞:在釋放Servlet執行個體前調用,通常會釋放資源;

5.不可用:釋放記憶體中的容器;

[要執行個體化一個Servlet,容器必須先找到Servlet類,加載servlet類并建立Servlet對象,然後通過調用Servlet的init()方法來初始化Servlet。ServletConfig接口對象作為參數傳給init()方法,該接口對象為Servlet提供對ServletContext接口的通路。Servlet容器使用ServletContext接口與Servlet容器進行通信;

如果初始化Servlet失敗,則抛出UnavailableException或ServletException異常,并再次嘗試對Servlet進行執行個體化和初始化。然後将ServletRequest和ServletResponse接口作為參數傳遞給service方法,該方法将處理請求并傳回響應。

如果響應請求時發生異常,則容器通過調用destroy()方法解除安裝執行個體]

Part 5    Servlet API

 在javax.servlet和javax.servlet.http包中的各類和接口如下:

(1)ServletInputStream類

    從java.io.InputStream類擴充而來的抽象類,該類建立的對象用于讀取用戶端請求中的二進制資料,而該類的readLine()方法用于每次讀取一行資料;

        該方法将從給定偏移處開始的每位元組讀取到數組中,直到該方法遇到換行符或者讀取完一定量的位元組數量,該方法傳回一個整數來指定實際讀取的位元組數,到達流尾時傳回-1

public int readLine(byte b[],int offset,int length)throws java.io.IOException
//b為用于存儲讀取的資料的位元組數組;offset指定方法開始讀取字元的起始位置;length讀取的最大位元組數      

 (2)ServletOutputStream類

     該類建立的對象用于将二進制資料從伺服器發送到用戶端。具體實作的方法如下:

       +   print():将字元串寫入用戶端。如果發生任何輸入或輸出異常,則方法print()會引發IOException異常,print()方法接受參數,如char,float,double,int,long,String。

public void print(String str)throws java.io.IOException
//str為發送到用戶端的字元串      

       +     println():将字元串寫入用戶端,緊跟後面輸出回車。如果發生任何I/O異常,則會引發IOException

 (3)ServletRequest接口

     使用ServletRequest接口建立對象,用于使用戶端請求資訊對Servlet可用。建立的對象作為參數傳遞至Servlet的service()。

     該類實作方法如下:

       +     getInputStream():傳回用戶端請求中的二進制資料,并将其儲存在getInputStream對象中

public ServletInputStream getInputStream()throws IOException      

       +     getParameter():用于擷取請求消息一起發送的附加資訊-----請求參數

public String getParameter(String str)      

       +     getContentLength():傳回用戶端發送的請求的實際長度,如果長度未知,則傳回-1

public int getContentLength()      

       +     getServerName():傳回請求發至的伺服器名稱

public String getServerName()      

   (4)ServletResponse接口

       使用該接口建立的對象用于向用戶端提供響應。建立的對象作為參數傳遞至Servlet的service()方法中。該接口實作的方法如下:

       +     getOutpouStream():傳回一個ServletOutputStream對象,它被用來發送對用戶端的響應

public ServletOutputStream getOutputStream()throws IOException      

       +     getWriter():傳回将字元文本發送到用戶端的PrintWriter對象

public PrintWriter getWriter()throws IOException      

       +      setContentLength():允許使用者設定将作為響應放的資料的長度

public void setContentLength(int length)      

       +      getBufferSize():檢索實際的以響應用戶端的緩存區大小。若沒有使用緩沖區則傳回0

public int getBufferSize()      

       +      setBufferSize():設定将發送到用戶端的資料的緩沖區的大小

public void setBufferSize(int size)      

    (5)HttpServletRequest接口

         容器在調用Servlet的doGet()或doPost()方法時,會建立一個HttpServletRequest接口的執行個體和一個HttpServletResponse接口的執行個體,作為參數傳遞給doGet和doPost()方法。該接口代表客戶請求,它提供了多種擷取請求資料的方法,具體繼承層次如圖:

Servlet 技術全總結 (已完成,不定期增加内容)

    (6)HttpServletResponse接口

         該接口代表傳回給用戶端的響應;具體繼承層次如圖:

Servlet 技術全總結 (已完成,不定期增加内容)

     (7)ServletConfig接口

           在初始化過程中,Servlet容器使用ServletConfig接口的對象作為參數來傳遞Servlet的配置資訊。方法如下:

              +    getServletName():用于擷取Servlet執行個體名稱

public String getServletName()      

              +     getInitParameter():檢索初始化參數的值,如果參數不存在,則getInitParameter()方法傳回null

public String getInitParameter(String name)
//name為初始化參數的名稱字元串      

              +     getServletContext():傳回Servlet用來與其容器互動的ServletContext對象

public ServletContext getServletContext()      

       (8)ServletContext接口

             該接口定義了一組方法,Servlet使用這些方法與容器進行互動并擷取資訊(如讀寫檔案等)

               +     getContext():傳回允許Servlet通路伺服器上下文的ServletContext類對象

public ServletContext getServletContext(String uripath)
//uripath是Web容器上的另外一個Web程式的上下文路徑名稱字元串      

              +     getMimeType():傳回檔案的MIME類型。MIME定義了一種協定,允許使用者通過Internet交換非ASCII消息。不同的MIME類型分為"text/html"和"image/gif"

public String getMimeType(String file)
//file是檔案名稱      

              +     getResource():傳回與路徑名相對應的資源的URL

public java.net.URL getResource(String path) throws MalFormedURLexception
//path是資源對應的路徑名稱字元串      

         (9)擷取請求中的資料

                在Servlet類的請求處理方法中(如doGet(),doPost()方法),要想獲得用戶端請求中送出的資料,需要使用HttpServletRequest提供的以下方法:

public String getParameter(String name)
//擷取指定名稱的參數值

public String[] getParameterValues(String name)
//擷取指定名稱參數的所有值數組。它适用于一個參數名對應多個值的情況,如頁面表單中的複選框,多選清單送出的值

public java.util.Enumeration getParameterNames()
//傳回一個包含請求消息中的所有參數名的Enumeration對象。通過周遊這個Enumeration對象,就可以擷取請求消息中所有的參數名

public java.util.Map getParameterMap()
//傳回一個保留了請求消息中所有參數名和值的Map對象。Map對象的key是字元串類型的參數名,value是這個參數所對應的Object類型的值數組。      

        [提示:在此說明如何處理用戶端送出給伺服器的資料的亂碼問題

                  若用戶端以POST方式送出請求,請求消息主體中的參數資料是按HTML頁面中指定的編碼方式進行編碼的,在Servlet類的請求處理方法中需要先調用         HttpServletRequest接口的setCharacterEncoding(String enc)方法對請求消息主體中的資料按參數指定的編碼方式進行編碼,然後才能使用上述介紹的方法正 确獲得參數值

                  若用戶端使用GET方法請求,上述方法無效,此時,最好的解決方案是在URL中不使用中文等非ASCII字元]

        (9)重定向和請求配置設定

              1.重定向:HttpServletRequest接口提供的sendRedirect()方法用于生成302響應碼和Location響應頭進而通知用戶端去重新通路Location響應頭中指定的URL

public void sendRedirect(String location)throws IOException
         //其中location參數指定了重定向的URL,它可以使用絕對或相對URL,Servlet會進行轉化                 

             2.請求分派:RequestDispatcher接口,(分派器),定義了下面兩個方法

public void forward(ServletRequest request,ServletResponse response)throws ServletException,IOException;
//forward()用于将請求轉發到RequestDispatcher執行個體封裝的資源

public void include(ServletRequest request,ServletResponse response)throws ServletException,IOException;
//include()用于将RequestDispatcher執行個體封裝的資源作為目前響應内容的一部分包含進來      
RequestDispatcher dispatcher=request.getRequestDispatcher("servlet1");
//參數為目前servlet名稱

dispatcher.forward(request,response);      

     [重定向與請求分派的差別:

             請求分派隻能将請求轉發到同Web應用中的其他元件;而重定向也可以發送到其他Web;

             請求分派過程結束後,浏覽器内網址不改變;重定向完成後浏覽器内原網址變為重定向目标網址;

             ]

    (11)利用請求域屬性傳遞對象資料

          HttpServletRequest接口中提供了幾個方法來操作請求執行個體中的存儲對象

public void setAttribute(String name,Object obj)
//将對象存儲進HttpServletRequest執行個體中

public Object getAttribute(String name)
//檢索存儲在HttpServletRequest執行個體中的對象

public Enumeration getAttributeNames()
//傳回HttpServletRequest執行個體中所有屬性名的Enumeration對象

public void removeAtribute(String name)
//從指定HttpServletRequest執行個體中删除指定名稱的屬性      

        這種存儲在HttpServletRequest中的對象稱之為請求域屬性,屬于同一請求過程的多個處理子產品之間可以通過請求域屬性來傳遞對象資料;

//Servlet1.java
package test

import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.*;

public class Servlet1 extends HttpServlet{
         public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
              this.doPost(request,response);
        }

         public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
              String str="Job done";
              request.setAttribute("string",str);
              RequestDispatcher dispatcher=request.getRequestDispatcher("Servlet2");
              dispatcher.forward(request,response);
        }

}      
//Servlet2.java
package test

import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.*;

public class Servlet2 extends HttpServlet{
         public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
              this.doPost(request,response);
        }

         public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
              response.setContentType("text/html;charset=utf-8");
              PrintWriter out=response.getWriter();
              out.println("<html>");
              out.println("<head><title>test</title><head>");
              out.println("<body>");

              //擷取名為"string"的請求域屬性的值
              String str=(String)request.getAttribute("string");
              
              out.println(str);
              out.println("</body>");
              out.println("</html>");
        }

}      

 Part 6   Servlet的線程安全問題 

     Servlet預設是多線程模式執行的,當有多個使用者同時并發請求一個Servlet時,容器将啟動多個線程調用相應的請求處理方法,此時,請求處理方法中的局部變量是安全的,但對于成員變量和共享資料是不安全的,是以這多個線程有可能同時都操作這些資料,這是需要同步處理。是以,編寫代碼時需要非常細緻的考慮多線程的安全性問題。多數人編寫Servlet時不注意多線程問題,導緻少量使用者通路時沒有問題,但并發量大則出現莫名其妙的問題。

    解決: 

    1.使用synchronized:使用synchonized關鍵字同步操作成員變量和共享資料的代碼,就可以防止出現線程安全性問題,但這也意味着線程需要排隊處理。是以,在使用同步語句時要盡可能縮小同步代碼範圍,不能直接在請求處理方法(如doGet(),doPost()方法)使用同步,這樣會嚴重影響效率。

    2.盡量少使用成員變量和共享資料:對于集合,使用Vector代替非線程安全的ArrayList,使用Hashtable代替HashMap;不能在Servlet内建立自己的線程,導緻複雜化。

Part 7   Servlet  過濾器

過濾器技術(Filter)是Servlet2.3以上版本新增的功能,2.5對其進一步增強。

Filter是一個程式,它先于相關的Servlet或JSP頁面運作在伺服器上。過濾器可附加到多個或一個Servlet或JSP上,并且可以檢查進入這些資源的請求消息。

Filter在Request到達Servlet之前預處理,也可以在離開Servlet時處理Response;其實本質就是一個Servlet Chaining.

 一個Filter必須實作javax.servlet.Filter接口并定義三個方法:

public void init(FilterConfig config)
//Filter執行個體化後進行初始化的回調方法

public void doFilter(ServletRequest req,ServletResponse res,FilterChain chain)
//處理過濾業務的方法

public void destroy()
//Filter釋放時回調的方法      

每一個Filter從doFilter()方法中得到目前的request和response。在該方法内,可進行任何針對request和response的操作。Filter調用chain.doFilter()方法把控制權交給下一個Filter。

執行個體:Filter進行中文亂碼問題:

import java.io.IOException;
import javax.servlet.*;

public class CharacterEncodingFilter implements Filter{
  private FilterConfig config;
  public void destroy(){
  }
  public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)throws IOException,ServletException{
     String encoding=config.getInitParameter("encoding");
     if(null!=encoding&&!"".equals(encoding)){
        request.setCharacterEncoding(encoding);
     }
     chain.doFilter(request,response);
     //将修改過的請求和響應傳遞給下一個過濾器或者Servlet
  }

  //Filter初始化時的回調方法
  //FilterConfig接口執行個體中封裝了這個Filter的初始化參數
  public void init(FilterConfig config){
     this.config=config;
  }
}      

在web.xml中注冊

......
    <filter>
         <filter-name>characterEncodingFilter</filter-name>
         <filter-class>(路徑).characterEncodingFilter</filter-class>
         <init-param>
                <param-name>encoding</param-name>
                <param-value>UTF-8</param-value>
          </init-param>
    </filter>
    <filter-mapping>
          <filter-name>characterEncodingFilter</filter-name>
          <url-pattern>/*</url-pattern>
    </filter-mapping>
.........      

此處/*是過濾所有頁面請求,如果需要,可以指定頁面 或檔案夾下的所有頁面。

過濾鍊:為web添加按順序執行的多個Filter,進入時執行順序A-B,離開時執行順序B-A

排列方法是在web.xml中,按順序注冊即可;

如下示例:(在上一示例基礎上添加一個可以自動壓縮響應輸出流的Filter,提高傳輸效率)

......
    <filter>
         <filter-name>characterEncodingFilter</filter-name>
         <filter-class>(路徑).characterEncodingFilter</filter-class>
         <init-param>
                <param-name>encoding</param-name>
                <param-value>UTF-8</param-value>
          </init-param>
    </filter>
    <filter-mapping>
          <filter-name>characterEncodingFilter</filter-name>
          <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
         <filter-name>Compression Filter</filter-name>
         <filter-class>compressionFilters.CompressionFilter</filter-class>
         <!--設定緩沖區大小-->
         <init-param>
                <param-name>compressionThreshold</param-name>
                <param-value>512</param-value>
          </init-param>

          <!--調試級别-->
          <init-param>
                <param-name>debug</param-name>
                <param-value>0</param-value>
          </init-param>
    </filter>
    <filter-mapping>
          <filter-name>Compression Filter</filter-name>
          <url-pattern>/*</url-pattern>
    </filter-mapping>
.........          

即可實作Filter Chain。

Part 8  Servlet 監聽器      

監聽器可使應用對某些事件做出反應:

Servlet2.3以上版本提供了一下幾個接口:

      +   ServletContextListener:應用上下文生命周期監聽器,用于監聽Web應用的啟動和銷毀事件;

      +   ServletContextAttributeListener:應用上下文屬性事件監聽器,用于監聽Web應用上下文中的屬性改變事件;

      +   ServletRequestListener:請求生命周期監聽器,監聽請求的建立和銷毀;

      +   ServletRequestAttributeListener:請求屬性事件監聽器,用于監聽請求中的屬性改變事件;

      +   HttpSessionListener:會話生命周期監聽器,用于監聽會話的建立和銷毀;

      +   HttpSessionActivationListener:會話激活和鈍化事件監聽器;

      +   HttpSessionAtributeListener:會話屬性事件監聽器;

      +   HttpSessionBindingListener:會話值綁定事件監聽器;

每個監聽器接口都定義了一些回調方法,當對應事件發生後,Web容器會自動調用對應監聽器實作類中的相關方法。

執行個體:用監聽器統計web線上人數: 

//OnlineListener.java

import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class OnlinListener implements ServletContextListener,HttpSessionAttributeListener,HttpSessionListener{
   private ServletContext application=null;
 
   public void attributeAdded(HttpSessionBindingEvent arg0){
   List<String>  online= (List<String>)this.application.getAttribute("online");
   
      if("uesrname".equals(arg0.getName())){
          online.add((String)arg0.getValue());
      }
    this.application.setAttribute("online",online);
    }
    
    //實作空方法
    public void attributeRemoved(HttpSessionBindingEvent arg0){}
    public void attributeReplaced(HttpSessionBindingEvent arg0){}
    public void sessionCreated(SessionEvent arg0){}

   //銷毀時的回調方法
    public void sessionDestroyed(HttpSessionEvent arg0){
       List<String> online=(List<String>)this.application.getAttribute("online");
       String username=(String)arg0.getSession().getAttribute("username");
       online.remove(username)
       this.application.setAttribute("online",online);
    }

    public void contextDestroyed(ServletContextEvent arg0){}

    public void contextInitialized(ServletContextEvent arg0){
        this.application=arg0.getServletContext();
        this.application.setAttribute("online",new LinkedList<String>());
    }
}              

在web.xml中注冊:

...
<listener>
    <listener-class>....</listener-class>
</listener>
...      

 最後,建立幾個Servlet測試監聽器實作效果:

import java.io.*;
import java.util.List;
import javax.servlet.ServletExcetion;
import javax.servlet.http.*;

public class LoginServlet extends HttpServlet{
  public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
     this.doPost(request,response);
  }
   public void doPost(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
      request.setCharacterEncoding("UTF-8");
      String username=request.getParameter("username");

      //向session中添加屬性
      //觸發HttpSessionAttributeListener中的AttributeAdded方法
      if(usesrname!=null&& !username.equals("")){
          request.getSession().setAttribute("username",username);
      }
      List<String> online=(List<String>)getServletContext().getAttribute("online");

      response.setContentType("text/html;charset=utf-8");
      PrintWriter out=response.getWriter();
      out.println("<HTML>");
      out.println("<HEAD><TITLE>使用者清單</TITLE></HEAD>");
      out.println("<BODY>");
      out.println("目前使用者是:"+username);
      out.println("<hr/><h3>線上使用者清單</h3>");

      int size=online==null?0:online.size();
       for(int i=0;i<size;i++){
           if(i>0){
               out.println("<br/>");
           }
           out.println(i+1+"."+online.get(i));
       }
      
      //注意,此處對連結URL進行重寫
      out.println("<hr/><a href=\""+response.encodingUrL("logout")+"\">登出</a>"); 
      out.println("</BODY>");
      out.println("</HTML>");
      out.flush();
      out.close();
   }
}      
import java.io.*;
import java.util.List;
import javax.servlet.ServletExcetion;
import javax.servlet.http.*;

public class LogoutServlet extends HttpServlet{
  public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
     this.doPost(request,response);
  }
   public void doPost(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
      request.setCharacterEncoding("utf-8");
      //銷毀會話,觸發SessionListener中的SessionDestroyed方法
      request.getSession().invalidate();

     
      List<String> online=(List<String>)getServletContext().getAttribute("online");

      response.setContentType("text/html;charset=utf-8");
      PrintWriter out=response.getWriter();
      out.println("<HTML>");
      out.println("<HEAD><TITLE>使用者清單</TITLE></HEAD>");
      out.println("<BODY>");
      out.println("目前使用者是:"+username);
      out.println("<hr/><h3>線上使用者清單</h3>");

      int size=online==null?0:online.size();
       for(int i=0;i<size;i++){
           if(i>0){
               out.println("<br/>");
           }
           out.println(i+1+"."+online.get(i));
       }
      
      //注意,此處對連結URL進行重寫
      out.println("<hr/><a href=\"index.html\">首頁</a>"); 
      out.println("</BODY>");
      out.println("</HTML>");
      out.flush();
      out.close();
   }
}      

最後建立index.html裡面添加表單

....
<form action="login" method="post">
      使用者名:<input type="text" username"/>
      <input type="submit" value="登入"/><br/><br/>
</form>
....      

Part 9  個人寫法

在這裡介紹下本人覺得比較好的寫法,感覺很類似Spring MVC,寫起來很簡單,而且運作效率高。

下圖是項目目錄,分為DAO,Entity,util,和最後一個:Servlet

Entity:java Beans (setter;getter); DAO 操作資料庫(本人使用JDBC);util(主要就是dbUtil,負責建立連接配接和關閉);最後一個:Servlet,其實隻有一個,我将它命名為ActionServlet,負責接收處理Request,其實作用相當于DispatcherServlet (Spring)

//核心代碼

public class ActionServlet extends HttpServlet {

    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        String uri = request.getRequestURI();
        String action = uri.substring(
                uri.lastIndexOf("/"),uri.lastIndexOf("."));
        if(action.equals("/regist")){
            //先進行驗證碼的檢驗
            //使用者名是否存在
            String username = request.getParameter("username");
            UserDAO dao = new UserDAO();
            try {
                User user = dao.findByUsername(username);
                if(user != null){
                    request.setAttribute(
                            "regist_error", "使用者名已經存在");
                    request.getRequestDispatcher("regist.jsp")
                    .forward(request, response);
                }else{
                    user = new User();
                    //populate方法:會依據Map中的key
                    //去給user對象對應的屬性指派。
                    BeanUtils.populate(user, 
                            request.getParameterMap());
                    int id = dao.save(user);
                    //為使用者建立一個用來儲存檔案的檔案加
                    String path = 
                        getServletContext().getRealPath("upload");
                    File file = new File(path + "\\" + "pic_" + id);
                    if(!file.exists())
                        file.mkdirs();
                    response.sendRedirect("login.html");
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServletException(e);
            }
        }else if(action.equals("/login")){
            String username = request.getParameter("username");
            String pwd = request.getParameter("pwd");
            UserDAO dao = new UserDAO();
            try {
                User user = dao.findByUsername(username);
                if(user !=null && user.getPwd().equals(pwd)){
                    //登入成功
                    HttpSession session = 
                        request.getSession();
                    session.setAttribute("user", user);
                    response.sendRedirect("projectlist.do");
                }else{
                    request.setAttribute("login_error",
                            "使用者名或密碼出錯");
                    request.getRequestDispatcher("project_list.jsp")
                    .forward(request, response);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else if(action.equals("/list")){
            UserDAO dao = new UserDAO();
            try {
                List<User> users = dao.findAll();
                request.setAttribute("users", users);
                request.getRequestDispatcher("user_list.jsp")
                .forward(request, response);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServletException(e);
            }
        }else if(action.equals("/userDetail")){
            int id = Integer.parseInt(request.getParameter("id"));
            UserDAO dao = new UserDAO();
            PicDAO dao2 = new PicDAO();
            try {
                User user = dao.findByUserId(id);
                List<Pic> pics = dao2.findPics(id);
                request.setAttribute("user", user);
                request.setAttribute("pics", pics);
                request.getRequestDispatcher("userDetail.jsp")
                .forward(request, response);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServletException(e);
            }
                  

在web.xml中注冊時,隻需要注冊這個Servlet,即可擷取所有請求,分析其名稱,使用相應的DAO方法操作資料庫,搞定!

(正在更新)