Servlet 3.0作為Java EE6規范體系中一員,隨著Java EE6規范一起發布。之前51CTO也曾報導過《Java EE 6新特性之Servlet 3.0的異步處理》的相關內容,而本文將比較全面的、並結合代碼實例介紹Servlet 3.0的新特性。
關於Java EE 6平台的更多內容,51CTO推薦閱讀:Java EE 6平台指南
異步處理支持:有了該特性,Servlet線程不再需要一直阻塞,直到業務處理完畢才能再輸出響應,最後才結束該Servlet線程。在接收到請求之後,Servlet線程可以將耗時的操作委派給另一個線程來完成,自己在不生成響應的情況下返回至容器。針對業務處理較耗時的情況,這將大大減少服務器資源的占用,並且提高並發處理速度。
新增的注解支持:該版本新增了若干注解,用於簡化Servlet、過濾器(Filter)和監聽器(Listener)的聲明,這使得web.XML部署描述文件從該版本開始不再是必選的了。可插性支持:熟悉Struts2的開發者一定會對其通過插件的方式與包括Spring在內的各種常用框架的整合特性記憶猶新。
將相應的插件封裝成JAR包並放在類路徑下,Struts2運行時便能自動加載這些插件。現在Servlet 3.0提供了類似的特性,開發者可以通過插件的方式很方便的擴充已有Web應用的功能,而不需要修改原有的應用。下面我們將逐一講解這些新特性,通過下面的學習,讀者將能夠明晰了解Servlet 3.0的變化,並能夠順利使用它進行日常的開發工作。
異步處理支持
Servlet 3.0之前,一個普通Servlet的主要工作流程大致如下:首先,Servlet接收到請求之後,可能需要對請求攜帶的數據進行一些預處理;接著,調用業務接口的某些方法,以完成業務處理;最後,根據處理的結果提交響應,Servlet線程結束。
其中第二步的業務處理通常是最耗時的,這主要體現在數據庫操作,以及其它的跨網絡調用等,在此過程中,Servlet線程一直處於阻塞狀態,直到業務方法執行完畢。在處理業務的過程中,Servlet資源一直被占用而得不到釋放,對於並發較大的應用,這有可能造成性能的瓶頸。對此,在以前通常是采用私有解決方案來提前結束Servlet線程,並及時釋放資源。
Servlet 3.0針對這個問題做了開創性的工作,現在通過使用Servlet 3.0的異步處理支持,之前的Servlet處理流程可以調整為如下的過程:首先,Servlet接收到請求之後,可能首先需要對請求攜帶的數據進行一些預處理;接著,Servlet線程將請求轉交給一個異步線程來執行業務處理,線程本身返回至容器,此時Servlet還沒有生成響應數據,異步線程處理完業務以後,可以直接生成響應數據(異步線程擁有ServletRequest和ServletResponse對象的引用),或者將請求繼續轉發給其它Servlet。如此一來,Servlet線程不再是一直處於阻塞狀態以等待業務邏輯的處理,而是啟動異步線程之後可以立即返回。
異步處理特性可以應用於Servlet和過濾器兩種組件,由於異步處理的工作模式和普通工作模式在實現上有著本質的區別,因此默認情況下,Servlet和過濾器並沒有開啟異步處理特性,如果希望使用該特性,則必須按照如下的方式啟用:
對於使用傳統的部署描述文件(web.XML)配置Servlet和過濾器的情況,Servlet 3.0為<servlet>和<filter>標簽增加了<async-supported>子標簽,該標簽的默認取值為false,要啟用異步處理支持,則將其設為true即可。以Servlet為例,其配置方式如下所示:
- <servlet>
- <servlet-name>DemoServlet</servlet-name>
- <servlet-class>footmark.servlet.DemoServlet</servlet-class>
- <async-supported>true</async-supported>
- </servlet>
對於使用Servlet 3.0提供的@WebServlet和@WebFilter進行Servlet或過濾器配置的情況,這兩個注解都提供了asyncSupported屬性,默認該屬性的取值為false,要啟用異步處理支持,只需將該屬性設置為true即可。以@WebFilter為例,其配置方式如下所示:
- @WebFilter(urlPatterns="/demo",asyncSupported=true)
- publicclassDemoFilterimplementsFilter{...}
一個簡單的模擬異步處理的Servlet示例如下:
- @WebServlet(urlPatterns="/demo",asyncSupported=true)
- publicclassAsyncDemoServletextendsHttpServlet{
- @Override
- publicvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)
- throwsIOException,ServletException{
- resp.setContentType("text/Html;charset=UTF-8");
- PrintWriterout=resp.getWriter();
- out.println("進入Servlet的時間:"+newDate()+".");
- out.flush();
- //在子線程中執行業務調用,並由其負責輸出響應,主線程退出
- AsyncContextctx=req.startAsync();
- newThread(newExecutor(ctx)).start();
- out.println("結束Servlet的時間:"+newDate()+".");
- out.flush();
- }
- }
- publicclassExecutorimplementsRunnable{
- privateAsyncContextctx=null;
- publicExecutor(AsyncContextctx){
- this.ctx=ctx;
- }
- publicvoidrun(){
- try{
- //等待十秒鐘,以模擬業務方法的執行
- Thread.sleep(10000);
- PrintWriterout=ctx.getResponse().getWriter();
- out.println("業務處理完畢的時間:"+newDate()+".");
- out.flush();
- ctx.complete();
- }catch(Exceptione){
- e.printStackTrace();
- }
- }
- }
除此之外,Servlet 3.0還為異步處理提供了一個監聽器,使用AsyncListener接口表示。它可以監控如下四種事件:
1.異步線程開始時,調用AsyncListener的onStartAsync(AsyncEventevent)方法;
2.異步線程出錯時,調用AsyncListener的onError(AsyncEventevent)方法;
3.異步線程執行超時,則調用AsyncListener的onTimeout(AsyncEventevent)方法;
4.異步執行完畢時,調用AsyncListener的onComplete(AsyncEventevent)方法;
要注冊一個AsyncListener,只需將准備好的AsyncListener對象傳遞給AsyncContext對象的addListener()方法即可,如下所示:
- AsyncContextctx=req.startAsync();
- ctx.addListener(newAsyncListener(){
- publicvoidonComplete(AsyncEventasyncEvent)throwsIOException{
- //做一些清理工作或者其他
- }
- ...
- });