初學JavaWeb開發,請遠離各種框架,從Servlet開始。
Web框架是開發者在使用某種語言編寫Web應用服務端是關於架構的最佳實踐。很多Web框架是從實際的Web項目抽取出來的,僅和Web的請求和響應處理有關,形成一個基礎,在開發別的應用項目的時候則可以從這個剝離出來的基礎做起,讓開發者更關注具體的業務問題,而不是Web的請求和響應的控制。
框架很多,但套路基本類似,幫你隱藏很多關於HTTP協議細節內容,專注功能開發。
但對於一個初學者來說,過早的接觸框架往往事倍功半!同樣一個問題,換一種框架你可能需要從頭開始研究。
所以,作為Java Web技術的核心基礎,Servlet的工作原理是必須掌握的,也是成為一名合格的Java Web技術開發人員的基本要求。
一、Servlet簡介
Servlet是Server與Applet的縮寫,是服務端小程序的意思。是SUN公司提供的一門用於開發動態Web資源的技術。目前最新版本為3.1。
Servlet本質上也是Java類,但要遵循Servlet規范進行編寫,沒有main()方法,它的創建、使用、銷毀都由Servlet容器進行管理(如Tomcat)。
Servlet是和HTTP協議是緊密聯系的,其可以處理HTTP協議相關的所有內容。這也是Servlet應用廣泛的原因之一。
提供了Servlet功能的服務器,叫做Servlet容器,其常見容器有很多,如Tomcat, Jetty, resin, Oracle Application server, WebLogic Server, Glassfish, Websphere, JBoss等。
二、Servlet工作原理解析
1、一個HTTP請求的執行過程:
客戶端發出請求http://localhost:8080/xxx
根據Web.xml文件的配置,找到<url-pattern>對應的<servlet-mapping>
讀取<servlet-mapping>中<servlet-name>的值
找到<servlet-name>對應的<servlet-class>
找到該class並加載執行該class
2、Servlet的執行過程
Servlet程序有Web服務器調用,當收到請求后,
檢查是否已裝載並創建了該Servlet對象,如果沒有則加載創建
調用Servlet的init()方法初始化實例
調用service()方法,處理請求並返回響應結果
在服務器被停止或重啟之前,調用destroy()方法釋放資源
3、Servlet接口實現類
SUN公司定義了兩個實現類,GenerricServlet和HttpServlet,其中后者是前者的子類,它在原有基礎上添加了一些HTTP協議處理方法,它比GenerricServlet功能更強大,所以我們一般將自己的類繼承自HttpServlet,並重寫doGet方法和doPost方法,不需要重寫Service方法。
4、Servlet的一些細節
4.1、由於客戶端是通過URL地址訪問web服務器中的資源,所以Servlet程序弱項被外界訪問,必須把Servlet程序映射到一個URL地址上,這個工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成(在Servlet3.0規范的新特性中,該功能可以使用注解完成,不要求必須使用web.xml,只用注解更簡單,該特性會在后文進行詳細講解)。
<servlet>元素用於注冊Servlet,它包含兩個主要的子元素:<servlet-name>和<servlet-class>,分別用於設置Servlet的注冊名稱和Servlet的完整類名。
<servlet-mapping>元素用於映射一個已注冊的Servlet的一個對外訪問路徑,它包含兩個子元素:<servlet-name>和<url-pattern>,分別用於指定Servlet的注冊名稱和Servlet的對外訪問路徑。
-
<web-app> <servlet> <servlet-name>AnyName</servlet-name><!--自定義的邏輯名--> <servlet-class>HelloServlet</servlet-class><!--Servlet對應類的全類名--> </servlet> <servlet-mapping> <servlet-name>AnyName</servlet-name><!--上面定義的邏輯名--> <url-pattern>/demo/hello.html</url-pattern><!--匹配的URL--> </servlet-mapping> </web-app>
4.2、同一個Servlet可以被映射到多個URL上,即多個<servlet-mapping>元素的<servlet-name>子元素的設置值可以是同一個Servlet的注冊名。
在Servlet映射到的URL中也可以使用*通配符,但是只能有兩種固定的格式:
一種是“*.擴展名”。
另一種是以(/)開頭並以(/*)結尾。
-
<!--方式一用於匹配所有某一擴展名的文件--> <servlet-mapping> <servlet-name>AnyName</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!--方式二用於匹配某一文件夾下的所有文件--> <servlet-mapping> <servlet-name>AnyName</servlet-name> <url-pattern>/action/*</url-pattern> </servlet-mapping>
兩個特點:可以精確匹配,就用精確匹配,最后使用范圍最寬泛的匹配
/* 的優先級高於 *.擴展名 會先匹配
4.3、如果某個Servlet的映射路徑只有一個(/),那么這個Servlet就成為當前Web應用程序的缺省Servlet。
凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它們的請求訪問都將交給缺省Servlet處理,也就是說,缺省Servlet用於處理所有其他Servlet都不處理的訪問請求。
在tomcat的安裝目錄/conf/web.xml中,注冊了一個名稱org.apache.catalina.servlets.DefaultServlet的Servlet,並將這個Servlet設置為缺省Servlet。
當訪問Tomcat服務器中的某個靜態HTML文件或圖片等資源時,實際上是在訪問這個缺省Servlet。
5、Servlet的生命周期
Servlet沒有main()方法,不能獨立運行,它的運行完全由Servlet引擎來控制和調度。
針對客戶端的多次Servlet請求,通常情況下,服務器只會在第一次請求的時候創建一個Servlet實例對象,並駐留在內存中,為后續的其他請求服務,直至web容器退出,Servlet實例對象才會銷毀。
在Servlet的整個生命周期內,Servlet的init()方法只被調用一次,而對於每一個請求都會調用一次Servlet的service()方法service根據請求方式調用doGet或doPost方法
如果在<servlet>元素中配置了一個<load-on-startup>元素,那么WEB應用程序在啟動時,就會裝載並創建實例對象、以及調用Servlet實例對象的init()方法。可以在啟動的時候為整個WEB應用創建一些必要的的資源或連接。但如果所有的Servlet都啟動加載,則會大大增加服務器負擔,而且有些Servlet永遠也不會被客戶訪問到,白白浪費資源,所以從性能角度,應合理利用該特性。
<servlet> <servlet-name>invoker</servlet-name> <servlet-class> org.apache.catalina.servlets.InvokerServlet </servlet-class> <load-on-startup>2</load-on-startup><!--數字表示啟動加載的優先級,大於0--> </servlet>
6、ServletConfig對象
在Servlet的配置文件中,可以使用一個或對個<init-param>標簽為Servlet配置一些初始化參數。
當Servlet配置了初始化參數后,web容器在創建Servlet實例對象時,會自動將這些初始化參數封裝到ServletConfig對象中,並在調用Servlet的init方法時,將ServletConfig對象傳似給Servlet,開發者可以通過ServletConfig對象得到初始化參數信息。
7、ServletContext對象
WEB容器在啟動時,它會為每個WEB應用程序都創建一個對應的Servlet對象,它代表當前web應用。
ServletConfig對象中維護了ServletContext對象的引用,開發者在編寫Servlet是,可以通過ServletConfig.getServletContext方法獲得該對象。
由於一個WEB應用中的所有Servlet共享同一個ServletContext對象,因此Servlet對象之間可以通過ServletContext對象來實現通訊。ServletContext對象通常也被稱之為Context域對象。
多個Servlet通過ServletContext對象實現數據共享。要確保注意線程安全。
可以通過ServletContext對象獲取WEB應用的初始化參數。
可以實現Servlet的轉發。getRequestDispatcher()方法
8、Servlet常用容器Tomcat(左圖:Tomcat容器;右圖:Servlet的執行流程)


9、Servlet體系結構

從上圖可以看出Servlet規范是基於這幾個類運轉的,與Servlet主動關聯的是三個類:ServletConfig、ServletRequest、ServletResponse。這三個類都是通過容器傳遞給Servlet的。
ServletConfig接口中的方法都是為了獲取這個Servlet的一些配置屬性,而這些配置屬性可能在Servlet運行時被用到。
ServletContext就是這些配置屬性的上下文環境。
三、Servlet中的Session與Cookie
Servlet能夠給我們提供兩部分數據,一個是在Servlet初始化時調用init方法設置的ServletConfig,它基本包含了Servlet本身和Servlet所運行的Servlet容器中的基本信息。另一個是ServletRequest提供的這次請求的HTTP協議信息,這部分需要很清楚HTTP協議。
Session與Cookie的作用都是為了保持訪問用戶與后端服務器的交互狀態,各有優缺點。然而具有諷刺意味的是它們優點和它們的使用場景又是矛盾的,例如使用 Cookie 來傳遞信息時,隨着 Cookie 個數的增多和訪問量的增加,它占用的網絡帶寬也很大,試想假如 Cookie 占用 200 個字節,如果一天的 PV 有幾億的時候,它要占用多少帶寬。所以大訪問量的時候希望用 Session,但是 Session 的致命弱點是不容易在多台服務器之間共享,所以這也限制了 Session 的使用。
Session正常工作的實現方式:
基於URL Path Parameter,默認支持
基於Cookie,若不修改Context中的cookie標識,默認支持
基於SSL,默認不支持,只有connector.getAttribute("SSLEnabled")為true時才支持。
四、Servlet中的Listener
整個Tomcat服務器中Listener使用的非常廣泛,它是基於觀察者模式的,Listener的設計對開發Servlet應用程序提供了一種快捷的手段,能夠方便的從另一個縱向唯獨控制程序和數據,目前Servlet中提供了5中兩類事件的觀察者接口,他們分別是:4個EventListener類型的,ServletContextAttributeListener、ServletRequestAttributeListener、ServletRequestListener、HttpSessionAttributeListener 和 2 個 LifecycleListeners 類型的,ServletContextListener、HttpSessionListener。
它們基本上涵蓋了整個Servlet生命周期中,你感興趣的每種事件,這些Listener的實現類可以配置在web.xml中的<listener>標簽中,當然也可以在應用程序中動態添加Listener,但是ServletContextListener在容器啟動之后就不能再添加新的,因為它監聽的事件已經不會再出現。掌握這些Listener的使用,能夠讓我們的程序設計的更加靈活。
五、Servlet3.0新特性注解
前面介紹過Servlet3.0提供了注解(annotation),使得不再需要在web.xml文件中進行Servlet的部署描述。
開發者可以使用@WebServlet注解將一個繼承於javax.servlet.http.HttpServlet的類標注為可以處理用戶請求的Servlet。
@WebServlet注解的相關屬性 NO. 屬性名 描述 1 asyncSupported 聲明Servlet是否支持異步操作模式 2 description Servlet的描述信息 3 displayName Servlet的顯示名稱 3 initParams Servlet的初始化參數 5 name Servlet的名稱 6 urlPatterns Servlet的訪問URL 7 value Servlet的訪問URL
Servlet的訪問URL是Servlet的必選屬性,可以選擇使用urlPatterns或者value定義。
更多關於Servlet3.0的注解使用知識請參考博客: