Java servlets 是一項被普遍接受的技術,用於構建基於 web 應用程序的動態內容;Servlet 3.0 規範早期草案版本的發行讓該技術在特性和應用程序介面(Application Program Interface,API)方面得到了極大增強.Java Specification Request(JSR)已經以 JSR 315 的形式得到了批准,並計劃成為 Java Enterprise Edition 6(JSR 316)或更高版本的組成部分.與之前僅僅是維護髮行版(maintenance releases)的一些版本規範不同,Servlet 3.0 規範隨帶了許多 web 開發新時代所需的最令人興奮的特性.在本文中,我們將研究新版 Java servlets 中引入的主要特性.值得注意的是,本規範仍處於草案版本階段,因此在本文中所討論的技術細節可能會發生變化.
新規範主要交付了以下新特性:
開發的簡易性
可插拔性和可擴展性
非同步支持
安全性增強
其他雜項變化
很明顯,與其他技術相比,servlets 在 Java Enterprise Edition 家族中有著更廣泛的應用.Servlets 保留了其簡潔性和能夠處理 HTTP 請求並向 web 客戶機傳迴響應的優點.Servlets 可以用於實現簡單和小型應用程序的業務邏輯.在 web 框架中,servlets 作為所有傳入請求的入口點(即 controller servlet);因此,所有流行框架都是在原始的 servlets 上建立的.Servlet 3.0 中的新增特性旨簡化 servlet 應用程序的開發,並讓 servlet 開發人員和框架開發人員從中受益.在以下章節中,我們將詳細介紹每個新增特性,並討論如何使用它們來開發更優秀的應用程序.
開發的簡易性
開發的簡易性是任何技術成功的關鍵因素.Servlet 3.0 API 通過使用 JSR 175 註釋 集中解決開發簡易性問題,允許開發人員採用聲明式的編程方式.這意味著您可以通過使用像 @Servlet 或者 @ServletFilter 這樣的適當註釋對類進行註釋來快速開發一個 servlet 或者過濾器類.註釋不僅使 servlet、過濾器和偵聽器類的編碼更容易,而且,即使應用程序存檔可能有 servlet、過濾器或者上下文偵聽器類也可以選擇用於 web 應用程序的開發部署描述符.Web 容器負責處理各種註釋,其位置在 WEB-INF/classes 目錄下的各個類中、WEB-INF/lib 目錄下的 .jar 文件中、或者應用程序類路徑中任何可以找到的類中.
註釋與部署描述符
值得注意的是,部署描述符優先於註釋.換句話說,部署描述符覆蓋通過註釋機制所規定的配置信息.Web 部署描述符的 3.0 版本在 web-app 元素上包含一種名為 metadata-complete 的新屬性.該屬性定義了 web 描述符是否完整,或者 web 應用程序的類文件是否針對指定部署信息的註釋而進行檢查.如果該屬性被設置為 true,則部署工具必須忽略類文件中所存在的任何 servlet 註釋,並只使用描述符中所提及的配置細節.否則,如果沒有指定該值或者該值被設置為 false,容器必須針對註釋而掃描應用程序的所有類文件.這個屬性提供了在應用程序啟動階段啟用或者禁用註釋掃描以及對註釋的處理.
在 Servlet 3.0 中所引入的所有註釋都可以在 javax.servlet.http.annotation 和 javax.servlet.http.annotation.jaxrs 軟體包中找到.以下章節闡述 Servlet 3.0 中註釋的完整集合:
@Servlet:javax.servlet.http.annotation.Servlet 是一個類級別的註釋,確認經過註釋的類為一個 servlet 並保存關於所聲明的 servlet 的元數據.urlMappings 屬性是指定 URL 模式(調用該 servlet)的 @Servlet 的強制屬性.當接收到了一個請求時,容器將請求中的 URL 與 servlet 的 urlMappings 進行匹配,且如果 URL 模式匹配,則調用相應的 servlet 以響應該項請求.該註釋的所有其他屬性都是可選的,並帶有合理的默認值.Servlet 類中必須有一種使用像 GET、PUT、POST、HEAD 或者 DELETE 這樣的 HttpMethod 註釋進行註釋的方法.這些方法應將 HttpServletRequest 和 HttpServletResponse 作為方法參數.與以前的版本相反,servlets 3.0 的版本可以作為簡單傳統 Java 對象(Plain Old Java Objects,POJOs)而實現;也就是 servlets 不必再擴展像 HTTPServlet 或者 GenericServlet 這樣的基礎 servlet 實現類.
為了進行比較,在此給出了使用傳統 Servlet 2.5 API 編寫的 Java servlet 代碼片段,如下所示.在 Servlet 2.5 中,只要在部署描述符中配置了 servlet 的詳細信息,web 容器就將初始化 servlet.
public class MyServlet extends HttpServlet { public void doGet (HttpServletRequest req, HttpServletResponse res) { .... } } |
Deployment descriptor (web.xml)
<web-app> <servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>samples.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/MyApp</url-pattern> </servlet-mapping> ... </web-app> |
這裡給出的是使用 Servlet 3.0 API 編寫的較為簡化的版本.當 MyServlet 使用 @Servlet 註釋而被註釋為一個 servlet 時,則在 web 容器的啟動期間對其初始化.注意,在這種情況下部署描述符是可選的.
@Servlet(urlMappings={"/MyApp"}) public class MyServlet { @GET public void handleGet(HttpServletRequest req, HttpServletResponse res) { .... } } Deployment descriptor (web.xml) optional |
@ServletFilter 和 @FilterMapping:您可以使用 javax.servlet.http.annotation.ServletFilter 註釋來註釋過濾器類,從而輕鬆創建一個 servlet 過濾器.該註釋封裝正被聲明的過濾器的有關元數據.在過濾器類上具有 @FilterMapping 註釋也是強制性的.@FilterMapping 註釋定義用於過濾器的 URL 模式.@ServletFilter 的所有其他屬性都是可選的,並帶有合理的默認值.V3.0 過濾器類現在類似 POJO 類,並且沒有用於這些類所需的 Filter 介面或者非參數公用構造器.以下給出了使用 Servlet v2.5 API 的過濾器類的代碼片段:
public class MyFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { ...... } } Deployment descriptor (web.xml) <web-app> <filter> <filter-name>My Filter</filter-name> <filter-class>samples.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>My Filter</filter-name> <url-pattern>/foo</url-pattern> </filter-mapping> ... </web-app> |
使用 Servlet 3.0 編寫的一個示例過濾器類如下所示.該類使用 ServletFilter 註釋,容器將 MyFilter 標記為一個過濾器類.MyFilter 截取所有收到的請求,其中該請求的 URL 匹配模式 /foo.Servlet 3.0 為過濾器配置提供了可選的部署描述符.
@ServletFilter @FilterMapping("/foo") public class MyFilter { public void doFilter(HttpServletRequest req, HttpServletResponse res) { ..... } } Deployment descriptor (web.xml) optional |
@InitParam:該註釋可以用來定義必須傳遞給 servlet 或者過濾器類的任意初始化參數.它是 @Servlet 和 @ServletFilter 註釋的一個屬性.以下代碼示例解釋了如何將具有 english 值、稱作 lang 的初始化參數傳遞給一個 servlet 類.
@Servlet(urlMappings={"/MyApp"}, initParams ={@InitParam(name="lang", value="english")}) public class MyServlet { @GET public void handleGet(HttpServletRequest req, HttpServletResponse res) { .... } } |
@ServletContextListener:javax.servlet.http.annotation.ServletContextListener 註釋將該類聲明為一個 servlet 上下文偵聽器.當 web 容器創建或者銷毀 ServletContext 時,該上下文偵聽器接收註釋.上下文偵聽器是一個 POJO 類,且不必實現 ServletContextListener 介面.使用 Servlet 2.5 API 編寫的偵聽器類如下所示.當且僅當您在部署描述符中配置了該偵聽器類,容器才識別它.
public class MyListener implements ServletContextListener { public void contextInitialized(ServletContextEvent sce) { } ..... } Deployment Descriptor (web.xml) <web-app> <listener> <listener-class>samples.MyListener</listener-class> </listener> .... </web-app> |
使用 Servlet 3.0 API 編寫的一個得到極大簡化的偵聽器類,如下所示.
@ServletContextListener public class MyListener { public void contextInitialized (ServletContextEvent sce) { } ..... } Deployment Descriptor (web.xml) optional |
可插拔性和可擴展性
如今,像 Struts、JSF 和 Springweb 這樣的 web 框架已經被普遍接受,並已經確立成為構建 web 應用程序的技術.將這些框架集成為一個 web 應用程序並不是那麼容易,這一過程涉及將不同的片段同化在一起,之後編輯一個描述如何將所有這些片段配合在一起的單獨描述符文件.絕大多數框架強制您在應用程序的部署描述符文件中配置像 servlet 類(通常為 控制器 Servlet)、過濾器類或者偵聽器類這樣的框架細節.這種必備配置的主要原因是當今的 web 應用程序只支持一個單獨的整體部署描述符,其中定義了所有的部署信息.當應用程序的大小增加時,外部框架的依賴關係可能也增加,從而生成複雜的部署描述符文件.正如您可能知道的,複雜描述的維護始終存在爭議.
為了解決這些問題,Servlet 3.0 最為顯著的概念之一是 web 片段 或者 模塊 web.xml 的思想.Web 片段是將 web 應用程序邏輯分區為 servlet、servlet-mapping、servlet-filter、filter-mapping、servlet-listener 之類的元素及其子元素.框架開發人員可以利用該功能以定義存在於框架內自己的 web 片段,開發人員可以在不修改現有的部署描述符的情況下僅僅通過將庫文件包含到應用程序的類路徑中來插入越來越多的框架.簡言之,該功能旨在當用框架或者庫進行工作時進行零配置.
對該部署描述符已經進行了更改以包含保存 web 片段細節的
<web-fragment>
<servlet>
<servlet-name>myservlet</servlet-name>
<servlet-class>samples.MyServlet</servlet-class>
</servlet>
<listener>
<listener-class>samples.MyListener</listener-class>
</listener>
</web-fragment>
為了提供增強的可插拔性,Servlet 3.0 藉助添加到 ServletContext 的 API 為增加 servlets 和過濾器類的編程提供了非常需要的支持.這些新的 API 使您能通過編程來聲明 servlets、過濾器類及其 URL 映射.這些類在應用程序啟動或者運行期間進行初始化.最重要的是,您只能通過 ServletContext 的 contextInitialized 方調用這些 API.有關這些 API 的更多信息,請參閱 Servlet 3.0 API 文檔.通過編程添加 servlet 和過濾器類的代碼示例如下所示:
@ServletContextListener public class MyListener { public void contextInitialized (ServletContextEvent sce) { ServletContext sc = sce.getServletContext(); //Declare servlet and servlet mapping sc.addServlet("myServlet", "Sample servlet", "samples.MyServlet", null, -1); sc.addServletMapping("myServlet", new String[] {"/urlpattern/*"}); //Declare filter and filter mapping sc.addFilter("myFilter", "Sample Filter", " samples.MyFilter", null); sc.addFilterMapping("myFilter", new String[] {"/urlpattern/*"}, "myServlet", DispatcherType.REQUEST, false); } } |
非同步支持
大家應該都遇到過 servlets 運行緩慢的問題,特別是 servlets 必須等待來自 web 服務、JDBC 連接、JMS 消息等的響應.在當前情況下,servlet 在生成響應前需要等待進程完成,這會導致低效率阻塞操作消耗容器的線程或者其他有限的資源.在採用 JDBC 連接時的另一個不利影響是,資料庫可能存在許多等待訪問的阻塞線程.這種情況最終將導致整個 web 容器的線程不足和服務質量下降.
為了克服上述缺點,Servlet 3.0 添加了對掛起和恢復請求處理的支持,使 servlet 以非同步、非阻塞的方式響應請求(這就是編程的 Comet 樣式).當一項請求被掛起時,處理請求的線程將在不生成任何響應的情況下返回給容器並準備執行其他任務.處理請求的 resume 方法恢復請求處理.只要當所請求的資源可用時,處理事件的線程才恢復被掛起的請並進行處理以生成響應.以下列舉的是非同步 servlets 的一些功能:
即使數據到達緩慢(非阻塞輸入),也能在沒有阻塞事件的情況下從客戶機接收數據.
即使客戶機或者網路運行緩慢(非阻塞輸出),也能在不發生阻塞的情況下向客戶機發送數據.
能處理被延遲的請求.如果在響應請求前必須獲得遠程/緩慢的資源,或者如果需要抑制訪問特定的資源以防止過多的同步訪問,對被延遲事件的處理是非常有用的.
能處理被延遲的響應關閉;也就是響應將保持打開以允許在發生非同步事件時發送其他數據.
能通知阻塞或者非阻塞事件.
將新的 API 添加到 ServletRequest 和 ServletResponse,用於掛起、恢復和查詢請求的狀況、啟用禁用和查詢響應的狀況.開發人員可以分別通過 requestSuspended(), requestResumed() 和 requestCompleted() 方法使用請求的 resume、suspend 和 complete 方法通知事件.有關這些方法的詳細信息請參閱 Servlet 3.0 API.涉及使用非同步 servlets 從遠程 web 服務獲取數據的事件順序在圖 1 中進行了圖解說明.
圖 1. 使用非同步 servlet 處理請求
安全性
在 3.0 規範的早期草案中沒有考慮安全性問題,專家團仍未就此問題達成一致.不過,方案建議提供通過編程實現登錄和註銷功能.HTTPServletRequest 中添加的新 API 可以啟用這項功能.HTTPServletRequest 的 login 方法使應用程序或者框架強制進行以容器為中介的驗證.HTTPServletRequest 和 HTTPSession 的 logout 方法允許應用程序重置請求的驗證狀態.
其他雜項變化
下面列舉了一些為簡化數據檢索和簡化應用程序開發而進行的功能增強.
HttpOnly Cookie:Servlet 3.0 規範允許將 cookie 標記為 HttpOnly cookie.由於向客戶端腳本代碼公開了 HttpOnly cookie,因而防止了某些種類的跨站點腳本攻擊.大多數現代瀏覽器支持該項功能.以下列舉了添加到 Cookie 類中以支持 HTTPOnly cookie 的方法:
boolean isHttpOnly() |
API 的變化: 將以下新增方法添加到 ServletRequest 中,以便輕鬆獲得與請求對象相關的 ServletResponse 和 ServletContext 實例.
ServletContext getServletContext() |
路線圖
3.0 規範的早期草案於 2008 年 6 月完成了審查.由於 Servlet 3.0 的目標是用於 Java EE 6 平台或者更高版本,因此該規範推薦的最終發行版也與 Java EE 6 的發行版匹配.但是,規範的早期測試版本將通過 GlassFish 來提供,後者是 Java EE 的參考實現.預計 Servlet 3.0 的參考實現將被整合到 GlassFish 的版本 3 中.
JSR 草案在審查過程中確定了一些變化,正在編製的新方案將處理來自社區的建議和反饋意見.新方案中的主要變化如下:
@Servlet 被重新命名為 @WebServlet,而 @ServletContextListener 被重新命名為 @WebServletContextListener.
新方案建議 @WebServlet 必須擴展 HTTPServlet 類.因此,如前所述,servlet 類可以不是 POJO 類.
類似地,@ServletFilter 類和 @WebServletContextListener 類需要分別實現 Filter 和 ServletContextListener 介面.
為啟用和禁用 Servlet 提供選項.
要了解更多詳細信息,請參見在本文 參考資料 章節中提供的 更新方案.還需注意,這些新增特性會因本方案的最終批准而發生變更.
結束語
在本文中,我們討論了在 Servlet 3.0 的早期草案規範中提出的激動人心的特性.該規範已經開始在社區引起強烈反響.通過註釋機制而實現的開發簡易性、零配置使用框架和庫、非同步 servlets、可插拔性、安全性以及更多新增功能都是 Java Servlets 期待已久的特性.這說明,一些已經閱讀了該規範的人表達了對註釋過度使用、在掃描 web 片段時的安全威脅等問題的關注.但是,毫無疑問,Java Servlet 3.0 將使 servlet 開發人員的工作更輕鬆,並為開發下一代 web 應用程序提供了更多的支持.
[火星人 ] Servlet 3.0 介紹已經有707次圍觀