1. 程式人生 > 其它 >深入瞭解tomcat中servlet的建立方式實現

深入瞭解tomcat中servlet的建立方式實現

Tomcat如何建立Servlet?

A.先到快取中尋找有沒有這個物件(Servlet是單例項的,只會建立一次)
如果沒有:
1、通過反射去建立相應的物件(執行構造方法)
2、tomcat會把物件存放到快取中
3、執行初始化方法init

B.執行服務方法(Service方法)

C.返回響應的資料到客戶端(瀏覽器

一、 什麼是servlet

1.1、用官方的話解釋:
Servlet是oracle公司提供的一門用於開發動態web資源的技術,屬於javaEE體系中的一種核心規範。
通俗解釋一下:就是我們開發人員所編寫的一個類,必須直接或者間接實現這個javaEE的核心規範,也就是實現Servlet介面,因為這種類產生的物件可以被瀏覽器訪問到,因此稱之為Servlet,並且javaEE中規定了只有Servlet的實現類產生的物件才可以被瀏覽器訪問,就是Servlet.(也就是說這個類要直接或者間接實現了Servlet介面)

二、開始進入servlet的建立

2.1、通過前面介紹,我們知道了一個什麼樣的類建立的物件可以被瀏覽器訪問,首先我們直接上程式碼:

package com.briup.web;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class FirstWay implements Servlet {
    public FirstWay() {
        System.out.println("物件建立了");
    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub
        System.out.println("我是init:我被呼叫了");
    }
    @Override
    public ServletConfig getServletConfig() {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        // TODO Auto-generated method stub
        System.out.println("我是service,我被呼叫了");  
    }
    @Override
    public String getServletInfo() {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        System.out.println("我是destory:我被呼叫了");
    }
}

那麼,一個滿足servlet的類已經建立好了,接下來丟擲疑問

servet物件由誰建立?

裡面實現的介面方法,哪些會呼叫,什麼時候呼叫,呼叫幾次?
第一個疑問: 既然是servlet類,由我們開發人員自己手動建立物件,顯然是不合理,所以這個物件的建立,是交給tomcat建立的,我們開發人員只需要告訴 tomcat,讓他建立,讓他什麼時候建立就行了;
如何告訴?

1、方法一:通過配置webxml的方式。(極其不推薦使用)

對於整個動態web專案而言,web.xml是最先載入的配置檔案,所以在web.xml的方式配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
 <display-name>firstWay</display-name>
 <servlet>
        <servlet-name>FirstWay</servlet-name>
        <servlet-class>com.briup.web.FirstWay</servlet-class>
        <!-- <load-on-startup>1</load-on-startup> -->
 </servlet>
 <servlet-mapping>
    <servlet-name>FirstWay</servlet-name>
    <url-pattern>/FirstWay</url-pattern>
 </servlet-mapping>
</web-app>

解釋:
1、servlet-name:見名知意:servlet的名字,注意要與下面你設定對映的名字對應
2、serlvet-class:serlvet的全限定名
3、load-on-startup:是否在tomcat啟動的時候就建立servlet物件,傳入一個大於0的整數‘'(預設是瀏覽器第一次請求的時候建立servlet物件)
4、servlet-mapping:見名知意,設定瀏覽器的訪問對映
5、servlet-name:於上面的對應
6、url-pattern:瀏覽器的訪問對映(假設預設是本機的話,且tomcat的埠號為8080,那麼瀏覽器訪問這個servlet的路徑為:localhost:8080/專案名/FirstWay)

有了這些基礎,讓我們訪問看看;

第一步:啟動tomcat
在這裡插入圖片描述
tomcat正常啟動

第二步:通過瀏覽器訪問(我們這裡手動訪問3次)
在這裡插入圖片描述
瀏覽器訪問正常

第三步:觀察控制檯
在這裡插入圖片描述

通過執行結果分析:

第一次啟動伺服器,物件並沒有被建立

瀏覽器請求三遍,但是物件只建立一次,init()方法也只調用一次

每訪問一次,物件便會呼叫一次service()方法

其他方法沒被呼叫
解釋為什麼沒被呼叫:
getServletConfig():得到ServletConfig物件;getServletInfo():得到Servlet的資訊,比如作者;
destroy():servlet銷燬的時候才會呼叫這個方法,(比如:tomcati正常關閉 這裡我就不去測試,想測試的小夥伴,可以右鍵service,點選stop)然後再觀察控制檯便可知了。

2、方法二:註解的方式告訴tomcat(與前者相比,推薦使用)

@WebServlet(value ="對映路徑")
public Fristservlet implement Servelt {
}

通過這個註解也可以設定,是否在啟動伺服器的時候就建立物件,這裡就不演示了,
注意:(一旦使用了註解的方式告訴tomcat如果建立某個物件,就不能在web.xml裡面再對這個servlet進行訪問設定了)

三、迴歸主題,servlet的第二種建立方式

有了前面的解釋,直接上程式碼然後再分析

package com.briup.web;
import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
@WebServlet(value = "/secondWay")
public class SecondWayCreate extends GenericServlet {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        // TODO Auto-generated method stub
        System.out.println("呼叫了service方法");
    }
}

1、比第一種方法簡潔,實現的是GenericServlet這個類

2、我們看一下GenericServlet原始碼,然後進行分析;

public abstract class GenericServlet implements Servlet, ServletConfig

可知,這是個抽線類,是servlet介面的實現類,那麼GenericServlet間接 實現了servlet介面,
與第一種方式相比:開發者不是必須將一些介面中不必要的方法實現,可以具有選擇性,減少了程式碼量。然而並沒有上面ruan用,就是裝13而已

三、重點第三種方式(與前兩者相比,我更推薦第三種方式)

直接上程式碼

package com.briup.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(value = "/ThreeWayCreate")
public class ThreeWayCreate extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        super.doGet(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        super.doPost(req, resp);
    }
}

通過以上程式碼,可能就有小夥伴要問了
不是說servlet要直接或者間接實現servlet介面嗎,不是說瀏覽器每請求一次就要呼叫一次service方法嗎?方法在哪呢?這不是與前面理論衝突了嗎?
我們繼續看原始碼,原始碼才是道理
我在下面值列舉原始碼裡面比較核心的部分,需要理解更加深入瞭解的小夥伴,直接去看原始碼,tomcat是開源的

package com.briup.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(value = "/ThreeWayCreate")
public class ThreeWayCreate extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        super.doGet(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        super.doPost(req, resp);
    }
}

分析:
第一步分析
在這裡插入圖片描述
可知這個抽象類繼承了GennericeServlet這個抽象類 也就是逐層往下推,實現了Servle介面,那麼這個抽線類必然也繼承了serice方法。
第二步分析
在這裡插入圖片描述
這個是繼承servlet介面的service方法,當瀏覽器每請求一次時,都會呼叫這個方法,由圖可知,這個方法已經被HttpServlet實現了,由實現類可以得出,請求物件req,和響應物件res,被強轉成了HttpServletRequest,和HttpServletResponse(向下轉型),然後將強轉的物件,傳入HttpServlet過載的Service方法中,呼叫,第三步,分析過載後的Service(HttpRequest req,HttpRespone res);
第三步分析

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) {
        // servlet doesn't support if-modified-since, no reason
        // to go through further expensive logic
        doGet(req, resp);
      } else {
        long ifModifiedSince;
        try {
          ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
        } catch (IllegalArgumentException iae) {
          // Invalid date header - proceed as if none was set
          ifModifiedSince = -1;
        }
        if (ifModifiedSince < (lastModified / 1000 * 1000)) {
          // If the servlet mod time is later, call doGet()
          // Round down to the nearest second for a proper compare
          // A ifModifiedSince of -1 will always be less
          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 {
      //
      // Note that this means NO servlet supports whatever
      // method was requested, anywhere on this server
      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);
    }
  }

通過傳過來的HttpRequest物件,判斷請求方式,通過請求方式,決定呼叫哪個方法(如果請求方式是post方式,那麼就會呼叫doPost(HttpRequest req,HttpRestpone Res)方法)

第四步分析
綜上分析,總結:tomcat建立物件,當瀏覽器請求的時候,呼叫Servlet的Service(ServeltRequest req,ServletRespone res )方法,然後這個方法再呼叫,HttpServlet裡面過載的Servlet(HttpServletReqeust req ,HttpServletRespone res)方法,然後這個方法會通過請求方式是什麼,選擇性的呼叫doPost(),還是doGet()方法(當然還有很多其他的方式這裡就不列舉了), 因此第三種方式,的本質還是當瀏覽器發起一次請求的時候呼叫了Servlet接口裡面的Service(ServeltRequest req,ServletRespone res )方法,然後通過實現類的裡面的邏輯,間接的呼叫了doPost()等方法。

優點:

1、通過請求方式可以處理相應的請求,使得邏輯更加清晰

2,減少程式碼量,是程式更加簡潔

3,使得請求或者響應的操作性更加豐富

4…

四、 總結:

注意點:瀏覽器發起請求呼叫的一定是servlet種的service方法
轉載地址:https://www.jb51.net/article/197292.htm