Servlet(一 基礎介紹)

 Servlet(Server Applet),全稱Java Servlet。   對這個Servlet一直不瞭解,也沒有學習過,如今進行各基礎瞭解學習。
html

 Servlet是用Java編寫的服務端程序,其主要功能在於交互式地瀏覽和修改數據,生成動態的Web內容。Servlet運行於支持Java的應用服務器中。從實現上講,Servlet能夠響應任何類型的請求,但絕大多數狀況下Servlet只用來擴展基於HTTP協議的Web服務器。java

 Servlet的工做模式:程序員

1:客戶端發送求情到服務器web

2:服務器啓動並調用Servlet, Servlet根據客戶端請求生成響應內容並將其傳給服務器數據庫

3:服務器將響應返回給客戶端安全

Servlet的主要相關類服務器

其中GenericServlet下面還有一個HttpServlet class.網絡

Servlet接口中定義的方法多線程

 1 public interface Servlet {
 2 
 3 public void init(ServletConfig config) throws ServletException;
 4 
 5 public ServletConfig getServletConfig();
 6 
 7 public void service(ServletRequest req, ServletResponse res)
 8             throws ServletException, IOException;
 9 
10 public String getServletInfo();
11 
12 public void destroy();
13 
14 }

 根據上面Servlet的接口函數,描述一下Servlet的運行過程併發

 Servlet程序是由WEB服務器調用,web服務器收到客戶端的Servlet請求訪問後,

1:web服務器首先檢查是否已經裝在並建立了該Servlet的實例對象,若是已經建立,則直接到第四步;不然執行第二步;

2:裝載並建立該servlet的一個實例對象;

3:調用Servlet實例對象的init()方法;

4:建立一個用於封裝Http請求消息的HttpServletRequest對象和一個表明HttpServletResponse對象,而後調用Servlet的Service()方法並將請求和響應對象做爲參數傳進去。

5:web應用程序被停職或從新啓動以前,servlet引擎將卸載servlet,並在卸載以前調用Servlet的destory()方法。

 Servlet 的生命週期

其中,init(), service(),destory()是servlet生命週期的方法。表明了Servlet從出生到工做,再到死亡的過程。Servlet容器(例如Tomcat)會根據下面的規則來調用這三個方法。

1,init(),當Servlet第一次被請求時,Servlet容器就會開始調用這個方法來初始化一個Servlet對象出來,可是這個方法不會再後續請求中被Servlet容器調用,就像人是能出生一次同樣。咱們能夠利用init()方法來執行相應的初始化工做。調用這個方法時,Servlet容器會傳入一個ServletConfig對象進來從而對Servlet對象進行初始化。

2,Service()方法,每當請求Servlet時,Servlet就會調用這個方法。就像人同樣,須要不停的接受老闆的指令而且工做。第一次請求時,Servlet容器會先調用init()方法初始化一個Servlet對象出來,而後調用它的service()方法進行工做。但在後續請求中,servlet容器只會調用service方法了。

3,destory, 當要銷燬Servlet時,Servlet容器就會調用這個方法,就如人同樣。到時候就會死亡。在卸載應用程序或者關閉Servlet容器時,就會發生這種狀況,通常在這個方法中會寫一些清除代碼。

Servlet 的其它兩個方法

getServletInfo( ),這個方法會返回Servlet的一段描述,能夠返回一段字符串。

getServletConfig( ),這個方法會返回由Servlet容器傳給init( )方法的ServletConfig對象。

 此處應該增長例子,可是在我添加例子過程當中,常常出現找不到servlet,出現404錯誤。

後面找到真正緣由後,再添加例子

按照博客中給的例子進行驗證,經常出現http404錯誤。

 

GenericServlet抽象類

前面咱們編寫Servlet一直是經過實現Servlet接口來編寫的,可是,這種方法必須實現接口servlet中定義的全部方法。即便有一些方法中沒有任何東西也要實現。而且還須要本身手動的維護ServletConfig這個對象的引用。所以,這樣去實現Servlet是比較麻煩的。幸虧,GenericServlet抽象類的出現很好的解決了這個問題。本着儘量使代碼簡潔的原則,GenericServlet實現了Servlet和ServletConfig接口。 GenericServlet類實現:

 1 public abstract class GenericServlet implements Servlet, ServletConfig,
 2         java.io.Serializable {
 3    
 4  @Override
 5     public void destroy() {
 6         // NOOP by default
 7     }
 8 
 9     @Override
10     public ServletConfig getServletConfig() {
11         return config;
12     }
13 
14     @Override
15     public String getServletInfo() {
16         return "";
17     }
18 
19     @Override
20     public void init(ServletConfig config) throws ServletException {
21         this.config = config;
22         this.init();
23     }
24 
25     public void init() throws ServletException {
26         // NOOP by default
27     }
28 
29     @Override
30     public abstract void service(ServletRequest req, ServletResponse res)
31             throws ServletException, IOException;
32 
33 }
其中,GenericServlet抽象類相比於直接實現Servlet接口,有如下幾個好處:

 1.爲Servlet接口中的大部分方法提供了默認的實現,則程序員須要什麼就直接改什麼,再也不須要把全部的方法都本身實現了。

 2.將init( )方法中的ServletConfig參數賦給了一個內部的ServletConfig引用從而來保存ServletConfig對象,不須要程序員本身去維護ServletConfig了。

可是,咱們發如今GenericServlet抽象類中還存在着另外一個沒有任何參數的Init()方法:

public void init() throws ServletException {
}

設計者的初衷究竟是爲了什麼呢?在第一個帶參數的init()方法中就已經把ServletConfig對象傳入而且經過引用保存好了,完成了Servlet的初始化過程,那麼爲何後面還要加上一個不帶任何參數的init()方法呢?

抽象類是沒法直接產生實例的,須要另外一個類去繼承這個抽象類,那麼就會發生方法覆蓋的問題,若是在類中覆蓋了GenericServlet抽象類的init()方法,那麼程序員就必須手動的去維護ServletConfig對象了,還得調用super.init(servletConfig)方法去調用父類GenericServlet的初始化方法來保存ServletConfig對象,這樣會給程序員帶來很大的麻煩。GenericServlet提供的第二個不帶參數的init( )方法,就是爲了解決上述問題的。這個不帶參數的init()方法,是在ServletConfig對象被賦給ServletConfig引用後,由第一個帶參數的init(ServletConfig servletconfig)方法調用的,那麼這意味着,當程序員若是須要覆蓋這個GenericServlet的初始化方法,則只須要覆蓋那個不帶參數的init( )方法就行了,此時,servletConfig對象仍然有GenericServlet保存着。簡而言之一句話,就是覆蓋的時候能夠不用管servletConfig。
可是GenericServlet沒有處理Service(),  HttpServlet()纔是主角。

javax.servlet.http包內容

之因此所HttpServlet要比GenericServlet強大,其實也是有道理的。HttpServlet是由GenericServlet抽象類擴展而來的,HttpServlet抽象類的聲明以下所示:

1 public abstract class HttpServlet extends GenericServlet {}

HttpServlet之因此運用普遍的另外一個緣由是如今大部分的應用程序都要與HTTP結合起來使用。這意味着咱們能夠利用HTTP的特性完成更多更強大的任務。Javax。servlet.http包是Servlet API中的第二個包,其中包含了用於編寫Servlet應用程序的類和接口。Javax.servlet.http中的許多類型都覆蓋了Javax.servlet中的類型。

HttpServlet抽象類

HttpServlet抽象類是繼承於GenericServlet抽象類而來的。使用HttpServlet抽象類時,還須要藉助分別表明Servlet請求和Servlet響應的HttpServletRequest和HttpServletResponse對象。

1 public interface HttpServletRequest extends ServletRequest {}
2 public interface HttpServletResponse extends ServletResponse {}

先看一下GenericServlet中service定義:

1     @Override
2     public abstract void service(ServletRequest req, ServletResponse res)
3             throws ServletException, IOException;

再來看看HttpServlet()是怎麼來覆蓋的:

 1     @Override
 2     public void service(ServletRequest req, ServletResponse res)
 3         throws ServletException, IOException {
 4 
 5         HttpServletRequest  request;
 6         HttpServletResponse response;
 7 
 8         try {
 9             request = (HttpServletRequest) req;
10             response = (HttpServletResponse) res;
11         } catch (ClassCastException e) {
12             throw new ServletException("non-HTTP request or response");
13         }
14         service(request, response);
15     }
16 
17 protected void service(HttpServletRequest req, HttpServletResponse resp)
18         throws ServletException, IOException {
19 
20         String method = req.getMethod();
21 
22         if (method.equals(METHOD_GET)) {
23             long lastModified = getLastModified(req);
24             if (lastModified == -1) {
25                 // servlet doesn't support if-modified-since, no reason
26                 // to go through further expensive logic
27                 doGet(req, resp);
28             } else {
29                 long ifModifiedSince;
30                 try {
31                     ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
32                 } catch (IllegalArgumentException iae) {
33                     // Invalid date header - proceed as if none was set
34                     ifModifiedSince = -1;
35                 }
36                 if (ifModifiedSince < (lastModified / 1000 * 1000)) {
37                     // If the servlet mod time is later, call doGet()
38                     // Round down to the nearest second for a proper compare
39                     // A ifModifiedSince of -1 will always be less
40                     maybeSetLastModified(resp, lastModified);
41                     doGet(req, resp);
42                 } else {
43                     resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
44                 }
45             }
46 
47         } else if (method.equals(METHOD_HEAD)) {
48             long lastModified = getLastModified(req);
49             maybeSetLastModified(resp, lastModified);
50             doHead(req, resp);
51 
52         } else if (method.equals(METHOD_POST)) {
53             doPost(req, resp);
54 
55         } else if (method.equals(METHOD_PUT)) {
56             doPut(req, resp);
57 
58         } else if (method.equals(METHOD_DELETE)) {
59             doDelete(req, resp);
60 
61         } else if (method.equals(METHOD_OPTIONS)) {
62             doOptions(req,resp);
63 
64         } else if (method.equals(METHOD_TRACE)) {
65             doTrace(req,resp);
66 
67         } else {
68             //
69             // Note that this means NO servlet supports whatever
70             // method was requested, anywhere on this server.
71             //
72 
73             String errMsg = lStrings.getString("http.method_not_implemented");
74             Object[] errArgs = new Object[1];
75             errArgs[0] = method;
76             errMsg = MessageFormat.format(errMsg, errArgs);
77 
78             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
79         }
80     }

    咱們發現,HttpServlet中的service方法把接收到的ServletRequsest類型的對象轉換成了HttpServletRequest類型的對象,把ServletResponse類型的對象轉換成了HttpServletResponse類型的對象。之因此可以這樣強制的轉換,是由於在調用Servlet的Service方法時,Servlet容器總會傳入一個HttpServletRequest對象和HttpServletResponse對象,預備使用HTTP。所以,轉換類型固然不會出錯了。
轉換以後,service方法把兩個轉換後的對象傳入了本身定義的service方法進行運行。下面看怎麼運行的:

 咱們會發如今service方法中仍是沒有任何的服務邏輯,可是卻在解析HttpServletRequest中的方法參數,並調用如下方法之

 一:doGet,doPost,doHead,doPut,doTrace,doOptions和doDelete。這7種方法中,每一種方法都表示一個Http方法。doGet和doPost是最經常使用的。因此,若是咱們須要實現具體的服務邏輯,再也不須要覆蓋service方法了,只須要覆蓋doGet或者doPost就行了。

    總之,HttpServlet有兩個特性是GenericServlet所不具有的:

    1.不用覆蓋service方法,而是覆蓋doGet或者doPost方法。在少數狀況,還會覆蓋其餘的5個方法。

    2.使用的是HttpServletRequest和HttpServletResponse對象

下面講述如何解析HttpServletRequest以及如何組裝HttpServletResponse

 再次先省略這兩部份內容。

 

Servlet的工做流程

 

 

 Servlet線程安全

Servlet多線程體系結構是創建在Java多線程機制之上的,它的生命週期是由Web容器負責的。

 當客戶端第一次請求某個Servlet時,Servlet容器將會根據web.xml配置文件實例化這個Servlet類,此時它貯存於內存中。。當有新的客戶端請求該Servlet時,通常不會再實例化該Servlet類,也就是有多個線程在使用這個實例。 這樣,當兩個或多個線程同時訪問同一個Servlet時,可能會發生多個線程同時訪問同一資源的狀況,數據可能會變得不一致。因此在用Servlet構建的Web應用時要注意線程安全的問題。每個請求都是一個線程,而不是進程,所以,Servlet對請求的處理的性能很是高。

 對於Servlet,它被設計爲多線程的(若是它是單線程的,你就能夠想象,當1000我的同時請求一個網頁時,在第一我的得到請求結果以前,其它999我的都在鬱悶地等待),若是爲每一個用戶的每一次請求都建立 一個新的線程對象來運行的話,系統就會在建立線程和銷燬線程上耗費很大的開銷,大大下降系統的效率。

所以,Servlet多線程機制背後有一個線程池在支持,線程池在初始化初期就建立了必定數量的線程對象,經過提升對這些對象的利用率,避免高頻率地建立對象,從而達到提升程序的效率的目的。(由線程來執行Servlet的service方法,servlet在Tomcat中是以單例模式存在的, Servlet的線程安全問題只有在大量的併發訪問時纔會顯現出來,而且很難發現,所以在編寫Servlet程序時要特別注意。線程安全問題主要是由實例變量形成的,所以在Servlet中應避免使用實例變量。若是應用程設計沒法避免使用實例變量,那麼使用同步來保護要使用的實例變量,但爲保證系統的最佳性能,應該同步可用性最小的代碼路徑)

 

從創建工程,到配置web.xml文件。編寫Servlet,啓動Tomcat等基本都沒有問題。可是常常會出現http404錯誤。這個過程後面會在前面補例子。

 

Servlet的生命週期

Servlet的生命週期是由Tomcat容器管理。

a)客戶發出請求->web服務器轉發到web容器Tomcat;

b)Tomcat主線程對轉發來用戶的請求作出響應建立兩個對象:HttpServletRequset和HttpServletResponse;

c)從請求中的URL中找到正確的Servlet,Tomcat 爲其建立或者分配一個線程,同時把2建立的兩個對象傳遞給該線程。

d)Tomcat調用Servlet的Service()方法,根據請求參數的不一樣調用doGet()或者doPost()方法;

e)假設是HTTP GET請求,doGet()方法生成靜態頁面。並組合到響應對象裏;

Servlet線程結束後,Tomcat將響應對象轉換爲Http響應發回給客戶,同時刪去請求和響應對象。

從該過程當中,咱們能夠理解Servlet的生命週期:Servlet類加載(對應第3步);調用init方法(對應第3步);調用service方法(對應第四、5步);調用destory()方法(對應第6步)。

五:Servlet生命週期的各個階段

 

Servlet的生命週期包含了下面4個階段: 

(1)加載和實例化

Servlet容器負責加載和實例化Servlet。當Servlet容器啓動時,或者在容器檢測到須要這個Servlet來響應第一個請求時,建立Servlet實例。當Servlet容器啓動後,它必需要知道所需的Servlet類在什麼位置,Servlet容器能夠從本地文件系統、遠程文件系統或者其餘的網絡服務中經過類加載器加載Servlet類,成功加載後,容器建立Servlet的實例。由於容器是經過Java的反射API來建立Servlet實例,調用的是Servlet的默認構造方法(即不帶參數的構造方法),因此咱們在編寫Servlet類的時候,不該該提供帶參數的構造方法。

(2)初始化

在Servlet實例化以後,容器將調用Servlet的init()方法初始化這個對象。初始化的目的是爲了讓Servlet對象在處理客戶端請求前完成一些初始化的工做,如創建數據庫的鏈接,獲取配置信息等。對於每個Servlet實例,init()方法只被調用一次。在初始化期間,Servlet實例可使用容器爲它準備的ServletConfig對象從Web應用程序的配置信息(在web.xml中配置)中獲取初始化的參數信息。在初始化期間,若是發生錯誤,Servlet實例能夠拋出ServletException異常或者UnavailableException異常來通知容器。ServletException異經常使用於指明通常的初始化失敗,例如沒有找到初始化參數;而UnavailableException異經常使用於通知容器該Servlet實例不可用。例如,數據庫服務器沒有啓動,數據庫鏈接沒法創建,Servlet就能夠拋出UnavailableException異常向容器指出它暫時或永久不可用。

(3)請求處理

Servlet容器調用Servlet的service()方法對請求進行處理。要注意的是,在service()方法調用以前,init()方法必須成功執行。在service()方法中,Servlet實例經過ServletRequest對象獲得客戶端的相關信息和請求信息,在對請求進行處理後,調用ServletResponse對象的方法設置響應信息。在service()方法執行期間,若是發生錯誤,Servlet實例能夠拋出ServletException異常或者UnavailableException異常。若是UnavailableException異常指示了該實例永久不可用,Servlet容器將調用實例的destroy()方法,釋放該實例。此後對該實例的任何請求,都將收到容器發送的HTTP 404(請求的資源不可用)響應。若是UnavailableException異常指示了該實例暫時不可用,那麼在暫時不可用的時間段內,對該實例的任何請求,都將收到容器發送的HTTP 503(服務器暫時忙,不能處理請求)響應。

(4)服務終止

當容器檢測到一個Servlet實例應該從服務中被移除的時候,容器就會調用實例的destroy()方法,以便讓該實例能夠釋放它所使用的資源,保存數據到持久存儲設備中。當須要釋放內存或者容器關閉時,容器就會調用Servlet實例的destroy()方法。在destroy()方法調用以後,容器會釋放這個Servlet實例,該實例隨後會被Java的垃圾收集器所回收。若是再次須要這個Servlet處理請求,Servlet容器會建立一個新的Servlet實例。

在整個Servlet的生命週期過程當中,建立Servlet實例、調用實例的init()和destroy()方法都只進行一次,當初始化完成後,Servlet容器會將該實例保存在內存中,經過調用它的service()方法,爲接收到的請求服務。下面給出Servlet整個生命週期過程的UML序列圖

 

本節Servlet學習在這。看了好多博客,貼的有點亂,總體對serclet有了理解。後面再加一貼針對具體例子。

 

https://www.cnblogs.com/gaoxiangde/p/4339571.html-----後面部分主要參考

https://blog.csdn.net/qq_19782019/article/details/80292110------主要參考很是好,後面一部分ServletContext沒有理解。

https://www.cnblogs.com/whgk/p/6399262.html-----源碼解釋的很清楚

https://www.cnblogs.com/xdp-gacl/p/3729033.html  ------Java web開發入門

相關文章
相關標籤/搜索