Servlet3.0規範的新特性主要是爲了3個目的: 1.簡化開發 2.便於佈署 3.支持Web2.0原則 爲了簡化開發流程,Servlet3.0引入了註解(annotation),這使得web佈署描述符web.xml不在是必須的選擇。 Pluggability可插入性 當使用任何第三方的框架,如Struts,JSF或Spring,咱們都須要在web.xml中添加對應的Servlet的入口。這使得web描述符笨重而難以維護。Servlet3.0的新的可插入特性使得web應用程序模塊化而易於維護。經過web fragment實現的可插入性減輕了開發人員的負擔,不須要再在web.xml中配置不少的Servlet入口。 Asynchronous Processing 異步處理 另一個顯著的改變就是Servlet3.0支持異步處理,這對AJAX應用程序很是有用。當一個Servlet建立一個線程來建立某些請求的時候,如查詢數據庫或消息鏈接,這個線程要等待直到得到所須要的資源纔可以執行其餘的操做。異步處理經過運行線程執行其餘的操做來避免了這種阻塞。 Apart from the features mentioned here, several other enhancements have been made to the existing API. The sections towards the end of the article will explore these features one by one in detail. 除了這些新特性以外, Servlet3.0對已有的API也作了一些改進,在本文的最後咱們會作介紹。 Annotations in Servlet Servlet中使用註解 Servlet3.0的一個主要的改變就是支持註解。使用註解來定義Servlet和filter使得咱們不用在web.xml中定義相應的入口。 @WebServlet @WebServlet用來定義web應用程序中的一個Servlet。這個註解能夠應用於繼承了HttpServlet。這個註解有多個屬性,例如name,urlPattern, initParams,咱們可使用者的屬性來定義Servlet的行爲。urlPattern屬性是必須指定的。 例如咱們能夠象下面的例子這樣定義: 1. @WebServlet(name = "GetQuoteServlet", urlPatterns = {"/getquote"} ) 2. public class GetQuoteServlet extends HttpServlet { 3. @Override 4. protected void doGet(HttpServletRequest request, HttpServletResponse response) 5. throws ServletException, IOException { 6. PrintWriter out = response.getWriter(); 7. try { 8. String symbol = request.getParameter("symbol"); 9. out.println("<h1>Stock Price is</h1>" + StockQuoteBean.getPrice(symbol); 10. } finally { 11. out.close(); 12. } 13. } 14. } 15. 16. public class StockQuoteBean { 17. private StockQuoteServiceEntity serviceEntity = new StockQuoteServiceEntity(); 18. public double getPrice(String symbol) { 19. if(symbol !=null ) { 20. return serviceEntity.getPrice(symbol); 21. } else { 22. return 0.0; 23. } 24. } 25. } 複製代碼 在上面的例子中,一個Servlet只對應了一個urlPattern。實際上一個Servlet能夠對應多個urlPattern,咱們能夠這樣定義: 1. @WebServlet(name = "GetQuoteServlet", urlPatterns = {"/getquote", "/stockquote"} ) 2. public class GetQuoteServlet extends HttpServlet { 3. @Override 4. protected void doGet(HttpServletRequest request, HttpServletResponse response) 5. throws ServletException, IOException { 6. PrintWriter out = response.getWriter(); 7. try { 8. String symbol = request.getParameter("symbol"); 9. out.println("<h1>Stock Price is</h1>" + StockQuoteBean.getPrice(symbol); 10. } finally { 11. out.close(); 12. } 13. } 14. } 複製代碼 @WebFilter 咱們可使用@WebFilter註解來定義filter。這個註解能夠被應用在實現了javax.servlet.Filter接口的類上。一樣的,urlPattern屬性是必須指定的。下面就是一個例子。 1. @WebFilter(filterName = "AuthenticateFilter", urlPatterns = {"/stock.jsp", "/getquote"}) 2. public class AuthenticateFilter implements Filter { 3. 4. public void doFilter(ServletRequest request, ServletResponse response, 5. FilterChain chain) throws IOException, ServletException { 6. String username = ((HttpServletRequest) request).getParameter("uname"); 7. String password = ((HttpServletRequest) request).getParameter("password"); 8. if (username == null || password == null) { 9. ((HttpServletResponse) response).sendRedirect("index.jsp"); } 10. if (username.equals("admin") && password.equals("admin")) { 11. chain.doFilter(request, response); } 12. else { 13. ((HttpServletResponse) response).sendRedirect("index.jsp"); } 14. } 15. 16. public void destroy() { 17. } 18. public void init(FilterConfig filterConfig) { 19. } 20. } 複製代碼 @WebInitParam 可使用@WebInitParam註解來制定Servlet或filter的初始參數。固然咱們也可使用@WebServlet或@WebFileter的initParam屬性來指定初始參數。下面是使用@WebInitParam的例子: 1. @WebServlet(name = "GetQuoteServlet", urlPatterns = {"/getquote"}) 2. @WebInitParam(name = "default_market", value = "NASDAQ") 3. public class GetQuoteServlet extends HttpServlet { 4. @Override 5. protected void doGet(HttpServletRequest request, HttpServletResponse response) 6. throws ServletException, IOException { 7. response.setContentType("text/html;charset=UTF-8"); 8. PrintWriter out = response.getWriter(); 9. try { 10. String market = getInitParameter("default_market"); 11. String symbol = request.getParameter("symbol"); 12. out.println("<h1>Stock Price in " + market + " is</h1>" + StockQuoteBean.getPrice(symbol, market)); 13. } finally { 14. out.close(); 15. } 16. } 17. } 複製代碼 下面是使用initParam屬性的例子: 1. @WebServlet(name = "GetQuoteServlet", 2. urlPatterns = {"/getquote"}, 3. initParams={@WebInitParam(name="default_market", value="NASDAQ")} 4. ) 5. public class GetQuoteServlet extends HttpServlet { 6. @Override 7. protected void doGet(HttpServletRequest request, HttpServletResponse response) 8. throws ServletException, IOException { 9. response.setContentType("text/html;charset=UTF-8"); 10. PrintWriter out = response.getWriter(); 11. try { 12. String market = getInitParameter("default_market"); 13. String symbol = request.getParameter("symbol"); 14. out.println("<h1>Stock Price in " + market + " is</h1>" + StockQuoteBean.getPrice(symbol, market)); 15. } finally { 16. out.close(); 17. } 18. } 19. } 複製代碼 @WebListener @WebListener註解被應用在做爲listener監聽web應用程序事件的類上,因此@WebListener可以被應用在實現了ServletContextListener,ServletContextAttributeListener,ServletRequestListener,ServletRequestAttributeListener,HttpSessionListener和HttpSessionAttributeListener接口的類上。在下面的例子中,該類實現了ServletContextListener接口。 1. @WebListener 2. public class QuoteServletContextListener implements ServletContextListener { 3. public void contextInitialized(ServletContextEvent sce) { 4. ServletContext context = sce.getServletContext(); 5. context.setInitParameter(「default_market」, 「NASDAQ」); 6. } 7. public void contextDestroyed(ServletContextEvent sce) { 8. } 9. } 複製代碼 @MultipartConfig 使用@MultipartConfig註解來指定Servlet要求的multipart MIME類型。這種類型的MIME附件將從request對象中讀取。 The Metadata and Common Annotations元數據與通用的註解 除了以上的Servlet特定的註解以外,Servlet3.0還支持JSR175(Java元數據規範)和JSR250(Java平臺通用註解)所規定的註解,包括: * 安全相關的註解,如 @DeclareRoles 和 @RolesAllowed * 使用EJB的註解,如 @EJB 和 @EJBs * 資源注入相關的註解,如 @Resource 和 @Resources * 使用JPA的註解,如 @PersistenceContext, @PersistenceContexts, @PersistenceUnit, 和 @PersistenceUnits * 生命週期的註解,如 @PostConstruct和 @PreDestroy * 提供WebService引用的註解,如 @WebServiceRef and @WebServiceRefs 註解和web.xml哪一個會生效 註解的引入使得web.xml變成可選的了。可是,咱們仍是可使用web.xml。容器會根據web.xml中的metadata-complete元素的值來決定使用web.xml仍是使用註解。若是該元素的值是true,那麼容器不處理註解,web.xml是全部信息的來源。若是該元素不存在或者其值不爲true,容器纔會處理註解。 Web框架的可插入性 咱們前面說過了Servlet3.0的改進之一就是使得咱們可以將框架和庫插入到web應用程序中。這種可插入性減小了配置,而且提升了web應用程序的模塊化。Servlet3.0是經過web模塊佈署描述片斷(簡稱web片斷)來實現插入性的。 一個web片斷就是web.xml文件的一部分,被包含在框架特定的Jar包的META-INF目錄中。Web片斷使得該框架組件邏輯上就是web應用程序的一部分,不須要編輯web佈署描述文件。 Web片斷中使用的元素和佈署文件中使用的元素基本相同,除了根元素不同。Web片斷的根元素是<web-fragment>,並且文件名必須叫作web-fragment.xml。容器只會在放在WEB-INF\lib目錄下的Jar包中查找web-fragment.xml文件。若是這些Jar包含有web-fragment.xml文件,容器就會裝載須要的類來處理他們。 在web.xml中,咱們要求Servlet的name必須惟一。一樣的,在web.xml和全部的web片斷中,Servlet的name也必須惟一。 下面就是一個web-fragment的例子: web-fragment.xml 1. <web-fragment> 2. <servlet> 3. <servlet-name>ControllerServlet</servlet-name> 4. <servlet-class>com.app.control.ControllerServlet</servlet-class> 5. </servlet> 6. <listener> 7. <listener-class>com.listener.AppServletContextListener</listener-class> 8. </listener> 9. </web-fragment> 複製代碼 框架的Jar包是放在WEB-INF\lib目錄下的,可是Servlet3.0提供兩種方法指定多個web片斷之間的順序: 1. 絕對順序 2. 相對順序 咱們經過web.xml文件中的<absolute-ordering>元素來指定絕對順序。這個元素有之元素name,name的值是各個web片斷的name元素的值。這樣就指定了web片斷的順序。若是多個web片斷有相同的名字,容器會忽略後出現的web片斷。下面是一個指定絕對順序的例子: web.xml 1. <web-app> 2. <name>DemoApp</name> 3. <absolute-ordering> 4. <name>WebFragment1</name> 5. <name>WebFragment2</name> 6. </absolute-ordering> 7. ... 8. </web-app> 複製代碼 相對順序經過web-fragment.xml中的<ordering>元素來肯定。Web片斷的順序由<ordering>的子元素<before>,<after>和<others>來決定。當前的web片斷會放在全部的<before>元素中的片斷以前。一樣的,會放在全部的<after>元素中的片斷以後。<others>用來代替全部的其餘片斷。注意只有當web.xml中沒有<absolute-ordering>時,容器纔會使用web片斷中定義的相對順序。 下面是一個幫助理解相對順序的例子: web-fragment.xml 1. <web-fragment> 2. <name>WebFragment1</name> 3. <ordering><after>WebFragment2</after></ordering> 4. ... 5. </web-fragment> 複製代碼 web-fragment.xml 1. <web-fragment> 2. <name>WebFragment2</name> 3. .. 4. </web-fragment> 複製代碼 web-fragment.xml 1. <web-fragment> 2. <name>WebFragment3</name> 3. <ordering><before><others/></before></ordering> 複製代碼 .. </web-fragment> 這些文件將會按照下面的順序被處理: 1. WebFragment3 2. WebFragment2 3. WebFragment1 包含WebFragment3的Jar文件被最早處理,包含WebFragment2的文件被第二個處理,包含WebFragment1的文件被最後處理。 若是既沒有定義絕對順序,也沒有定義相對順序,那麼容器就認爲全部的web片斷間沒有順序上的依賴關係。 Servlet中的異步處理 不少時候Servlet要和其餘的資源進行互動,例如訪問數據庫,調用web service。在和這些資源互動的時候,Servlet不得不等待數據返回,而後纔可以繼續執行。這使得Servlet調用這些資源的時候阻塞。Servlet3.0經過引入異步處理解決了這個問題。異步處理容許線程調用資源的時候不被阻塞,而是直接返回。AsyncContext負責管理從資源來的迴應。AsyncContext決定該回應是應該被原來的線程處理仍是應該分發給容器中其餘的資源。AsyncContext有一些方法如start,dispatch和complete來執行異步處理。 要想使用Servlet3.0的異步處理,咱們須要設置@Webservlet和@WebFilter註解的asyncSupport屬性。這個屬性是布爾值,缺省值是false。 Servlet3.0的異步處理能夠很好的和AJAX配合。在Servlet的init方法中,咱們可以訪問數據庫或從JMS讀取消息。在doGet或doPost方法中,咱們可以啓動異步處理,AsyncContext會經過AsyncEvent和AsyncListener來管理線程和數據庫操做/JMS操做本身的關係。 已有API的改進 除了這些新特性以外,Servlet3.0還對以往已經存在的API作了一些改進。 HttpServletRequest To support the multipart/form-data MIME type, the following methods have been added to the HttpServletRequest interface: 爲了支持multipart/form-data MIME類型,在HttpServletRequest接口中添加了項目的方法: * Iterable<Part> getParts() * Part getPart(String name) Cookies 爲了不一些跨站點***,Servlet3.0支持HttpOnly的cookie。HttpOnly cookie不想客戶端暴露script代碼。Servlet3.0在Cookie類中添加了以下的方法來支持HttpOnly cookie: * void setHttpOnly(boolean isHttpOnly) * boolean isHttpOnly() ServletContext 經過在ServletContext中添加下面的方法,Servlet3.0容許Servlet或filter被編程的加入到context中: * addServlet(String servletName, String className) * addServlet(String servletName, Servlet servlet) * addServlet(String servletName, Class<? extends Servlet> servletClass) * addFilter(String filterName, String className) * addFilter(String filterName, Filter filter) * addFilter(String filterName, Class<? extends Filter>filterClass) * setInitParameter (String name, String Value) Servlet3.0新功能: 異步處理 J2EE 6和Glassfish 3V正式發佈了,J2EE 6正式發佈了Servlet3.0, 爲了能更好的對WEB2.0提供支持, 3.0添加了異步處理的機制. HTTP1.1相對於HTTP1.0的影響. HTTP1.1最大的一個改變就是提供了長鏈接,這樣HTTP再也不是一次請求,一次鏈接的協議了,只要HTTP的connection不關閉,一次HTTP鏈接能夠支持任意屢次request/reponse處理. 當WEB Client與WEB Server創建鏈接時,客戶端能夠採用長鏈接,也就是說Client會一直保持對WEB Server的鏈接(例如:Browser對一個網站保持當鏈接,知道Browser關閉或最終退出該網站). 舊的WEB Server會爲每個Http鏈接分配一個active的Thread,這樣當Client的數量增大時,Server端Thread Pool的最大容量也須要相應增大,但Thread是至關耗內存的,一個不當心就會致使Server端NotEnoughMemory... 基於HTTP1.1,大部分支持Servlet2.X的WEB容器都採用的NIO去接收和處理請求. 當Client和Server端創建鏈接時,Server端並不分配一個Thread給HTTP鏈接.直到Server端收到Client端發送的Request時, Server纔開始爲該Request分配Thread(注意:這裏不是爲HTTP鏈接分配Thread). 這樣當大量的Client創建長鏈接與Server進行交互時,Server無需維持一個Thread給inactive的HTTP長鏈接, 每一個Servlet在doReceived()時其實對應的是一個active Request,而不是HTTPConnection自己. 這樣Server端所需的最大Thread數大大地減小了. AJAX的影響 1. Request的數量爆炸性增長增長 過去WEB Browser打開一個Web page,只須要和Web Server端創建一個HTTP鏈接.但AJAX技術出現之後,一個Web page上可能有多個與Web Server的鏈接,並且Ajax request一般是十分頻繁的,Server接收到的Request數量大大增加了, 這樣原先NIO的技術已經不能很好的支持基於Ajax的服務了. Servlet 3.0的異步處理就可以解決上面的問題. Servlet3.0的solution: 當request發送到Server端時,servlet的doReceived()將request放進一個queue裏,而後doReceived結束.這個時候server並無關閉response,Client端一直在等server端response的內容. Server端維護本身的ThreadPool,當ThreadPool裏有idle的Thread,就從queue裏取出一個request,分配idle的Thread給request,並進行處理. Java代碼 1. @WebServlet("/test" asyncSupported=true) 2. public class MyServlet extends HttpServlet { 3. ScheduledThreadPoolExecutor executor = null; 4. 5. public void init(ServletConfig arg0) throws ServletException { 6. executor = new ThreadPoolExecutor(10);//獨立的線程池處理請求 7. } 8. public void doGet(HttpServletRequest req, HttpServletResponse res) { 9. ... 10. AsyncContext aCtx = request.startAsync(req, res); 11. executor.execute(new AsyncWebService(aCtx));//異步處理 12. } 13. } 14. 15. public class AsyncWebService implements Runnable { 16. AsyncContext ctx; 17. public AsyncWebService(AsyncContext ctx) { 18. this.ctx = ctx; 19. } 20. public void run() {//處理請求 21. //Do something here ... 22. 23. // Dispatch the request to render the result to a JSP. 24. ctx.dispatch("/render.jsp"); 25. } 26.} @WebServlet("/test" asyncSupported=true) public class MyServlet extends HttpServlet { ScheduledThreadPoolExecutor executor = null; public void init(ServletConfig arg0) throws ServletException { executor = new ThreadPoolExecutor(10);//獨立的線程池處理請求 } public void doGet(HttpServletRequest req, HttpServletResponse res) { ... AsyncContext aCtx = request.startAsync(req, res); executor.execute(new AsyncWebService(aCtx));//異步處理 } } public class AsyncWebService implements Runnable { AsyncContext ctx; public AsyncWebService(AsyncContext ctx) { this.ctx = ctx; } public void run() {//處理請求 //Do something here ... // Dispatch the request to render the result to a JSP. ctx.dispatch("/render.jsp"); }} 以上的例子能夠用於處理對Ajax的請求,由於一般Ajax的請求多,但對響應速度的要求並不過高. 對於正常的頁面請求,要求必定的響應速度,能夠沿用之前Servlet同步的實現. 2. Server端推送信息 在Web2.0的應用中, Ajax可用經過不斷的發送Request來獲取Server端某種信息的變化,但這種實現會產生大量的Client請求. 當前推薦的方法是,讓Server端本身推送信息變化給Client. 由於Servlet3.0提供了異步處理的方式, Request提交給Server之後, Server能夠爲Request註冊一個Listener,由Listener去monitor信息的變化,當信息發生變化時,由Listener負責把信息變化發送給Cient(Listener關閉HTTP response).