Servlet3.0的新特性


注意: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。
     e824b899a9014c0818ddbb0e087b02087af4f4b3.jpg
    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="提交"/>&nbsp;&nbsp;
                <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即可。
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM