深入理解Servlet技術(shù)

1. 什么是servlet

servlet Servlet是javax.servlet.Servlet包中定義的一個(gè)接口.它聲明了servlet生命周期中不可少的三個(gè)方法init,service和destory(),每個(gè)servlet必須實(shí)現(xiàn)這三個(gè)方法,而且服務(wù)器在特定的時(shí)刻調(diào)用.

2.生命周期

容器加載 -> 初始化 init (僅一次) -> 進(jìn)入服務(wù) service (Get/Post 請(qǐng)求)-> 銷毀 destroy -> 容器卸載

執(zhí)行過程

  • 客戶端發(fā)出請(qǐng)求http://localhost:8080/hello
  • 根據(jù)web.xml文件的配置,找到<url-pattern>子元素的值“/hello”的<servlet-mapping>元素
    讀取<servlet-mapping>元素的<servlet-name>子元素的值,由此確定Servlet的名字為”HelloServlet”
    找到<servlet-name>值為HelloServlet的<servlet>元素
    讀取<servlet>元素的<servlet-class>子元素的值,由此確定Servlet的類名為com.kaishengit.web.HelloServlet。
    到Tomcat安裝目錄/webapps/Demo1/WEB-INF/classes/cn/itcast目錄下查找到HelloServlet.class文件

客戶端發(fā)出請(qǐng)求,容器產(chǎn)生request和response對(duì)象,容器根據(jù)url找到合適的servlet并分配線程進(jìn)行訪問,service根據(jù)請(qǐng)求頭調(diào)用doXX方法,servlet使用相應(yīng)對(duì)象通過容器對(duì)客戶端做出響應(yīng),service方法執(zhí)行結(jié)束,然后調(diào)用destory()方法,訪問線程和request、response對(duì)象被銷毀。


Servlet的執(zhí)行過程.jpg

細(xì)節(jié)

  • 由于客戶端是通過URL地址訪問web服務(wù)器中的資源,所以Servlet程序若想被外界訪問,必須把servlet程序映射到一個(gè)URL地址上,這個(gè)工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成。
  • <servlet>元素用于注冊(cè)Servlet,它包含有兩個(gè)主要的子元素:<servlet-name>和<servlet-class>,分別用于設(shè)置Servlet的注冊(cè)名稱和Servlet的完整類名。
    一個(gè)<servlet-mapping>元素用于映射一個(gè)已注冊(cè)的Servlet的一個(gè)對(duì)外訪問路徑,它包含有兩個(gè)子元素:<servlet-name>和<url-pattern>,分別用于指定Servlet的注冊(cè)名稱和Servlet的對(duì)外訪問路徑。例如:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  
  
  <!-- 
       配置Servlet
     servlet-name 名字自定義
     servlet-class Servlet類的完全限定名
  -->
  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.kaishengit.web.HelloServlet</servlet-class>
  </servlet>
  <!-- 
    servlet-name 名字自定義,但是必須和<servlet>節(jié)點(diǎn)中的<servlet-name>值相同
    url-pattern 客戶端請(qǐng)求的路徑名稱,必須以/開頭
   -->
  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
  
  <servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com.kaishengit.web.LoginServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/login</url-pattern>
  </servlet-mapping>
  
  
  
  <!-- 
    重要提示?。?!修改web.xml文件必須要重啟容器,才能生效
    
    歡迎頁(yè)面配置,優(yōu)先級(jí)是從上倒下越來越低,如果都找不到,則顯示404錯(cuò)誤頁(yè)面
    
    常見的HTTP狀態(tài)碼:
    200 正常響應(yīng)
    404 訪問的資源不存在
    500 服務(wù)器異常 
  -->
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>main.jsp</welcome-file>
    <welcome-file>home.jsp</welcome-file>
  </welcome-file-list>
  
  
  
</web-app>

Servlet重要的四個(gè)生命周期方法

  • 構(gòu)造方法: 創(chuàng)建servlet對(duì)象的時(shí)候調(diào)用。默認(rèn)情況下,第一次訪問servlet的時(shí)候創(chuàng)建servlet對(duì)象 只調(diào)用1次。證明servlet對(duì)象在tomcat是單實(shí)例的。
  • init方法: 創(chuàng)建完servlet對(duì)象的時(shí)候調(diào)用。只調(diào)用1次。
  • service方法: 每次發(fā)出請(qǐng)求時(shí)調(diào)用。調(diào)用n次。
  • destroy方法: 銷毀servlet對(duì)象的時(shí)候調(diào)用。停止服務(wù)器或者重新部署web應(yīng)用時(shí)銷毀servlet對(duì)象。
    只調(diào)用1次。

偽代碼演示servlet的生命周期

Tomtcat內(nèi)部代碼運(yùn)行:
```
        1)通過映射找到到servlet-class的內(nèi)容,字符串: gz.itcast.a_servlet.FirstServlet
        2)通過反射構(gòu)造FirstServlet對(duì)象
                2.1 得到字節(jié)碼對(duì)象
                Class clazz = class.forName("gz.itcast.a_servlet.FirstServlet");
                2.2 調(diào)用無參數(shù)的構(gòu)造方法來構(gòu)造對(duì)象
                Object obj = clazz.newInstance();     ---1.servlet的構(gòu)造方法被調(diào)用
        3)創(chuàng)建ServletConfig對(duì)象,通過反射調(diào)用init方法
                3.1 得到方法對(duì)象
                Method m = clazz.getDeclareMethod("init",ServletConfig.class);
                3.2 調(diào)用方法
                m.invoke(obj,config);             --2.servlet的init方法被調(diào)用
        4)創(chuàng)建request,response對(duì)象,通過反射調(diào)用service方法
                4.1 得到方法對(duì)象
                Methodm m =clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletResponse.class);
                4.2 調(diào)用方法
                m.invoke(obj,request,response);  --3.servlet的service方法被調(diào)用
        5)當(dāng)tomcat服務(wù)器停止或web應(yīng)用重新部署,通過反射調(diào)用destroy方法
                5.1 得到方法對(duì)象
                Method m = clazz.getDeclareMethod("destroy",null);
                5.2 調(diào)用方法
                m.invoke(obj,null);            --4.servlet的destroy方法被調(diào)用
            

```

(重點(diǎn)圖)用時(shí)序圖來演示servlet的生命周期

image.png

常用類

HttpServletRequest
  • 是JSP的內(nèi)置對(duì)象之一,jsp中叫做request
  • 用于接受客戶端請(qǐng)求,可以獲取客戶端一些數(shù)據(jù)
  • getParameter(String name) 獲取URL或者form表單中的值
  • setAttribute(String name,Object value)向跳轉(zhuǎn)目標(biāo)對(duì)象傳值
  • getAttribute(String name) 獲取傳值
  • getRequestDispatcher(String path) 獲取RequestDispatcher對(duì)象,使用RequestDispatcher進(jìn)
    行請(qǐng)求轉(zhuǎn)發(fā)跳轉(zhuǎn)
HttpservletResponse
  • 給客戶端做出響應(yīng)
  • sendRedirect(String urlName) ,以重定向方式,跳轉(zhuǎn)指定路徑中


    image.png
重定向和請(qǐng)求轉(zhuǎn)發(fā)的區(qū)別
  • 重定向跳轉(zhuǎn)是使用url重寫的方式進(jìn)行值的傳遞,值顯示在url的地址欄中
  • 請(qǐng)求轉(zhuǎn)發(fā)使用HttpServletRequest對(duì)象的setAttribute方法進(jìn)行值傳遞,值不會(huì)顯示在地址
    欄中
  • 重定向傳值方式傳遞適合不敏感數(shù)據(jù)以及簡(jiǎn)單的字符串、數(shù)字等基本類型
  • 請(qǐng)求轉(zhuǎn)發(fā)跳轉(zhuǎn)傳值方式適合傳遞敏感數(shù)據(jù)以及對(duì)象、數(shù)組、集合等類型的數(shù)據(jù)
  • 重定向跳轉(zhuǎn)后瀏覽器的地址欄中顯示的是跳轉(zhuǎn)目標(biāo)的URL(地址欄會(huì)發(fā)生改變)
  • 請(qǐng)求轉(zhuǎn)發(fā)跳轉(zhuǎn)后地址欄不會(huì)顯示跳轉(zhuǎn)目標(biāo)的URL(地址欄不會(huì)發(fā)生改變)
  • 重定向跳轉(zhuǎn)不會(huì)引起表單的重復(fù)提交
  • 請(qǐng)求轉(zhuǎn)發(fā)跳轉(zhuǎn)會(huì)引起表單的重復(fù)提交
  • 重定向跳轉(zhuǎn)本質(zhì)上是服務(wù)器產(chǎn)生302響應(yīng),客戶端再次向服務(wù)器發(fā)出二次請(qǐng)求


    image.png

會(huì)話技術(shù)

  • Cookie 技術(shù): 會(huì)話數(shù)據(jù)保存在瀏覽器客戶端
  • Session技術(shù): 會(huì)話數(shù)據(jù)保存在服務(wù)端

Cookie核心技術(shù)

Cookie類:用于存儲(chǔ)會(huì)話數(shù)據(jù)
  1. 構(gòu)造cookie對(duì)象
Cookie(java.lang.String name, java.lang.String value)
  1. 設(shè)置cookie
void setPath(java.lang.String uri)   :設(shè)置cookie的有效訪問路徑
                   void setMaxAge(int expiry) : 設(shè)置cookie的有效時(shí)間
                   void setValue(java.lang.String newValue) :設(shè)置cookie的值

  1. 發(fā)送cookie到瀏覽器端保存
void response.addCookie(Cookie cookie)  : 發(fā)送cookie
  1. 服務(wù)器接受cookie
Cookie[] request.getCookies()  : 接收cookie

設(shè)置
package com.kaishengit.web;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SetCookieServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Cookie cookie = new Cookie("playId","1002");
        cookie.setDomain("localhost");
        cookie.setPath("/");
        cookie.setMaxAge(60 * 60 * 24 * 7);
        cookie.setHttpOnly(true); //
        resp.addCookie(cookie);

        Cookie cookie2 = new Cookie("productId","2908");
        cookie2.setDomain("localhost");
        cookie2.setPath("/");
        cookie2.setMaxAge(60 * 60 * 24 * 7);
        resp.addCookie(cookie2);

        System.out.println("set cookie success!");
    }
}

獲取Cookie

package com.kaishengit.web;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class GetCookieServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      Cookie[] cookies = req.getCookies();
      if(cookies != null) {
          for(Cookie cookie : cookies) {
              System.out.println(cookie.getName() + " -> " + cookie.getValue());
          }
      }

      System.out.println("get cookie success!");
  }
}


JQuery Cookie插件:

Cookie插件:
Cookie原理
  1. 服務(wù)器創(chuàng)建cookie對(duì)象,把會(huì)話數(shù)據(jù)存儲(chǔ)到cookie對(duì)象中
new Cookie("name","value");

2.服務(wù)器發(fā)送cookie信息到瀏覽器

    response.addCookie(cookie);
    舉例: set-cookie: name=eric  (隱藏發(fā)送了一個(gè)set-cookie名稱的響應(yīng)頭)
  1. 瀏覽器得到服務(wù)器發(fā)送的cookie,然后保存在瀏覽器端。
  2. 瀏覽器在下次訪問服務(wù)器時(shí),會(huì)帶著cookie信息
 舉例: cookie: name=eric  (隱藏帶著一個(gè)叫cookie名稱的請(qǐng)求頭)
  1. 服務(wù)器接收到瀏覽器帶來的cookie信息
request.getCookies();

記住賬號(hào)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="/static/css/bootstrap.min.css" />
</head>
<body>

    <div class="container">
        
        
        <c:if test="${not empty param.callback}">
            <div class="alert alert-danger">請(qǐng)登錄再繼續(xù)</div>
        </c:if>
        
        <c:if test="${not empty message }">
            <div class="alert alert-danger">${message }</div>
        </c:if>
        
        
        <form method="post" id="loginForm">
            <div class="form-group">
                <label>賬號(hào)</label>
                <input type="text" name="username" id="username" class="form-control" value="${username }"/>
            </div>
            
            <div class="form-group">
                <label>賬號(hào)</label>
                <input type="password" name="password" class="form-control"/>
            </div>
            <div class="checkbox">
                <label>
                    <input type="checkbox" name="remeberme" value="remeberme" id="remeberme" />記住賬號(hào)
                </label>
            </div>
            <div>
                <button type="button" id="loginBtn" class="btn btn-success">登錄</button>
            </div>
        </form>
    
    </div>
    <script src="/static/js/jquery-1.11.3.min.js"></script>
    <script src="/static/js/jquery.cookie.js"></script>
    <script>
    
        $(function(){
            
            //$("#username").val($.cookie("username"));
            
            
            $("#loginBtn").click(function(){
                /* if($("#remeberme")[0].checked) {
                    $.cookie("username",$("#username").val(),{ expires: 7, path: '/' });
                } */
                $("#loginForm").submit();
            });
            
        });
    
    
    
    </script>
</body>
</html>

服務(wù)端記住賬號(hào)

package com.kaishengit.web;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;

import com.kaishengit.entity.Admin;
import com.kaishengit.exception.ServiceException;
import com.kaishengit.service.AdminService;

public class LoginServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = "";
        
        Cookie[] cookies = req.getCookies();
        if(cookies != null) {
            for(Cookie cookie : cookies) {
                if(cookie.getName().equals("username")) {
                    username = cookie.getValue();
                    break;
                }
            }
        }
        req.setAttribute("username", username);
        
        req.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(req, resp);
    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String callback = req.getParameter("callback");
        String remeberme = req.getParameter("remeberme");
        
        AdminService adminService = new AdminService();
        
        try {
            Admin admin = adminService.login(username, password);
            
            //判斷是否選中了[記住賬號(hào)]框
            if(StringUtils.isNotEmpty(remeberme)) {
                Cookie cookie = new Cookie("username",username);
                cookie.setDomain("localhost");
                cookie.setPath("/");
                cookie.setMaxAge(60 * 60 * 24 * 365 * 100);
                cookie.setHttpOnly(true);
                
                resp.addCookie(cookie);
            }
            
            
            
            //獲取HttpSession
            HttpSession session = req.getSession();
            session.setAttribute("admin", admin);
            if(StringUtils.isEmpty(callback)) {
                resp.sendRedirect("/list");
            } else {
                resp.sendRedirect(callback);
            }
            
            
            
        } catch (ServiceException e) {
            req.setAttribute("message", e.getMessage());
            req.setAttribute("username", username);
            
            req.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(req, resp);
        }
        
        
        
    }
    
    

}


Session原理
問題: 服務(wù)器能夠識(shí)別不同的瀏覽者!??!
前提: 在哪個(gè)session域?qū)ο蟊4鏀?shù)據(jù),就必須從哪個(gè)域?qū)ο笕〕觯。。。?/h6>
瀏覽器1:(給s1分配一個(gè)唯一的標(biāo)記:s001,把s001發(fā)送給瀏覽器)
                    1)創(chuàng)建session對(duì)象,保存會(huì)話數(shù)據(jù)
                            HttpSession session = request.getSession();   --保存會(huì)話數(shù)據(jù) s1
            瀏覽器1    的新窗口(帶著s001的標(biāo)記到服務(wù)器查詢,s001->s1,返回s1) 
                    1)得到session對(duì)象的會(huì)話數(shù)據(jù)
                            HttpSession session = request.getSession();   --可以取出  s1

            新的瀏覽器1:(沒有帶s001,不能返回s1)
                    1)得到session對(duì)象的會(huì)話數(shù)據(jù)
                            HttpSession session = request.getSession();   --不可以取出  s2

            瀏覽器2:(沒有帶s001,不能返回s1)
                    1)得到session對(duì)象的會(huì)話數(shù)據(jù)
                            HttpSession session = request.getSession();  --不可以取出  s3


代碼解讀:HttpSession session = request.getSession();
  1. 第一次訪問創(chuàng)建session對(duì)象,給session對(duì)象分配一個(gè)唯一的ID,叫JSESSIONID
    new HttpSession();
  1. 把JSESSIONID作為Cookie的值發(fā)送給瀏覽器保存
Cookie cookie = new Cookie("JSESSIONID", sessionID);
                    response.addCookie(cookie);
  1. 第二次訪問的時(shí)候,瀏覽器帶著JSESSIONID的cookie訪問服務(wù)器
  2. 服務(wù)器得到JSESSIONID,在服務(wù)器的內(nèi)存中搜索是否存放對(duì)應(yīng)編號(hào)的session對(duì)象。
if(找到){
                        return map.get(sessionID);
                    }
                    Map<String,HttpSession>]


                    <"s001", s1>
                    <"s001,"s2>
  1. 如果找到對(duì)應(yīng)編號(hào)的session對(duì)象,直接返回該對(duì)象
  2. 如果找不到對(duì)應(yīng)編號(hào)的session對(duì)象,創(chuàng)建新的session對(duì)象,繼續(xù)走1的流程

結(jié)論:通過JSESSION的cookie值在服務(wù)器找session對(duì)象?。。。?!


總結(jié):
        1)會(huì)話管理: 瀏覽器和服務(wù)器會(huì)話過程中的產(chǎn)生的會(huì)話數(shù)據(jù)的管理。

                2)Cookie技術(shù):
                        new Cookie("name","value")
                        response.addCookie(coookie)
                        request.getCookies()
                3)Session技術(shù)
                        request.getSession();
                        
                        setAttrbute("name","會(huì)話數(shù)據(jù)");
                        getAttribute("會(huì)話數(shù)據(jù)")

監(jiān)聽器

web.ml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- 登錄過濾器 -->
    <filter>
        <filter-name>ValidateFilter</filter-name>
        <filter-class>com.kaishengit.web.filter.ValidateServlet</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>ValidateFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


</web-app>
適配器模式:AbstractFilter
package com.kaishengit.web.filter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/**
 * @author Wgs
 * @version 1.0
 * @create:2018/05/26
 */
public class FilterServlet implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("FilterServlet.init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("FilterServlet.doFilter");
    }

    @Override
    public void destroy() {

    }
}

登陸攔截器
package com.kaishengit.web.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.kaishengit.entity.Admin;

public class ValidateFilter extends AbstractFilter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        
        //1. 獲取用戶訪問的資源地址
        String uri = request.getRequestURI();
        System.out.println(uri);
        
        if("/".equals(uri) || "/index.jsp".equals(uri) || "/login".equals(uri)
                || uri.startsWith("/static/")) {
            filterChain.doFilter(request, response);
        } else {
            
            HttpSession session = request.getSession();
            Admin admin = (Admin) session.getAttribute("admin");
            
            if(admin != null) {
                filterChain.doFilter(request, response);
            } else {
                response.sendRedirect("/login?callback="+uri);
            }
            
        }

    }

}

監(jiān)聽器

web.xml
    <!--監(jiān)聽器的配置-->
    <listener>
        <listener-class>com.kaishengit.web.listener.MyServletContextListener</listener-class>
    </listener>
    <listener>
        <listener-class>com.kaishengit.web.listener.MyHttSessionListener</listener-class>
    </listener>

    <context-param>
        <param-name>userName</param-name>
        <param-value>jack</param-value>
    </context-param>

HttpSessionListener
package com.kaishengit.web.listener;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class MyHttSessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        System.out.println("session create...");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println("session destory...");
    }
}


ServletContextListener
package com.kaishengit.web.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class MyServletContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContextListener init....");

        //通過ServletContextEvent對(duì)象來獲取ServletContext對(duì)象
        ServletContext servletContext = servletContextEvent.getServletContext();
        String userName = servletContext.getInitParameter("userName");
        System.out.println(userName);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContextListener destroy....");
    }
}

異步驗(yàn)證

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容