Servlet的概念
什么是Servlet呢?
Java中有一個叫Servlet的接口,如果一個普通的類實現了這個接口,這個類就是一個Servlet。Servlet下有一個實現類叫HttpServlet,一個普通的java如果繼承了HttpServlet類,覆蓋了它的doGet和doPost方法,那么這個普通類也可以叫做Servlet。最后,servlet程序交給服務器運行!
那么,當我們寫好了一個Servlet,交給了服務器,它是如何執行的呢!?
Servlet的執行過程
我們寫了一個Servlet名叫hello。那么瀏覽器是如何訪問到這個資源呢?
要說這個,我們先來學習一下瀏覽器的地址輸入。
有這樣一個輸入: http://localhost:8080/day10/hello
http: http協議
localhost: 域名,到本地C盤下的hosts文件查找是否存在域名對應的ip映射記錄地址。有的話就直接訪問該IP,沒有的話就到DNS上去找。
8080 端口號,這里指tomcat服務器。localhost匹配到tomcat的默認站點,到webapps目錄下找web應用。
/day10 web應用的名稱。在webapps下找是否存在day10的目錄。
/hello web資源。在day10web應用下查找是否有這個資源。(如果看不懂,最好先去了解一下tomcat里的文件結構。)
這里/hello 資源就是我們寫的一個Servlet,服務器得到這個字符串后就是經過以下過程來找到servlet的!
-> 得到/hello字符串
-> 使用/hello到web.xml文件中查找每一個<servlet-mapping>下的<url-pattern>標簽里的內容,然后得到sevlet-name
-> 使用sevlet-name去servlet標簽中找到對應的相同名稱的servlet配置。
-> 得到servlet配置中的servlet-class內容。字符串:gz.itcast.a_servlet.HelloServlet
<servlet> <servlet-name>HelloServlet</servlet-name>
<servlet-class>com.vmaxtam.numzero.addServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
最后通過反射實例化HelloServlet對象,然后調用HelloServlet中的方法。
Servlet的映射路徑
<url-pattern>/hello</url-pattern> 這里的映射路徑還可以用多種方式來填寫!
url-pattern 瀏覽器輸入URL 精確匹配 /demo1 http://localhost:8080/day10/demo1 /itcast/demo1 http://localhost:8080/day10/itcast/demo1
模糊匹配 /* http://localhost:8080/day10/任意路徑 /itcast/* http://localhost:8080/day10/itcast/任意路徑 *.后綴名 http://localhost:8080/day10/任意路徑.后綴。例如 *.do,*.action, *.html
注意: 1)url-pattern要以/開頭,要么以*開頭。demo1這種寫法是錯誤的!! 2) /itcast/*.do 這種寫法不合法。不能同時使用兩種模糊匹配 3)當一個請求有多個servlet被匹配的情況下: a)長的最像的url-pattern會優先被匹配 b)以后綴名結尾的url-pattern優先級最低!!! |
說到這里,就有人問:這個只是在找Servlet文件,那么如果要找服務器的靜態文件(html,xml等),服務器的執行過程如何??如果要找的文件不存在又會怎么樣?
下面為大家講解:
缺省路徑
在tomcat服務器中有一個默認的Servlet,叫DefaultServlet,DefaultServlet的url-pattern為 / 。這個DefaultServlet的作用主要用於處理靜態資源的請求。
輸入: http://localhost:8080/day10/test.html 如何找到資源呢?
1)在day10的web應用下查找web.xml文件,用/test.html,存在匹配是否存在符合規則url-pattern,找到就會執行對應的動態資源(servlet)。
2)如果找不到對應的url-pattern,則到day10當前web應用的根目錄下查找一個test.html名稱的靜態資源文件。如果找到這個文件,DefaultServlet讀取該靜態文件內容輸出到瀏覽器客戶端。
3)如果在day10下找不到test.html的靜態資源文件,那么返回404的頁面。
前面只說到服務器是如何找到Servlet這個文件,那么Servlet是如何執行的呢!!!這就是重點,Servlet的生命周期
Servlet的生命周期
tomcat服務器什么時候創建servlet對象?什么時候銷毀對象?什么時候調用了什么方法?!
其實也就是這樣的一個過程 :
1.Servlet對象的創建。
2.Servlet對象執行某些方法來給我們服務。
3.Servlet對象的銷毀。
而這個過程有4個很核心的方法需要執行:
構造方法: servlet對象創建時調用。默認情況下,第一次訪問servlet時,servlet對象創建。只被調 用1次。servlet在tomcat服務器中是單實例的。
init方法: 在創建完servlet對象之后被調用。用於對servlet對象進行初始化。只調用1次。
service方法:每次發出請求時被調用。調用n次。
destroy方法: 在tomcat服務器停止或者web應用重新加載時調用。只調用1次。
下面我們用偽代碼來演示一下 瀏覽器發出請求 到服務器作出相應 整個過程中Servlet的執行過程!
瀏覽器輸入: http://localhost:8080/day10/hello
進入web.html查詢資源。
得到字符串 gz.itcast.a_servlet.HelloServlet
模擬tomcat服務器內部的運行代碼:
1)用反射構造HelloServlet對象
1.1 得到字節碼對象
Class clazz = Class.forName("gz.itcast.a_servlet.HelloServlet ")
1.2 調用無參的構造方法構造對象
Object obj = clazz.newInstance(); ----1.Servlet的構造方法被執行
2)創建ServletConfig對象,調用init方法
2.1 得到init方法對象
Method method = clazz.getDeclareMethod("init",ServletConfig.class);
2.2 執行init方法
method.invoke(obj, config); --2. Servlet的init方法被執行
3)創建request和response對象,調用service方法
3.1 得到service方法對象
Method m = clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletResponse.class);
3.2 執行方法
m.invoke(obj,request,response); --3.Servlet的service方法被執行
4)tomcat服務器停止或web應用重新加載時,調用destroy方法
4.1 得到destroy方法對象
Method method = clazz.getDeclareMethod("destroy",null);
4.2 執行方法
method.invoke(obj,null); --4.Servlet的destroy方法被執行
如果文字不夠直觀,那么我看可以看一下流程圖
Servlet對象自動加載
引入
默認情況下,servlet對象會在第一次請求servlet時被創建。創建完之后調用init方法。假如構造方法或者init方法執行的業務邏輯比較多,那么用戶在第一次訪問servlet時的等待會變長,影響用戶的體驗。
解決辦法
改變servlet的創建時機:讓servlet對象在tomcat服務器啟動web應用加載時創建。
在servlet的配置中,加入一個配置:
<load-on-startup>正整數</load-on-startup>
<servlet> <servlet-name>LifeDemo1</servlet-name> <servlet-class>gz.itcast.c_life.LifeDemo1</servlet-class> <load-on-startup>1</load-on-startup> </servlet> |
注意: 正整數的數值越大,創建的優先級就越低!!
Servlet的多線程安全問題
結論: servlet在tomcat服務器中是單實例多線程的.
多線程並發問題
多個線程之間同時操作了共享數據!!!
解決多線程並發問題
給需要同步的代碼塊加上唯一的對象鎖。
如何編寫線程安全的servlet類:
1)盡量不要使用成員變量。
2)如果使用成員變量,要給用到了成員變量的代碼塊加上鎖。盡量縮小同步代碼塊的范圍,以避免因為同步導致執行效率問題。
Init方法
在GenericServlet中提供了一個無參數的init方法!!!
有參數的init:該方法是Servlet的四個生命周期方法中的一個,該方法一定會被服務器調用。在該方法中會調用無參數的init方法。
無參數的init:該方法是Sun公司設計出來方便開發者進行重寫,在該方法中實現對servlet對象的初始化工作,這個方法會被有參的init方法調用!