05-Servlet物件的生命週期
建立一個名為idea-servletweb-03的空Java工程,然後新增模組Module,命名為servlet生命週期-1,將該模組新增框架支援Add Framework Support,選擇為web應用。匯入servlet-api.jar規範包,配置Tomcat伺服器,Application context命名為/servlet生命週期。
首先,我們在無引數構造方法、init、service和destroy方法中的輸出語句前加上斷點
然後Debug Tomcat伺服器,進入如下html索引頁面,該頁面中有個超連結,轉到Servlet實現類ServletLifeCycle,
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index page</title> </head> <body> <a href="/servlet生命週期/lifecycle">測試servlet物件的生命週期</a> </body> </html>
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>servletLifeCycle</servlet-name> <servlet-class>com.servlet.ServletLifeCycle</servlet-class> </servlet> <servlet-mapping> <servlet-name>servletLifeCycle</servlet-name> <url-pattern>/lifecycle</url-pattern> </servlet-mapping> </web-app>
點選頁面中的超連結,返回idea,就會出現Debug頁面,將Tomcat伺服器輸出臺上的東西清空
從上圖可以看到加了斷點,我們可以知道在Tomcat啟動後我們點選了測試Servlet物件生命週期,才出現Debug頁面,然後執行到ServletLifeCycle類中的無參構造方法停止,說明伺服器啟動階段並沒有例項化Servlet物件。當我們傳送符合web.xml中繫結資源的路徑後Tomcat伺服器才根據web.xml檔案中相對映的路徑與資源,獲取該Servlet實現類的完整類名,底層通過反射機制建立物件。
我們不要點選Step Over,一步一步走,因為要點很多次,然後才進入下一個斷點,點選Resume Program
\> Show Execution Point (Alt + F10):如果你的游標在其它行或其它頁面,點選這個按鈕可跳轉到當前程式碼執行的行。
> Step Over (F8):步過,一行一行地往下走,如果這一行上有方法不會進入方法。
> Step Into (F7):步入,如果當前行有方法,可以進入方法內部,一般用於進入自定義方法內,不會進入官方類庫的方法,如第25行的put方法。
> Force Step Into (Alt + Shift + F7):強制步入,能進入任何方法,檢視底層原始碼的時候可以用這個進入官方類庫的方法。
> Step Out (Shift + F8):步出,從步入的方法內退出到方法呼叫處,此時方法已執行完畢,只是還沒有完成賦值。
> Drop Frame (預設無):回退斷點,後面章節詳細說明。
我們可以看到,第二個斷點是在init方法中,就是說伺服器建立完物件後執行的第一個方法是init方法,再次點選,可以看到下一個執行的是service方法
再點選,時並沒有執行到destroy方法中的斷點處,說明該物件還會存在很長一段時間,不會被銷燬。伺服器還在執行
當我們在瀏覽器點選返回到index.html頁面時,再次點選測試Servlet生命週期,會發現idea中再次進入Debug頁面,點選執行發現直接到service方法中,
然後我們在瀏覽器中無論是重新整理點連結後的頁面還是後退之後再點選連線發現只執行service方法。說明servlet物件只會建立一個,當其他使用者和我一同傳送同一個請求時是共用一個物件的,在伺服器中只有一個,但支援多執行緒。感覺是單例模式,但是Servlet實現類中的構造方法卻不是私有的,只能叫偽單例,也叫單例項物件。當我們關閉伺服器時發現執行了destroy方法
destroy方法執行,就說明了這個Servlet物件即將被銷燬,說明Tomcat伺服器開始做銷燬該物件的一些準備了。
--關於Servlet物件的生命週期
1、什麼是生命週期
生命週期表示一個Java物件從最初建立到銷燬的全過程
2、Servlet物件的生命週期是誰來管理的?
Servlet物件的生命週期web程式設計師無權干涉,包括該物件的相關方法呼叫
Servlet物件從建立,方法的呼叫以及銷燬全程由web容器管理
Web Container管理Servlet物件的生命週期
3、"預設情況下",Servlet物件再伺服器啟動階段不被建立(例項化),若想在該階段例項化需要特殊設定
4、描述Servlet物件的生命週期
1)使用者再瀏覽器上輸入URL: http://localhost:8080/servlet生命週期/lifecycle
2)web容器擷取請求路徑:/servlet生命週期/lifecycle
3)web容器從上下文中找該請求路徑對應的Servlet物件
4)若找到了對應的Servlet物件
4.1) web容器直接呼叫該Servlet物件的service方法提供服務
5)若沒有找到對應的Servlet物件
5.1) 通過web.xml檔案中的相關配置資訊,得到擷取的請求路徑中/lifecycle對應的完整類名
5.2) web容器通過反射機制,呼叫Servlet類中的無參構造方法建立Servlet物件
5.3) 然後web容器呼叫Servlet物件中的init方法完成初始化操作
5.4) 再然後web容器呼叫Servlet物件的service方法提供服務
6)當web容器關閉或者是webapp重新部署又或者該Servlet物件長時間沒有使用者再次訪問時,web容器會將該Servlet物件銷燬,銷燬之前web容器會呼叫Servlet物件的destroy方法,完成銷燬之前的準備。
5、總結
5.1 Servlet類的構造方法只執行一次
5.2 Servlet物件的init方法只執行一次
5.3 Servlet物件的service方法,使用者請求一次,則執行一次
5.4 Servlet物件的destroy方法只執行一次
6、Servlet物件是單例,但不符合單例模式,只能稱為偽單例。真正的單例模式的物件的構造方法是私有的
Tomcat伺服器支援多執行緒,所以Servlet物件在單例項多執行緒的環境下執行。所以不建議在Servlet物件中使用例項變數,儘量使用區域性變數。
7、若希望在伺服器啟動階段就建立Servlet物件,可以在web.xml檔案中的<servlet>標籤下面新增子標籤<load-on-startup>標籤,如果專案中有多個類實現了Servlet介面,該標籤中的數字為0所對應的Servlet類就比數字為1的Servlet類先例項化和執行init方法,自然數越小優先順序越高,如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>servletLifeCycle</servlet-name>
<servlet-class>com.servlet.ServletLifeCycle</servlet-class>
<!--其中的數字1表示Servlet物件被建立的優先順序-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>servletLifeCycle</servlet-name>
<url-pattern>/lifecycle</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>WelcomeServlet</servlet-name>
<servlet-class>com.servlet.WelcomeServlet</servlet-class>
<!--其中的數字0表示Servlet物件被建立的優先順序-->
<!--表示在伺服器啟動階段WelcomeServlet例項化,且比ServletLifeCycle先例項化和執行init方法-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>WelcomeServlet</servlet-name>
<url-pattern>/welcome</url-pattern>
</servlet-mapping>
</web-app>
8、在伺服器啟動階段就會解析各個webapp的web.xml檔案,此時Web容器建立一個Map集合,將web.xml檔案中的url-pattern和對應的Servlet實現類的完整包名放進去:
Map集合:
Map<String,String> 集合
key value
------------------------------------------------
/lifecycle com.servlet.ServletLifeCycle
/welcome com.servlet.WelcomeServlet
...
9、例項化的Servlet物件都被儲存到哪裡了?
大多數的Web容器將Servlet物件和與其對應的url-pattern儲存到一個Map集合中了:
Web容器有這樣一個Map集合:
Map<String,Servlet> 集合
key value
---------------------------------
/lifecycle ServletLifeCycle物件引用
/welcome WelcomeServlet物件引用
...
就是說,我們在理解Servlet物件生命週期時,並不是在使用者在瀏覽器傳送請求後伺服器才解析web.xml檔案然後尋找其中對應的Servlet物件。而是Web容器啟動時就解析了web.xml檔案,假設啟動時不例項化Servlet物件,當我們是第一次傳送請求某個Servlet物件的資源時,Web容器根據請求路徑key定位儲存Servlet物件的Map1中的value,找不到,然後去儲存Servlet實現類完整類名的Map2集合中尋找,此時value是完整類名,然後建立物件,用該Servlet物件提供服務的同時將其與其對應的url-pattern存到另一個Map集合中,下一次是同一個請求時就直接從Map2中定位到了。
10、Servlet介面中的這些方法應該編寫什麼內容?
1)--無引數的構造方法【儘量不要在構造方法中編寫其他程式碼】
2)--init方法
以上兩個方法執行時間幾乎是相同的,執行次數都是一次,構造方法執行物件正在建立,init方法執行說明物件已被建立:
如果這個系統要求在物件建立時刻要執行一段特殊的程式,這段程式儘量寫到init方法中。
原因是寫到無參構造方法中是有風險的,程式設計師編寫無參構造方法可能疏忽把這個方法編成有引數的構造方法,使該Servlet類沒有無參構造方法,Web就會無法例項化該Servlet實現類。
--Servlet中的init方法是SUN公司為Java程式設計師提供,以防如果該專案設計在初始化時刻執行一段特殊的程式,就將這段程式寫入到init方法中,自動被呼叫。
3)--service方法
該方法是最重要的,該方法中編寫的程式碼需要完成本專案中的業務邏輯處理,請求處理以及響應等。
4)--destroy方法
這個方法也是SUN公司為程式設計師提供的在一個特殊時刻執行特定需求程式碼的方法,該特殊時刻稱為物件銷燬時刻,如果希望在銷燬時刻執行一段特殊程式碼,就寫在該方法中,然後自動被容器呼叫。
--在類載入階段想要執行一段特殊的程式碼,SUN公司也提供了Java的靜態語句塊。