JavaWeb Servlet知識點歸納
Servlet概述
生命週期方法:
l void init(ServletConfig):出生之後(1次);
l void service(ServletRequest request, ServletResponse response):每次處理請求時都會被呼叫;
l void destroy():臨死之前(1次);
特性:
1)單例,一個類只有一個物件;當然可能存在多個Servlet類;
2)執行緒不單例的,所以它的效率是高的!
Servlet類由我們來寫,但物件由伺服器來建立,並且由伺服器來呼叫相應的方法。
1 什麼是Servlet
Servlet是JavaWeb的三大元件之一,它屬於動態資源。
l 接收請求資料;
l 處理請求;
l 完成響應。
例如客戶端發出登入請求,或者輸出註冊請求,這些請求都應該由Servlet來完成處理!Servlet需要我們自己來編寫,每個Servlet必須實現javax.servlet.Servlet介面。
2 實現Servlet的方式(由我們自己來寫!)
實現Servlet有三種方式:
l 實現javax.servlet.Servlet介面;
l 繼承javax.servlet.GenericServlet類;
l 繼承javax.servlet.http.HttpServlet類;
通常我們會去繼承HttpServlet類來完成我們的Servlet,但學習Servlet還要從javax.servlet.Servlet介面開始學習。
Servlet.java
public interface Servlet { public void init(ServletConfig config) throws ServletException; public ServletConfig getServletConfig(); public void service(ServletRequest req, ServletResponse res) throws public String getServletInfo(); public void destroy(); } |
3 建立helloservlet應用
我們開始第一個Servlet應用吧!首先在webapps目錄下建立helloservlet目錄,它就是我們的應用目錄了,然後在helloservlet目錄中建立準備JavaWeb應用所需內容:
l 建立/helloservlet/WEB-INF目錄;
l 建立/helloservlet/WEB-INF/classes目錄;
l 建立/helloservlet/WEB-INF/lib目錄;
l 建立/helloservlet/WEB-INF/web.xml檔案;
接下來我們開始準備完成Servlet,完成Servlet需要分為兩步:
l 編寫Servlet類;
l 在web.xml檔案中配置Servlet;
HelloServlet.java
public class HelloServlet implements Servlet { public void init(ServletConfig config) throws ServletException {} public ServletConfig getServletConfig() {return null;} public void destroy() {} public String getServletInfo() {return null;} public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { System.out.println("hello servlet!"); } } |
我們暫時忽略Servlet中其他四個方法,只關心service()方法,因為它是用來處理請求的方法。我們在該方法內給出一條輸出語句!
web.xml(下面內容需要背下來)
<servlet> <servlet-name>hello</servlet-name> <servlet-class>cn.itcast.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/helloworld</url-pattern> </servlet-mapping> |
在web.xml中配置Servlet的目的其實只有一個,就是把訪問路徑與一個Servlet繫結到一起,上面配置是把訪問路徑:“/helloworld”與“cn.itcast.servlet.HelloServlet”繫結到一起。
l <servlet>:指定HelloServlet這個Servlet的名稱為hello;
l <servlet-mapping>:指定/helloworld訪問路徑所以訪問的Servlet名為hello。
<servlet>和<servlet-mapping>通過<servlet-name>這個元素關聯在一起了!
接下來,我們編譯HelloServlet,注意,編譯HelloServlet時需要匯入servlet-api.jar,因為Servlet.class等類都在servlet-api.jar中。
javac -classpath F:/tomcat6/lib/servlet-api.jar -d . HelloServlet.java
然後把HelloServlet.class放到/helloworld/WEB-INF/classes/目錄下,然後啟動Tomcat,在瀏覽器中訪問:http://localhost:8080/helloservlet/helloworld即可在控制檯上看到輸出!
l /helloservlet/WEB-INF/classes/cn/itcast/servlet/HelloServlet.class;
Servlet介面
1 Servlet的生命週期
所謂xxx的生命週期,就是說xxx的出生、服務,以及死亡。Servlet生命週期也是如此!與Servlet的生命週期相關的方法有:
l void init(ServletConfig);
l void service(ServletRequest,ServletResponse);
l void destroy();
1.1 Servlet的出生
伺服器會在Servlet第一次被訪問時建立Servlet,或者是在伺服器啟動時建立Servlet。如果伺服器啟動時就建立Servlet,那麼還需要在web.xml檔案中配置。也就是說預設情況下,Servlet是在第一次被訪問時由伺服器建立的。
而且一個Servlet型別,伺服器只建立一個例項物件,例如在我們首次訪問http://localhost:8080/helloservlet/helloworld時,伺服器通過“/helloworld”找到了繫結的Servlet名稱為cn.it.servlet.HelloServlet,然後伺服器檢視這個型別的Servlet是否已經建立過,如果沒有建立過,那麼伺服器才會通過反射來建立HelloServlet的例項。當我們再次訪問http://localhost:8080/helloservlet/helloworld時,伺服器就不會再次建立HelloServlet例項了,而是直接使用上次建立的例項。
在Servlet被建立後,伺服器會馬上呼叫Servlet的void init(ServletConfig)方法。請記住, Servlet出生後馬上就會呼叫init()方法,而且一個Servlet的一生。這個方法只會被呼叫一次。這好比小孩子出生後馬上就要去剪臍帶一樣,而且剪臍帶一生只有一次。
我們可以把一些對Servlet的初始化工作放到init方法中!
1.2 Servlet服務
當伺服器每次接收到請求時,都會去呼叫Servlet的service()方法來處理請求。伺服器接收到一次請求,就會呼叫service() 方法一次,所以service()方法是會被呼叫多次的。正因為如此,所以我們才需要把處理請求的程式碼寫到service()方法中!
1.3Servlet的離去
Servlet是不會輕易離去的,通常都是在伺服器關閉時Servlet才會離去!在伺服器被關閉時,伺服器會去銷燬Servlet,在銷燬Servlet之前伺服器會先去呼叫Servlet的destroy()方法,我們可以把Servlet的臨終遺言放到destroy()方法中,例如對某些資源的釋放等程式碼放到destroy()方法中。
1.4 測試生命週期方法
修改HelloServlet如下,然後再去訪問http://localhost:8080/helloservlet/helloworld
public class HelloServlet implements Servlet { public void init(ServletConfig config) throws ServletException { System.out.println("Servlet被建立了!"); } public ServletConfig getServletConfig() {return null;} public void destroy() { System.out.println("Servlet要離去了!"); } public String getServletInfo() {return null;} public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { System.out.println("hello servlet!"); } } |
在首次訪問HelloServlet時,init方法會被執行,而且也會執行service方法。再次訪問時,只會執行service方法,不再執行init方法。在關閉Tomcat時會呼叫destroy方法。
2Servlet介面相關型別
在Servlet介面中還存在三個我們不熟悉的型別:
l ServletRequest:service() 方法的引數,它表示請求物件,它封裝了所有與請求相關的資料,它是由伺服器建立的;
l ServletResponse:service()方法的引數,它表示響應物件,在service()方法中完成對客戶端的響應需要使用這個物件;
l ServletConfig:init()方法的引數,它表示Servlet配置物件,它對應Servlet的配置資訊,那對應web.xml檔案中的<servlet>元素。
2.1ServletRequest和ServletResponse
ServletRequest和ServletResponse是Servlet#service() 方法的兩個引數,一個是請求物件,一個是響應物件,可以從ServletRequest物件中獲取請求資料,可以使用ServletResponse物件完成響應。你以後會發現,這兩個物件就像是一對恩愛的夫妻,永遠不分離,總是成對出現。
ServletRequest和ServletResponse的例項由伺服器建立,然後傳遞給service()方法。如果在service() 方法中希望使用HTTP相關的功能,那麼可以把ServletRequest和ServletResponse強轉成HttpServletRequest和HttpServletResponse。這也說明我們經常需要在service()方法中對ServletRequest和ServletResponse進行強轉,這是很心煩的事情。不過後面會有一個類來幫我們解決這一問題的。
HttpServletRequest方法:
l String getParameter(String paramName):獲取指定請求引數的值;
l String getMethod():獲取請求方法,例如GET或POST;
l String getHeader(String name):獲取指定請求頭的值;
l void setCharacterEncoding(String encoding):設定請求體的編碼!因為GET請求沒有請求體,所以這個方法只只對POST請求有效。當呼叫request.setCharacterEncoding(“utf-8”)之後,再通過getParameter()方法獲取引數值時,那麼引數值都已經通過了轉碼,即轉換成了UTF-8編碼。所以,這個方法必須在呼叫getParameter()方法之前呼叫!
HttpServletResponse方法:
l PrintWriter getWriter():獲取字元響應流,使用該流可以向客戶端輸出響應資訊。例如response.getWriter().print(“<h1>Hello JavaWeb!</h1>”);
l ServletOutputStream getOutputStream():獲取位元組響應流,當需要向客戶端響應位元組資料時,需要使用這個流,例如要向客戶端響應圖片;
l void setCharacterEncoding(String encoding):用來設定字元響應流的編碼,例如在呼叫setCharacterEncoding(“utf-8”);之後,再response.getWriter()獲取字元響應流物件,這時的響應流的編碼為utf-8,使用response.getWriter()輸出的中文都會轉換成utf-8編碼後傳送給客戶端;
l void setHeader(String name, String value):向客戶端新增響應頭資訊,例如setHeader(“Refresh”, “3;url=http://www.itcast.cn”),表示3秒後自動重新整理到http://www.itcast.cn;
l void setContentType(String contentType):該方法是setHeader(“content-type”, “xxx”)的簡便方法,即用來新增名為content-type響應頭的方法。content-type響應頭用來設定響應資料的MIME型別,例如要向客戶端響應jpg的圖片,那麼可以setContentType(“image/jepg”),如果響應資料為文字型別,那麼還要臺同時設定編碼,例如setContentType(“text/html;chartset=utf-8”)表示響應資料型別為文字型別中的html型別,並且該方法會呼叫setCharacterEncoding(“utf-8”)方法;
l void sendError(int code, String errorMsg):向客戶端傳送狀態碼,以及錯誤訊息。例如給客戶端傳送404:response(404, “您要查詢的資源不存在!”)。
2.1ServletConfig
ServletConfig物件對應web.xml檔案中的<servlet>元素。例如你想獲取當前Servlet在web.xml檔案中的配置名,那麼可以使用servletConfig.getServletName()方法獲取!
ServletConfig物件是由伺服器建立的,然後傳遞給Servlet的init()方法,你可以在init()方法中使用它!
l String getServletName():獲取Servlet在web.xml檔案中的配置名稱,即<servlet-name>指定的名稱;
l ServletContext getServletContext():用來獲取ServletContext物件,ServletContext會在後面講解;
l String getInitParameter(String name):用來獲取在web.xml中配置的初始化引數,通過引數名來獲取引數值;
l Enumeration getInitParameterNames():用來獲取在web.xml中配置的所有初始化引數名稱;
在<servlet>元素中還可以配置初始化引數:
<servlet> <servlet-name>One</servlet-name> <servlet-class>cn.itcast.servlet.OneServlet</servlet-class> <init-param> <param-name>paramName1</param-name> <param-value>paramValue1</param-value> </init-param> <init-param> <param-name>paramName2</param-name> <param-value>paramValue2</param-value> </init-param> </servlet> |
在OneServlet中,可以使用ServletConfig物件的getInitParameter()方法來獲取初始化引數,例如:
String value1 = servletConfig.getInitParameter(“paramName1”);//獲取到paramValue1
GenericServlet
1GenericServlet概述
GenericServlet是Servlet介面的實現類,我們可以通過繼承GenericServlet來編寫自己的Servlet。下面是GenericServlet類的原始碼:
GenericServlet.java
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable { private static final long serialVersionUID = 1L; private transient ServletConfig config; public GenericServlet() {} @Override public void destroy() {} @Override public String getInitParameter(String name) { return getServletConfig().getInitParameter(name); } @Override public Enumeration<String> getInitParameterNames() { return getServletConfig().getInitParameterNames(); } @Override public ServletConfig getServletConfig() { return config; } @Override public ServletContext getServletContext() { return getServletConfig().getServletContext(); } @Override public String getServletInfo() { return ""; } @Override public void init(ServletConfig config) throws ServletException { this.config = config; this.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); } @Override public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; @Override public String getServletName() { return config.getServletName(); } } |
2GenericServlet的init()方法
在GenericServlet中,定義了一個ServletConfig config例項變數,並在init(ServletConfig)方法中把引數ServletConfig賦給了例項變數。然後在該類的很多方法中使用了例項變數config。
如果子類覆蓋了GenericServlet的init(StringConfig)方法,那麼this.config=config這一條語句就會被覆蓋了,也就是說GenericServlet的例項變數config的值為null,那麼所有依賴config的方法都不能使用了。如果真的希望完成一些初始化操作,那麼去覆蓋GenericServlet提供的init()方法,它是沒有引數的init()方法,它會在init(ServletConfig)方法中被呼叫。
3 實現了ServletConfig介面
GenericServlet還實現了ServletConfig介面,所以可以直接呼叫getInitParameter()、getServletContext()等ServletConfig的方法。
HttpServlet
1HttpServlet概述
HttpServlet類是GenericServlet的子類,它提供了對HTTP請求的特殊支援,所以通常我們都會通過繼承HttpServlet來完成自定義的Servlet。
2HttpServlet覆蓋了service()方法
HttpServlet類中提供了service(HttpServletRequest,HttpServletResponse)方法,這個方法是HttpServlet自己的方法,不是從Servlet繼承來的。在HttpServlet的service(ServletRequest,ServletResponse)方法中會把ServletRequest和ServletResponse強轉成HttpServletRequest和HttpServletResponse,然後呼叫service(HttpServletRequest,HttpServletResponse)方法,這說明子類可以去覆蓋service(HttpServletRequest,HttpServletResponse)方法即可,這就不用自己去強轉請求和響應物件了。
其實子類也不用去覆蓋service(HttpServletRequest,HttpServletResponse)方法,因為HttpServlet還要做另一步簡化操作,下面會介紹。
HttpServlet.java
public abstract class HttpServlet extends GenericServlet { protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { …… } @Override 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); } …… } |
3doGet()和doPost()
在HttpServlet的service(HttpServletRequest,HttpServletResponse)方法會去判斷當前請求是GET還是POST,如果是GET請求,那麼會去呼叫本類的doGet()方法,如果是POST請求會去呼叫doPost()方法,這說明我們在子類中去覆蓋doGet()或doPost()方法即可。
public class AServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletRespo |