web筆記四:認識Servlet

在瞭解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三個生命週期方法在什麼時候執行

  1. 初始化init方法會在服務器啓動時或者第一次請求到來時執行,這取決於web.xml是否設置<load-on-startup>1</load-on-startup>,只要設置了loadOnStartup並且值大於0,那麼該servlet將會在服務器啓動完成之前完成初始化。
  2. service方法會在客戶端請求對應的url-mapping時調用,容器會將請求信息封裝成一個HttpServletRequest,將輸出信息封裝成一個HttpServletResponse,然後作爲參數傳遞到service方法中。
  3. 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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章