注意:Servlet3.0的項目一定要使用Tomcat7.0才能看到效果!!
1、新增標注支持
在Servlet3.0的部署描述文件web.xml的頂層標簽<web-app>中有一個metadata-complete屬性,如果把該屬性的值設置為true,則容器在部署時只依賴於web.xml部署文件中的配置,會忽略所以的標注(同時也會跳過web-fragment.xml的掃描,即禁用可插性支持);如果把該屬性的值設置為false或者不配置該屬性,則表示啟用標注支持和可插性支持。
1)WebServlet標注
@WebServlet用於將一個類聲明為Servlet,該標注將會在部署時被容器處理,容器將根據具體的屬性配置將相應的類部署為Servlet。
2)WebInitParam標注
@WebInitParam標注通常不單獨使用,而是配合@WebServlet或者@WebFilter使用。它的作業是為Servlet或者過濾器指定初始化參數,這等價於web.xml中<servlet>和<filter>的<init-param>子標簽
| 屬性名 | 類型 | 是否可選 | 描述 |
| name | String | 否 | 指定參數的名字,等價於<param-name> |
| value | String | 否 | 指定參數的值,等價於<param-value> |
| description | String | 是 | 指定參數的描述,等價於<description> |
創建一個Servlet3Annotation類:
package com.yyq.servlet3.annotation; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * Created by gao on 16-4-14. */ @WebServlet(name = "servlet3annotation", urlPatterns = {"/servlet3"}, description = "servletinfo", displayName = "abc", asyncSupported = true, loadOnStartup = -1, initParams = {@WebInitParam(name = "username", value = "YangYuqin")}) public class Servlet3Annotation extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //獲取ServletConfig的實例 ServletConfig config = this.getServletConfig(); //獲取指定參數名稱的值 String name = config.getInitParameter("username"); resp.setContentType("text/html;charset=utf-8"); PrintWriter out = resp.getWriter(); out.println("<html>"); out.println("<head><title>Servlet3應用實例</title></head>"); out.println("<body>"); out.print("獲取InitParamServlet的初始化參數\"username\"的字符串值:" + name); out.println("</body>"); out.println("</html>"); } @Override public void destroy() { //空 } @Override public void init() throws ServletException { //空 } }
啟動Tomcat,輸入:http://localhost:8080/servlet3
3)WebFilter標注
@WebFilter用於將一個類聲明為過濾器,該標注將會在部署時被容器處理。以下屬性均為可選屬性,但是value、urlPatterns、servletNames三者必需至少包含一個,且value和urlPattern不能共存,如果同時指定,通常忽略value的取值。
| 屬性名 | 類型 | 描述 |
| filterName | String | 指定過濾器的name屬性,等價於<filter-name> |
| value | String[] | 該屬性等價於urlPatterns屬性,兩個屬性不能同時使用 |
| urlPatterns | String[] | 指定一組Servlet的URL匹配模式,等價於<url-pattern>標簽 |
| servletNames | String[] | @WebServlet中的name屬性的取值,或者是web.xml中<servlet-name>的取值 |
| initParams | WebInitParam[] | 指定一組Servlet初始化參數,等價於<init-param>標簽 |
| dispatcherTypes | DispatcherType | 指定過濾器的轉發模式。具體取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST |
| asyncSupported | boolean | 聲明過濾器是否支持異步操作模式,等價於<async-supported>標簽 |
| description | String | 該Servlet的描述信息,等價於<description>標簽 |
| displayName | String | 該Servlet的顯示名,通常配合工具使用,等價於<display-name>標簽 |
package com.yyq.servlet3.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.annotation.WebInitParam; import java.io.IOException; /** * Created by gao on 16-4-14. */ @WebFilter(servletNames = {"servlet3filterannotation"}, filterName = "characterFilter", initParams = {@WebInitParam(name = "encoding", value = "UTF-8")}) public class Servlet3FilterAnnotation implements Filter { private FilterConfig filterConfig = null; @Override public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //獲取此Filter的初始參數的值 String encoding = filterConfig.getInitParameter("encoding"); System.out.println(encoding); //設置請求數據的編碼方式 servletRequest.setCharacterEncoding(encoding); //把請求和響應對象傳給過濾鏈中的下一個要調用的過濾器或Servlet filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { //空 } }
4)WebListener標注
該標注用於將類聲明為監聽器。
| 屬性名 | 類型 | 是否可選 | 描述 |
| value | String | 是 | 該監聽器的描述信息 |
監聽類:
package com.yyq.servlet3.listener; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import java.util.LinkedList; import java.util.List; /** * Created by gao on 16-4-14. */ @WebListener("This is the Listener") public class Servlet3Listener implements ServletContextListener, HttpSessionAttributeListener, HttpSessionListener { private ServletContext application = null; //往會話中添加屬性時回調的方法 public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) { //取得用戶名列表 List<String> online = (List<String>) this.application.getAttribute("online"); if ("username".equals(httpSessionBindingEvent.getName())) { //將當前用戶名添加到列表中 online.add((String) httpSessionBindingEvent.getValue()); } //將添加后的列表重新設置到application屬性中 this.application.setAttribute("online", online); } //以下方法用空實現 @Override public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) { } @Override public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) { } @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { } //應用上下文初始化會回調的方法 @Override public void contextInitialized(ServletContextEvent servletContextEvent) { //初始化一個application對象 this.application = servletContextEvent.getServletContext(); //設置一個列表屬性,用於保存在線用戶名 this.application.setAttribute("online",new LinkedList<String>()); } //會話銷毀時會回調的方法 @Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { //取得用戶名列表 List<String> online = (List<String>) this.application.getAttribute("online"); //取得當前用戶名 String username = (String) httpSessionEvent.getSession().getAttribute("username"); //將此用戶名從列表中刪除 online.remove(username); //將刪除后的列表重新設置到application屬性中 this.application.setAttribute("online", online); } }
登錄Servlet類:
package com.yyq.servlet3.listener; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.List; /** * Created by gao on 16-4-14. */ @WebServlet(name = "servlet3login",urlPatterns = {"/login"}) public class Servlet3Login extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //設置響應內容類型 req.setCharacterEncoding("utf-8"); //獲取請求參數中的用戶名 String username = req.getParameter("username"); //往Session中添加屬性 //會觸發HttpSessionAttributeListener中的attributeAdded方法 if (username != null && !username.equals("")) { req.getSession().setAttribute("username", username); } //從應用上下文中獲取在線用戶名列表 List<String> online = (List<String>) getServletContext().getAttribute("online"); resp.setContentType("text/html;charset=utf-8"); PrintWriter out = resp.getWriter(); out.println("<html>"); out.println("<head><title>用戶列表</title></head>"); out.println("<body>"); out.println("當前用戶是:" + username); out.println("<hr/><h3>在線用戶列表</h3>>"); int size = ((online == null) ? 0 : online.size()); for (int i = 0; i < size; i++) { if (i > 0) { out.println("<br />"); } out.println(i + 1 + "." + online.get(i)); } //注意:要對連接URL進行自動重寫處理 out.println("<hr/><a href=\"" + resp.encodeURL("logout") + "\">注銷</a>"); out.println("</body>"); out.println("</html>"); out.flush(); out.close(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override public void destroy() { //空 } @Override public void init() throws ServletException { //空 } }
注銷Servlet類:
package com.yyq.servlet3.listener; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.List; /** * Created by gao on 16-4-14. */ @WebServlet(name = "servlet3logout", urlPatterns = {"/logout"}) public class Servlet3Logout extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //設置響應內容類型 req.setCharacterEncoding("utf-8"); //銷毀會話,會觸發SessionLinstener中的sessionDestroyed方法 req.getSession().invalidate(); //從應用上下文中獲取在線用戶名列表 List<String> online = (List<String>)getServletContext().getAttribute("online"); resp.setContentType("text/html;charset=utf-8"); PrintWriter out = resp.getWriter(); out.println("<html>"); out.println("<head><title>用戶列表</title></head>"); out.println("<body>"); out.print("<h3>在線用戶列表</h3>"); int size = ((online == null) ? 0 : online.size()); for (int i = 0; i < size; i++) { if (i > 0) { out.println("<br />"); } out.println(i + 1 + "." + online.get(i)); } out.println("<hr><a href=\"index.jsp\">主頁</hr>"); out.println("</body>"); out.println("</html>"); out.flush(); out.close(); } @Override public void destroy() { //空 } @Override public void init() throws ServletException { //空 } }
登錄頁面:index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title></title>
</head>
<body>
<form action="login" method="post">
用戶名:<input type="text" name="username">
<input type="submit" value="登錄">
<br />
<br />
</form>
</body>
</html>
5)MultipartConfig標注
該標注主要是為了輔助Servlet3.0中HttpServletRequest提供的對上傳文件的支持。該標注標注在Servlet上,表示該Servlet希望處理的請求的MIME類型是multipart/form-data。
| 屬性名 | 類型 | 是否可選 | 描述 |
| fileSizeThreshold | int | 是 | 當數據量大於該值時,內容將被寫入文件 |
| location | String | 是 | 存放生成的文件地址 |
| maxFileSize | long | 是 | 允許上傳的文件最大值。默認值為-1,表示沒有限制 |
| maxRequestSize | long | 是 | 針對該multipart/form-data請求的最大數量,默認值為-1,表示沒有限制 |
文件上傳頁面upload.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*" pageEncoding="UTF-8" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Servlet3.0 上傳文件</title> </head> <body> <form action="uploadfile" method="post" enctype="multipart/form-data"> <table> <tr> <td> 選擇文件: </td> <td> <input type="file" name="file"/> </td> </tr> <tr> <td>描述: </td> <td> <input type="text" name="description"/> </td> </tr> <tr> <td colspan="2"> <input type="submit" value="提交"/> <input type="reset" value="重置"/> </td> </tr> </table> </form> </body> </html>
處理上傳文件的Servlet:
package com.yyq.servlet3.multipartconfig; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.UUID; /** * Created by gao on 16-4-14. */ @WebServlet(name = "upFile", urlPatterns = {"/uploadfile"}) @MultipartConfig(maxFileSize = 500000, maxRequestSize = -1) public class FileUploadServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //resp.setContentType("text/html;charset=utf-8"); //獲取請求參數值 Part part = req.getPart("file"); //存儲路徑 String storePath = req.getServletContext().getRealPath("/temp"); //Servlet3沒有提供直接獲取文件名,后綴名的方法,需要從請求頭中解析出來 //獲取請求頭 String header = part.getHeader("content-disposition"); //獲取文件后綴名 //String suffix = parseFileName(header); String name = parseFileName(header); //重新命名 //String name = UUID.randomUUID() + suffix; //把文件寫到指定路徑 part.write(storePath + File.separator + name); // PrintWriter out = resp.getWriter(); // out.println("上傳成功"); // out.flush(); // out.close(); //獲得文件描述信息 String description = req.getParameter("description"); req.setAttribute("f", name); req.setAttribute("des", description); req.getRequestDispatcher("info.jsp").forward(req, resp); } /* *根據請求頭解析出上傳文件的后綴名稱 */ /** * 根據請求頭解析出文件名 * 請求頭的格式:火狐和google瀏覽器下:form-data; name="file"; filename="snmp4j--api.zip" * IE瀏覽器下:form-data; name="file"; filename="E:\snmp4j--api.zip" * * @param header 請求頭 * @return 文件名 */ public String parseFileName(String header) { //return header.substring(header.lastIndexOf("."), header.length() - 1); /** * String[] tempArr1 = header.split(";");代碼執行完之后,在不同的瀏覽器下,tempArr1數組里面的內容稍有區別 * 火狐或者google瀏覽器下:tempArr1={form-data,name="file",filename="snmp4j--api.zip"} * IE瀏覽器下:tempArr1={form-data,name="file",filename="E:\snmp4j--api.zip"} */ String[] tempArr1 = header.split(";"); /** *火狐或者google瀏覽器下:tempArr2={filename,"snmp4j--api.zip"} *IE瀏覽器下:tempArr2={filename,"E:\snmp4j--api.zip"} */ String[] tempArr2 = tempArr1[2].split("="); //獲取文件名,兼容各種瀏覽器的寫法 String fileName = tempArr2[1].substring(tempArr2[1].lastIndexOf("\\") + 1).replaceAll("\"", ""); return fileName; } @Override public void destroy() { //空 } @Override public void init() throws ServletException { //空 } }
顯示上傳文件和描述信息的頁面info.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*" pageEncoding="UTF-8" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path +"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Servlet3.0 上傳文件</title> </head> <body> <h3><%=request.getAttribute("des")%></h3> <img alt = "servlet3" src="<%=basePath %>temp/<%=request.getAttribute("f")%>"> </body> </html>
2、異步處理支持
Servlet3.0支持異步處理支持,Servlet接收到請求之后,可能首先需要對請求攜帶的數據進行一些預處理;接着,Servlet線程將請求轉交給一個異步線程來執行業務處理,線程本身返回至容器,此時Servlet還沒有生成響應數據,異步線程處理完業務以后,可以直接生成響應數據(異步線程擁有ServletRequest和ServletResponse對象的引用),或者將請求繼續轉發給其他Servlet。
1)對於使用傳統的部署描述文件web.xml配置Servlet和過濾器的情況,Servlet3.0為<servlet>和<filter>標簽增加了<async-supported>子標簽,該標簽的默認取值為false,要啟用異步處理支持,則將其設為true即可。
<servlet>
<servlet-name>DemoServlet</servlet-name>
<servlet-class>com.yyq.servlet3.asyncsupported.AsyncDemoServlet</servlet-class>
<async-supported>true</async-supported>
</servlet>
2)對於使用Servlet3.0提供的@WebServlet和@WebFilter進行Servlet或過濾器配置的情況,這兩個標注都提供了asyncSupported屬性,默認該屬性的取值為false,要啟動異步處理支持,只需將該屬性設置為true即可。
package com.yyq.servlet3.asyncsupported; import javassist.bytecode.analysis.Executor; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; /** * Created by gao on 16-4-15. */ @WebServlet(urlPatterns = {"/asyncdemo"}, asyncSupported = true) public class AsyncDemoServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=utf-8"); PrintWriter out = resp.getWriter(); out.println("進入Servlet的時間:" + new Date() + "."); out.flush(); //在子線程中執行業務調用,並由其負責輸出響應,主線程退出 AsyncContext ctx = req.startAsync(); new Thread(new Executor(ctx)).start(); out.println("結束Servlet的時間:" + new Date() + "."); out.flush(); } public class Executor implements Runnable { private AsyncContext ctx = null; public Executor(AsyncContext ctx) { this.ctx = ctx; } public void run() { try { //等待10秒鍾,以模擬業務方法的執行 Thread.sleep(10000); PrintWriter out = ctx.getResponse().getWriter(); out.println("業務處理完畢的時間:" + new Date() + "."); out.flush(); ctx.complete(); }catch (Exception e){ e.printStackTrace(); } } } @Override public void destroy() { //空 } @Override public void init() throws ServletException { //空 } }
啟動Tomcat,輸入:http://localhost:8080/asyncdemo

3、可插性支持
Servlet3.0新增的可插性(Pluggability)支持則將Servlet配置的靈活性提升到了新的高度。使用該特性,現在我們可以在不修改已有Web應用的前提下,只需將按照一定格式打包成的JAR包放到WEB-INF/lib目錄下,即可實現新的功能的擴充,不需要額外的配置。Servlet3.0引入了稱為“Web模塊部署描述文件片段”的web-fragment.xml來實現可插性的。web-fragment.xml部署描述文件可以定義一切可以在web.xml中定義的內容。
1)新建的Servlet類:
package com.yyq.servlet3.pluggability; import javax.naming.NamingException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * Created by gao on 16-4-15. */ public class FragmentDemoServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=utf-8"); PrintWriter out = resp.getWriter(); out.println("這是我Servlet3.0的第一個可插性示例"); out.flush(); } @Override public void destroy() { //空 } @Override public void init() throws ServletException { //空 } }
2)在web目錄下新建一個目錄META-INF,在該目錄下新建一個web-fragment.xml模塊部署描述符文件片段:
<?xml version="1.0" encoding="UTF-8"?> <web-fragment xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd" metadata-complete="true"> <!--給當前配置文件定義一個名稱--> <name>FragmentA</name> <servlet> <servlet-name>fragmentDemo</servlet-name> <servlet-class>com.yyq.servlet3.pluggability.FragmentDemoServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>fragmentDemo</servlet-name> <url-pattern>/fragment</url-pattern> </servlet-mapping> </web-fragment>
3)將FragmentDemoServlet和META-INF目錄一起打包成JAR包,假如JAR包叫fragment.jar。
4)將fragment.jar放到其他Web項目中的WEB\lib目錄中,然后訪問
http://localhost:8080/fragment即可。
