深入了解tomcat中servlet的創建方式
一、 什么是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用,就是裝b而已
三、重點第三種方式(與前兩者相比,我更推薦第三種方式)
直接上代碼
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是開源的
public abstract class HttpServlet extends GenericServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
}
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);
}
}
/*
* Sets the Last-Modified entity header field, if it has not
* already been set and if the value is meaningful. Called before
* doGet, to ensure that headers are set before response data is
* written. A subclass might have set this header already, so we
* check.
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
}
}
分析:
- 第一步分析

可知這個抽象類繼承了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.uj5u.com/shujuku/169180.html
標籤:其他
下一篇:Java~util包中Timer的使用, 演示cancel方法 和 對比schedule和scheduleAtFixedRate方法
