一. Servlet簡介
基於Java技術的Web組件,運行在服務器端,由Servlet容器管理,用於生成動態內容。
Servlet容器管理Servlet,負責Servlet和客戶的通信以及調用Servlet的方法。
Servlet功能:
- 接收用戶請求的http協議
- 返回http響應協議
Servlet優勢:
- Servlet 在 Web 服務器的地址空間內執行。這樣它就沒有必要再創建一個單獨的進程來處理每個客戶端請求。
- Servlet 是獨立於平臺的,因爲它是用 Java 編寫的。
- 服務器上的 Java 安全管理器執行了一系列限制,以保護服務器計算機上的資源。因此,Servlet 是可信的。
- Java 類庫的全部功能對 Servlet 來說都是可用的。它可通過 sockets 和 RMI 機制與 applets、數據庫或其他軟件進行交互。
Servlet框架:
Servlet實現方式:
- 實現javax.servlet.Servlet接口 (對應三)
- 繼承javax.servlet.GenericServlet類 (對應六)
- 繼承javax.servlet.http.HttpServlet類(常用方式) (對應七)
Servlet核心jar包:
tomcat/lib目錄下的servlet-api.jar
響應客戶請求的過程:
二. 在web.xml中配置Servlet
在新建項目的WebContent\WEB-INF\下,有web.xml配置文件。如果沒有,新建項目時,第三步需要勾選
在根標籤<web-app></webapp>中配置Servlet的相關信息,如下:
<servlet>
<!-- 聲明名字,可以任意 -->
<servlet-name>firstServlet</servlet-name>
<!-- 聲明類名字 -->
<servlet-class>com.qibao.servlet.firstServlet</servlet-class>
</servlet>
<!-- 配置如何訪問這個servlet -->
<servlet-mapping>
<!-- 必須和要和 某個聲明的servlet名字一樣 -->
<servlet-name>oneServlet</servlet-name>
<!-- 聲明訪問的url路徑 -->
<url-pattern>/first</url-pattern>
</servlet-mapping>
- Servlet程序必須通過Servlet容器來啓動運行,並且儲存目錄有特殊要求,需要存儲在<WEB應用程序目錄>\WEB-INF\classes\目錄中。
- Servlet程序必須在WEB應用程序的web.xml文件中進行註冊和映射其訪問路徑,纔可以被Servlet引擎加載和被外界訪問。
- 一個<servlet>元素用於註冊一個Servlet,它包含有兩個主要的子元素:<servlet-name>和<servlet-class>,分別用於設置Servlet的註冊名稱和Servlet的完整類名。
- 一個<servlet-mapping>元素用於映射一個已註冊的Servlet的一個對外訪問路徑,它包含有兩個子元素:<servlet-name>和<url-pattern>,分別用於指定Servlet的註冊名稱和Servlet的對外訪問路徑。
- 一個Servlet可映射多個URL,即多個<servlet-mapping>元素的<servlet-name>設置的值可以是同一個Servlet的註冊名。
- Servlet映射中可使用*通配符,但是隻能有兩種固定的格式:一種格式是“*.擴展名”;另一種格式是以正斜槓/開頭並以“/*”結尾,例如/action/*。/代表當前web應用的路徑,即http://localhost:8080/webapp/。
- * 兩邊不能同時出現數據 /action/*do
<load-on-startup>1</load-on-startup>
在<servlet></servlet>標籤中添加,容器加載web應用時,就創建此servlet實例,並調用init()方法.需要一個大於0的整數.值越小優先級越高.
不加這個選項只有當頁面第一次發送請求時纔去創建servlet,調用init().
通過註解的方式註冊Servlet:
@WebServlet("/firstServlet")
- @WebServlet 用於將一個類聲明爲 Servlet
- @WebInitParam該註解通常不單獨使用,而是配合 @WebServlet 或者 @WebFilter 使用。它的作用是爲 Servlet 或者過濾器指定初始化參數
屬性名 |
類型 |
描述 |
name |
String |
指定 Servlet 的 name 屬性。如果沒有顯式指定,則該 Servlet 的取值即爲類的全限定名。 |
value |
String[] |
該屬性等價於 urlPatterns 屬性 |
urlPatterns |
String[] |
指定一組 Servlet 的 URL 匹配模式 |
loadOnStartup |
int |
指定 Servlet 的加載順序 |
initParams |
WebInitParam[] |
指定一組 Servlet 初始化參數 |
asyncSupported |
boolean |
聲明 Servlet 是否支持異步操作模式 |
description |
String |
該 Servlet 的描述信息 |
displayName |
String |
該 Servlet 的顯示名,通常配合工具使用 |
Servlet的調用過程:
三. 實現Servlet接口
開發Servlet的第一種方式——實現Servlet接口。
Servlet接口中有六個方法需要實現:
public class FirstServlet implements Servlet {
//tomcat服務停止時,初始化Servlet的方法,由tomcat調用,只執行一次。
@Override
public void init(ServletConfig arg0) throws ServletException {}
//用戶的每一次請求,都會執行這個方法,由用戶發出請求,由tomcat調用這個方法
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {}
//tomcat服務停止時,服務器會去銷燬Servlet,由tomcat調用,用於清空某些資源,只調用一次
@Override
public void destroy() {}
//獲取這個servlet在web.xml中的配置信息
@Override
public ServletConfig getServletConfig() {return null;}
//獲取這個serlvet的名稱
@Override
public String getServletInfo() {return null;}
}
四. Servlet生命週期
指的就是Servlet的出生到結束。分別經歷了加載、初始化、服務、銷燬。
- 加載階段:加載並實例化,創建servlet實例,只調用一次構造方法;
- 初始化階段: 調用init()方法,只調用一次初始化方法;
- 響應客戶請求階段:調用service()方法,一般業務邏輯在這裏處理;
- 終止階段:調用destroy()方法。
當請求servlet的時候:
- Servlet引擎檢查是否已經裝載並創建了該Servlet的實例對象。如果是,則直接執行第④步,否則,執行第②步。
- 裝載並創建該Servlet的一個實例對象:調用該 Servlet 的構造器
- 調用Servlet實例對象的init()方法。
- 創建一個用於封裝請求的ServletRequest對象和一個代表響應消息的ServletResponse對象,然後調用Servlet的service()方法並將請求和響應對象作爲參數傳遞進去。
- WEB應用程序被停止或重新啓動之前,Servlet引擎將卸載Servlet,並在卸載之前調用Servlet的destroy()方法。
五. ServletConfig接口
功能:讀取web.xml中配置的serlvet信息。
Servlet容器將代表當前web應用的對象(ServletContext)和Servlet的配置參數信息一併封裝到一個稱爲ServletConfig的對象中,並在初始化Servlet實例對象時調用init(ServletConfig config)方法將ServletConfig對象傳遞給Servlet。
常見方法(可在初始化時通過傳入的參數ServletConfig config調用):
- getServletName() 獲取當前Servlet在web.xml中配置的名字
- getServletContext() 獲取代表當前web應用的ServletContext對象
- getInitParameter(String) 獲取當前Servlet指定名稱的初始化參數的值
- getInitParameterNames() 獲取當前Servlet所有初始化參數的名字組成的枚舉
配置web.xml
<servlet>
<servlet-name>secondServlet</servlet-name>
<servlet-class>com.qibao.servlet.SecondServlet</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>admin</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>secondServlet</servlet-name>
<url-pattern>/second.do</url-pattern>
</servlet-mapping>
在init(ServletConfig config)方法中調用:
@Override
public void init(ServletConfig config) throws ServletException {
System.err.println("在配置文件中的名稱:" + config.getServletName());
//獲取所有初始化參數的名字組成的枚舉
Enumeration<String> en = config.getInitParameterNames();
while (en.hasMoreElements()) {
String nm = (String) en.nextElement();
// 根據初始化的名字獲取值
String value = config.getInitParameter(nm);
System.err.println(nm + ":" + value);
}
}
六. GenericServlet類
開發Servlet的第二種方式——繼承GenericServlet類。
省略在web.xml中配置的環節,直接看代碼
public class SecondServlet extends GenericServlet{
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {}
}
- GenericServlet提供除service()方法外所有Servlet接口中方法的缺省實現,GenericServlet的方法,只有service(req,res)是抽象方法,只需要實現這個方法就好
- GenericServlet也實現了ServletConfig接口,處理初始化參數和servlet上下文,提供對授權傳遞到init()方法中的ServletConfig對象的獲取方法getServletConfig()。
- 如果要做初始化操作,可重寫GenericServlet中的init()方法 。而非init(servleconfig)帶參數的方法。 init() --- 此方法不是serlvet的生命週期方法,只是被GenericSerlvet中的init(servleconfig)再次調用的方法 。
1:實現了javax.serlvet.Selvet接口,用戶只需要重寫service方法;
2:包裝了SevletConfig用戶可直接調用。
七. HttpServlet類
開發Servlet的第三種方式——繼承HttpServlet類。
- HttpServlet是GenericServlet的子類
- HttpServlet 類通過調用指定到HTTP請求的方法實現service()
- 通過HttpServlet去開發servlet,需要重寫doGet、doPost方法
@WebServlet("/thirdServlet")
public class ThirdServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public ThirdServlet() {super();}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 調用 MyServlet的service(ServletRequest, ServletResponse)方法,但MyServlet沒有重寫這個方法,所以到MyServlet的父類HttpServlet去找;
- 調用HttpServlet的service(ServletRequest, ServletResponse)方法,它又調用了service(HttpServletRequest, HttpServletResponse);
- HttpServlet的service(HttpServletRequest, HttpServletResponse)方法,它先對請的方法進行判斷,如果是GET請求,則調用doGet()方法,如果是POST請求,則調用doPost()方法;
- 如果在MyServlet中重寫了 doGet(),doPost()方法,則會調用MyServlet中的重寫的響應的doGet(),doPost()方法。
注:必須要重寫至少一個doXXX方法,如果沒有重寫doXXX方法,則會報405錯誤。
八. 線程安全問題
servlet不是線程安全的。Servlet體系結構建立在Java多線程機制之上,它的生命週期由Web容器負責。當客戶端第一次請求某個Servlet時,Servlet容器將會根據配置文件實例化這個Servlet類。當有新的客戶端請求該Servlet時,一般不會再實例化該Servlet類,也就是有多個線程在使用這個實例。當兩個或多個線程同時訪問同一個Servlet時,可能會發生多個線程同時訪問同一資源的情況,數據可能會變得不一致。可參考售票系統.
解決方法:
- 使用 synchronized同步代碼塊。效率太低,不建議使用
- 不依賴實例全局成員變量,將接收參數的變量聲明成局部變量。因爲,局部變量默認就是線程安全的。局部變量在線程的棧中,先進後出。
九. ServletContext接口
ServletContext是一個全局的儲存信息的空間,服務器開始就存在,服務器關閉才釋放。
一個web項目,只存在一個ServletContext實例,每個Servlet都可以訪問到它,用於共享數據。
獲取方法:
ServletConfig和GenericServlet的getServletContext()方法
功能:
- 獲取WEB應用程序的初始化參數。
- //配置全局初始化參數
<context-param>
<param-name>name</param-name>
<param-value>張三</param-value>
</context-param>
//獲取全局初始化參數
ServletContext ctx = getServletContext();
Enumeration<String> en = ctx.getInitParameterNames();
while (en.hasMoreElements()) {
String nm = (String) en.nextElement();
//根據初始化的名字獲取值
String value = ctx.getInitParameter(nm);
out.print(nm+":"+value+"<hr>");
}
- 獲取項目的真實的路徑。
//獲取真實路徑(在服務器上的絕對路徑)
response.getWriter().print(ctx.getRealPath("/imgs")+"<hr>");
//獲取項目應用上下文(/webapp名稱)
response.getWriter().print(ctx.getContextPath()+"<hr>");
- 做爲域對象,保存多個客戶共享的數據。