在瞭解Servlet之前,我們首先需要知道Servlet的生命週期,Servlet的生命週期分爲三個階段:
1. init
2. service
3. destroy
一個Servlet一生只實例化一次(除非實現了SingleThreadModel,該api在2.4已經被標記爲@deprecated),只會調用一次init和destroy,所以servlet是單例多線程。
瞭解Servlet需要知道的知識點:
1. servlet三個生命週期方法在什麼時候執行
2. ServletConfig和ServletContext的是什麼
3. 什麼是servlet-mapping,規則是什麼
一、Servlet三個生命週期方法在什麼時候執行
- 初始化init方法會在服務器啓動時或者第一次請求到來時執行,這取決於web.xml是否設置
<load-on-startup>1</load-on-startup>
,只要設置了loadOnStartup並且值大於0,那麼該servlet將會在服務器啓動完成之前完成初始化。 - service方法會在客戶端請求對應的url-mapping時調用,容器會將請求信息封裝成一個HttpServletRequest,將輸出信息封裝成一個HttpServletResponse,然後作爲參數傳遞到service方法中。
- destroy方法將會在服務器停止時調用。
那麼,這三個生命週期方法有什麼作用呢?init方法通常做一些初始化的操作,比如數據庫連接,init方法中會得到一個ServletConfig,可通過ServletConfig.getInitParameter(name)
拿到初始化的值來進行初始化工作,當然,你也可以直接定義局部變量進行初始化工作(除非你確定該變量不會變動,否則儘量通過配置ServletConfig InitParameter的方式,方便維護)。service方法是用來響應客戶端請求輸出內容返回到客戶端的,一般來說,我們更傾向於重寫doGet和doPost請求來響應不同請求方式,一個請求只會執行一個Servlet。destroy主要用於一些資源釋放的操作。
二、ServletConfig和ServletContext的是什麼
上面我們提到了ServletConfig,那麼什麼是ServletConfig,怎麼配置InitParameter,和ServletContext有什麼區別?
ServletConfig是一個作用域僅限於當前Servlet的容器,而ServletContext能被所有Servlet共享。
對於ServletConfig來說,配置InitParameter只能在web.xml中的webapp.servlet.init-param進行配置,它沒有對應的setXXX方法來設置參數。簡單的配置如下:
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.hxl.MyServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>hxl</param-value>
</init-param>
<init-param>
<param-name>date</param-name>
<param-value>2018</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
我們在Java程序中以下代碼可以得到配置的參數值
public void init(ServletConfig config) throws ServletException {
Enumeration<?> names = config.getInitParameterNames();
while(names.hasMoreElements()) {
String key = (String) names.nextElement();
System.out.println(key+":"+config.getInitParameter(key));
}
}
打印如下:
date:2018
name:hxl
對於ServletContext來說,可以通過webapp.context-param配置InitParameter,除了InitParameter,ServletContext還可以通過setAttribute的方式設置屬性,任何Servlet都可以通過getAttribute拿到該屬性,所以,attribute的使用需要謹慎小心,因爲可能會存在併發的問題。記住,ServletContext是所有Servlet共享的。
xml配置InitParameter:
<context-param>
<param-name>sql</param-name>
<param-value>mysql</param-value>
</context-param>
Java代碼獲取初始化參數,設置屬性
ServletContext servletContext = config.getServletContext();
//獲取InitParameter
String parameter = servletContext.getInitParameter("sql");
System.out.println(parameter);
//設置屬性,記錄請求數
servletContext.setAttribute(TOTAL_REQUEST, 0);
ServletContext servletContext = request.getSession().getServletContext();
//忽略併發代碼...
int total =Integer.parseInt(servletContext.getAttribute(TOTAL_REQUEST)+"");
System.out.println("totalRequest:"+(total+1));
servletContext.setAttribute(TOTAL_REQUEST, total+1);
這樣,當我們每請求一次,totalRequest就會+1
totalRequest:1
totalRequest:2
totalRequest:3
三、什麼是servlet-mapping,規則是什麼
servlet-mapping是用於將客戶端請求映射到指定的servlet,匹配的規則如下:
1. Map exact URL–精確匹配
2. Map wildcard paths–通配符匹配
3. Map extensions–擴展名匹配
4. Map to the default servlet–匹配到默認servlet
例如:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
當請求URL爲“/abc/a.html”,調用Servlet1。
當請求URL爲“/abc”時,調用Servlet3。
當請求URL爲“/abc/a.do”時,調用Servlet1。
當請求URL爲“/a.do”時,調用Servlet2。
當請求URL爲“/xxx/yyy/a.do”時,調用Servlet2。
我們一般都知道匹配規則的前三條,少部分人不知道第四條的default servlet是什麼,瞭解default servlet之前,我們先了解<url-pattern>/</url-pattern>
和<url-pattern>/*</url-pattern>
的區別。
1、如果我們寫的servlet(我們起類名爲MyServlet)使用的url-pattern是/*,那麼會響應一切url請求,就沒有default servlet什麼事了。
2、如果使用的是/,那麼當我們請求的路徑是.jsp爲後綴的時候(這種情況下也僅僅能處理.jsp結尾的url),該請求不再經過MyServlet,而是匹配到default servlet。例如請求的是/abc/index.jsp,那麼服務器會去abc文件夾(與WEB-INF文件夾同級)下查找index.jsp文件,如果請求/WEB-INF/index.jsp,即使存在該jsp,也請求不到任何資源,因爲WEB-INF文件夾裏的文件是受保護的。
3、如果使用的是/app/*,那麼任何不匹配/app/*的url都將匹配到default servlet,服務器會將該url當成是靜態資源文件路徑去查找相應的文件信息。這估計是最有效處理靜態資源的方式了。
最後,附上Servlet和web.xml的代碼
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet {
private static final String TOTAL_REQUEST="totalRequest";
@Override
public void init(ServletConfig config) throws ServletException {
Enumeration<?> names = config.getInitParameterNames();
while(names.hasMoreElements()) {
String key = (String) names.nextElement();
System.out.println(key+":"+config.getInitParameter(key));
}
ServletContext servletContext = config.getServletContext();
//獲取InitParameter
String parameter = servletContext.getInitParameter("sql");
System.out.println(parameter);
//設置屬性,記錄請求數
servletContext.setAttribute(TOTAL_REQUEST, 0);
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = request.getSession().getServletContext();
//忽略併發代碼...
int total =Integer.parseInt(servletContext.getAttribute(TOTAL_REQUEST)+"");
System.out.println("totalRequest:"+(total+1));
servletContext.setAttribute(TOTAL_REQUEST, total+1);
}
@Override
public void destroy() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="cyt" version="3.0">
<welcomefiles>
<welcomefile>/index.jsp</welcomefile>
</welcomefiles>
<context-param>
<param-name>sql</param-name>
<param-value>mysql</param-value>
</context-param>
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.hxl.MyServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>hxl</param-value>
</init-param>
<init-param>
<param-name>date</param-name>
<param-value>2018</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 這裏不能設置爲/*,否則default無效 -->
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 默認servlet,用於處理js靜態文件
Tomcat, Jetty, JBoss, and GlassFish 自帶的默認Servlet的名字:"default"
Google App Engine 自帶的 默認Servlet的名字:"_ah_default"
Resin 自帶的 默認Servlet的名字:"resin-file"
WebLogic 自帶的 默認Servlet的名字:"FileServlet"
WebSphere 自帶的 默認Servlet的名字:"SimpleFileServlet"
-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
</web-app>