1. 程式人生 > 實用技巧 >這些Servlet知識你一定要知道,金九銀十大廠面試官都愛問

這些Servlet知識你一定要知道,金九銀十大廠面試官都愛問

前言

Servlet是伺服器端的Java應用程式,可以生產動態Web頁面。透過JSP執行過程可以知道JSP最終被編譯成一個.class檔案,檢視該檔案對應的Java類,發現該Java類繼承自org.apache.jasper.runtime.HttpJspBase類,而HttpJspBase繼承自HttpServlet類,由此可知JSP第一次執行時實質上是被JSP引擎翻譯成了一個Servlet,然後再編譯,最後再執行。

自定義Servlet類繼 承HttpServlet抽象類,HttpServlet抽象類繼承自GenericServlet抽象類,GenericServlet抽象類實現了Servlet、ServletConfig和Serializable介面

Servlet宣告週期:

1、載入及例項化

Servlet容器負責載入和例項化Servlet。當客戶端第一次(在web.xml檔案中,通過load-on-startup標籤可以配置Servlet,當web專案釋出後立即建立Servlet例項) 給伺服器傳送該Servlet請求時,Servlet容器會載入並建立Servlet例項,(注意:預設情況下不是Tomcat伺服器或伺服器上的Web應用啟動的時候載入並例項化Servlet)。當客戶端(可以是非第一次請求的客戶端)再次向伺服器傳送該Servlet請求時,伺服器會從記憶體中查詢該Servlet例項,並用找到的Servlet例項處理使用者請求。

在該過程中,Servlet容器會建立一個ServletConfig物件,該物件包含了Servlet的初始化配置資訊。根據使用者請求的URL地址,Servlet容器會根據配置資訊查詢該請求對應的Servlet類,由容器建立並管理該Servlet。

2、初始化

在Servlet容器完成Servlet類的例項化操作後,Servlet容器會呼叫Servlet的init()方法(在javax.servelt.Servlet介面中定義)對該Servlet進行初始化。對於每一個Servlet例項來說,init()方法只會被呼叫一次。初始化的目的是讓Servlet在處理使用者請求之前,做一些必要的準備工作,例如建立資料庫連線,引用其他資源等。

3、處理請求

Servlet初始化之後,就處於就緒狀態等待接收使用者請求。當Servlet容器接收到客戶端針對該Servlet的請求後,首先會針對這個請求建立ServletRequest和ServletResponse物件,之後呼叫Servlet的service()方法並把這兩個引數傳遞給service()方法處理客戶端請求。Servlet例項通過ServletRequest物件獲得客戶端的請求,通過呼叫ServletResponse物件的方法進行響應。請求處理完畢,ServletRequest和ServletResponse物件被銷燬。

不管客戶端傳送請求的方式是Get還是POST,這個請求都由service方法來處理。在service方法的處理過程中,會根據客戶端傳送請求的方式不同,呼叫doGet和doPost方法分別進行處理,通過HttpServlet類中的service方法可以瞭解這一呼叫過程,如下程式碼:

protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
        String method = req.getMethod();
        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }
        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);
        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

  

4、銷燬

銷燬Servlet 由Servlet容器完成。預設情況下,使用者第一次傳送Servlet請求,該Servlet載入、例項化、初始化、處理使用者請求,當請求處理完畢後,該Servlet通常情況下駐留在記憶體中,等待處理下一個針對該Servlet的請求。當下一個針對該Servlet的請求到達時,直接從記憶體中獲取該Servlet例項並對該請求進行處理。如果Tomcat這個Web應用伺服器關閉(伺服器上所有的Web應用都關閉),或者該Servlet所在的Web應用關閉,該Servlet例項會被銷燬。

Web應用被關閉時,Servlet容器會先呼叫Servlet例項的destroy方法,然後再銷燬Servlet例項,同時也會銷燬與Servlet相關聯的ServletConfig物件。程式設計師通常在destroy()方法的實現中釋放該Servlet所佔用的資源,如關閉資料庫連線,關閉檔案輸入/輸出流等。

通過Servlet宣告週期可以知道所建立的Servlet物件屬於單例。

Servlet2.X配置

在web.xml檔案中,通過在<web-app>節點下配置servlet元素和servlet-mapping元素,把使用者訪問的URL對映到指定的Servlet類,如下程式碼:

<web-app>
  <!-- 省略其他配置 -->
  <servlet>
    <!-- servlet-name指定Servlet名,要與下面servlet-mapping元素中的servlet-name保持一致 -->
    <servlet-name>doLogin</servlet-name>
    <!-- servlet-class對應著Servlet類完全限定名 -->
    <servlet-class>com.jd.serlvet.LoginServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <!-- servlet-name要與上面servlet元素中的servlet-name保持一致 -->
    <servlet-name>doLogin</servlet-name>
    <!-- url-pattern設定當前Servlet在瀏覽器中執行時的url -->
    <url-pattern>/doLogin</url-pattern>
  </servlet-mapping>
</web-app>

  

上面採用了精確匹配的形式配置了URL到Servlet之間的對映關係,接下來介紹兩種非精確匹配的Servlet配置方式:

<!-- 對doLogin路徑下的所有請求都由doLogin對應的Servlet類進行處理 -->
  <servlet-mapping>
    <servlet-name>doLogin</servlet-name>
    <url-pattern>/doLogin/*</url-pattern>
  </servlet-mapping>
  
  <!-- 對所有以.do為字尾的請求都由doLogin對應的Servlet類進行處理 -->
    <servlet-mapping>
    <servlet-name>doLogin</servlet-name>
    <!-- 不能為/*.do -->
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

  

在配置了URL與Servlet的對映後,當Servlet容器收到一個請求時,首先確定是由哪個Web應用響應這個請求,然後從該Web應用的web.xml檔案中查詢URL對應的Servlet類進行處理。

Servlet初始化引數設定

在web.xml檔案中配置Servlet時,還可以在servlet元素中新增init-param元素預先對Servlet進行初始化設定,當Servlet載入時即可從該Servlet配置檔案中獲取初始化引數。

  <!-- 省略其他配置 -->
  <servlet>
    <servlet-name>doLogin</servlet-name>
    <servlet-class>com.jd.serlvet.LoginServlet</servlet-class>
    <!-- 配置多個初始化引數,則需要寫多個init-param元素 -->
    <init-param>
            <param-name>name</param-name>
            <param-value>Tom</param-value>
    </init-param>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>doLogin</servlet-name>
    <url-pattern>/doLogin</url-pattern>
  </servlet-mapping>

  

如何獲取:

a、在無參init方法中直接呼叫getInitParameter(String name)方法即可,如下程式碼:

@Override
public void init() throws ServletException {
    String name = getInitParameter("name");
    System.out.println(name);
}

  

b、在引數為ServletConfig的方法中呼叫ServletConfig內getInitParameter(String name)方法即可,如下程式碼:

@Override
public void init(ServletConfig config) throws ServletException {
    String name = config.getInitParameter("name");
    System.out.println(name);
}

  

Servlet上下文(環境物件)初始化引數設定

有時候不僅需要針對單個Servlet進行初始化引數設定,還需要對包含該Web引用中所有Servlet的環境物件進行初始化引數設定,使該引數能被所有的Servlet共享,如下程式碼:

  <!-- 省略其他配置 -->
  <!-- 配置多個初始化引數,則需要寫多個context-param元素 -->
   <context-param>
    <param-name>name</param-name>
    <param-value>Tom</param-value>
   </context-param>
 
  <servlet>
    <servlet-name>doLogin</servlet-name>
    <servlet-class>com.jd.serlvet.LoginServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>doLogin</servlet-name>
    <url-pattern>/doLogin</url-pattern>
  </servlet-mapping>
</web-app>

  

如何獲取:

a、在無參init方法中直接呼叫getServletContext ()方法獲取Servlet上下文物件,然後使用該物件呼叫getInitParameter方法即可,如下程式碼:

@Override
public void init() throws ServletException {
    String name = getServletContext ().getInitParameter("name");
    System.out.println(name);
}

  

b、在引數為ServletConfig的方法中呼叫ServletConfig內getServletContext ()方法獲取Servlet上下文物件,然後使用該物件呼叫getInitParameter方法即可,如下程式碼:

@Override
public void init(ServletConfig config) throws ServletException {
    ServletContext servletContext = config.getServletContext();
    String name = servletContext.getInitParameter("name");
    System.out.println(name);
}

  

Servlet 3.0

Servlet API包含javax.servlet和javax.servlet.http兩個包,從Servlet 3.0開始,為了實現Servlet3.0的一些新特性,又增加了javax.sevlet.annotation和javax.sevlet.descriptor兩個包,Tomcat伺服器必須是7.0及其以上版本

Servlet3.0的重大革新之一是支援註解,通過使用註解定義並部署Servlet,程式設計師無須在web.xml檔案中配置Servlet,如下程式碼:

@WebServlet(name="doLogin",urlPatterns="/doLogin",initParams={@WebInitParam(name="name",value="Tom")})
public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.sendRedirect("success.jsp");
    }
}

  

說明:

name屬性:指定Servlet名,類似於web.xml中servlet-name元素;

urlPatterns屬性:指定訪問URL,類似於web.xml中的url-pattern元素;可以是陣列,URL之間使用逗號間隔。

initParams屬性:設定初始化引數,該屬性中使用@WebInitParam註解設定單個初始化引數;在@WebInitParam註解中,name屬性指定引數名,類似於web.xml中的param-name元素;value屬性指定引數值,類似於web.xml中的param-value元素。可以在initParams屬性中配置多個@WebInitParam註解,用於初始化多個引數。

wb.xml:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >
 
<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <context-param>
        <param-name>name</param-name>
        <param-value>lucy</param-value>
  </context-param>
  <servlet>
    <servlet-name>TestServlet</servlet-name>
    <display-name>TestServlet</display-name>
    <description></description>
    <servlet-class>com.jd.servlet.TestServlet</servlet-class>
    <init-param>
        <param-name>mobile</param-name>
        <param-value>120</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/TestServlet</url-pattern>
  </servlet-mapping>
</web-app>

  

TestServlet.java:

package com.jd.servlet;
 
import java.io.IOException;
 
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
/**
 * Servlet implementation class TestServlet
 */
public class TestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       //Servlet物件屬於單例項(程式執行結束之前,快取中只有一個物件存在)
    public TestServlet() {
        //用於為成員變數賦值,會觸發物件建立:預設情況下第一次使用該Servlet時建立物件執行;
        //web.xml中加入<load-on-startup>1</load-on-startup>,在tomcat啟動時物件建立,TestServlet執行,隨之init()方法也立即執行
        
        super();
        System.out.println("TestServlet"+this);
       
    }
 
    //
    @Override
    public void destroy() {
        super.destroy();
         System.out.println("destroy"+this);
    }
 
 
    @Override
    public void init() throws ServletException {//初始化;對於每一個Servlet例項來說,建立物件執行init()方法,並且只會被呼叫一次
        super.init();
        String name=getServletContext().getInitParameter("name");//呼叫公共引數
        System.out.println("1111111"+name);
        System.out.println("init())"+this);
    }
    
 
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        String name=config.getServletContext().getInitParameter("name");//呼叫公共引數
        System.out.println("你好"+name);
        String mobile=config.getInitParameter("mobile");//呼叫私人蔘數
        System.out.println("快打"+mobile);
        System.out.println("init(config))"+this);
    }
 
    
    
    //請求最先到達service判斷執行哪個方法,doGet還是doPost
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.service(req, resp);
        System.out.println("service"+this);
    }
 
    //a標籤,form表單method是 get方法   非同步預設type是get方法
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         System.out.println("doGet"+this);
    }
    //form表單method是 post方法  非同步指定type是post方法
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         System.out.println("doPost"+this);
    }
 
}

  

最後

感謝你看到這裡,看完有什麼的不懂的可以在評論區問我,覺得文章對你有幫助的話記得給我點個贊,每天都會分享java相關技術文章或行業資訊,歡迎大家關注和轉發文章!