轉自:http://www.cnblogs.com/rongxh7/archive/2010/04/28/1723200.html
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)
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,并進行處理.
- @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() {
-
-
-
- ctx.dispatch("/render.jsp");
- }
- }
@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).