解讀 Servlet 源碼:GenericServlet,ServletConfig,ServletContext

来源:https://www.cnblogs.com/TheMagicalRainbowSea/archive/2023/03/20/17236669.html
-Advertisement-
Play Games

解讀 Servlet 源碼:GenericServlet,ServletConfig,ServletContext 每博一文案 人活著,就得隨時準備經受磨難。他已經看過一些書,知道不論是普通人還是了不起的人,都要在自己的一生中經歷許多磨難。磨難使人堅強。 人和社會、一切鬥爭的總結局也許都是中庸而已。 ...


解讀 Servlet 源碼:GenericServlet,ServletConfig,ServletContext

在這裡插入圖片描述

每博一文案

人活著,就得隨時準備經受磨難。他已經看過一些書,知道不論是普通人還是了不起的人,都要在自己的一生中經歷許多磨難。磨難使人堅強。
人和社會、一切鬥爭的總結局也許都是中庸而已。與其認真、不如隨便、採菊東籬下、悠然見南山。有錢就尋一醉、無錢就尋一睡、與過無爭、隨遇而安。
hellip;…人哪,活著是這麼的苦!一旦你從幸福的彼岸被拋到苦難的此岸,你真是處處走頭無路;而現在你才知道,在天堂與地獄之間原來也只有一步之遙!
倘若流落在他鄉異地,生活中的一切都將失去保障,得靠自己一個人去對付冷酷而嚴峻的現實了
青年,青年,無論受怎樣的挫折和打擊,都要咬著牙關挺住,因為你們完全有機會重建生活;只要不灰心喪氣,每一次挫折就只不過是通往新境界的一塊普通的絆腳石,而絕不會置人於死命。
你永遠要寬恕眾生,不論他有多壞,甚至他傷害過你,你一定要放下,才能得到真正的快樂。
也許人生僅有那麼一兩個輝煌的瞬間————甚至一生都可能在平淡無奇中度過......不過,每個人的生活同樣也是一個世界,即是最平凡的人,也得要為他那個世界的存在而戰鬥。這個意義上來說,在這些平凡的世界里,也沒有一天是平靜的。因此,大多數
普通人不會像飄飄欲仙的老莊,時常把自己看作是一粒塵埃————儘管地球在浩渺的宇宙中也只不過是一粒塵埃罷了。
                                       ——————《平凡的世界》

@

目錄

1. Servlet對象的生命周期

什麼是Servlet 對象的生命周期 ?

Servlet 對象的生命周期表示:一個Servlet對象從出生在最後的死亡,整個過程是怎樣的 。

Servlet對象是由誰來維護的?

  • Servlet 對象的創建,Servlet對象上方法的調用,以及Servlet 對象的銷毀,JavaWeb程式員都無權干預的。
  • Servlet 對象的生命周期是由 Tomcat 伺服器(web Server)全權負責的
  • Tomcat 伺服器通常又被我們稱之為 WEB容器 (WEB Container) 。WEB 容器來管理 Servlet對象的死活

我們自己new的Servlet對象受WEB容器的管理嗎?

我們自己 new 的 Servlet 對象是不受 WEB容器 管理的。

因為: WEB容器 創建的 Servelt 對象,這些Servlet 對象都會被放到一個集合當中(HashMap) ,只有放到這個 HashMap 集合中的 Servlet 才能夠被 WEB容器管理,自己 new 的 Servlet 對象不會被 WEB容器管理,因為我們自己 new 的Servlet對象並不沒有存放到 WEB 容器當中。

WEB容器底層應該有一個 HashMap這樣的集合,在這個集合當中存儲了Servlet對象和請求路徑之間的關係。
在這裡插入圖片描述

1.1 Servlet 五 個方法的調用周期

Servlet 必須重寫的五方法分別為:init(),service(ServletRequest request, ServletResponse response),getServletConfig(),getServletInfo(),destroy(),還有一個無參構造器 什麼時候創建的,什麼時候調用的,什麼時候銷毀的。

package com.RainbowSea.servlet;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class AServlet implements Servlet {
    @Override
    public void init(ServletConfig config) throws ServletException {
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {

    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}




執行如下代碼,啟動 Tomcat 伺服器:並訪問該AServlet ,通過在瀏覽器其當中輸入: http://127.0.0.1:8080/blog01/A

package com.RainbowSea.servlet;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class AServlet implements Servlet {

    public AServlet(){
        System.out.println("AServlet 的無參構造器的調用");
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("AServlet 中的 init 的調用執行");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        System.out.println("AServlet 中的 service 的方法的調用執行");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("AServlet 中的 destroy 方法的執行調用");
    }
}

<?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_4_0.xsd"
         version="4.0">


    <servlet>
<!--        兩個 name 值要保持一致-->
        <servlet-name>AServlet</servlet-name>
        <servlet-class>com.RainbowSea.servlet.AServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AServlet</servlet-name>
<!--        註意: / 開始-->
        <url-pattern>/A</url-pattern>
    </servlet-mapping>
</web-app>

執行結果如下:

在這裡插入圖片描述

重點:

  • 當我們在預設普通配置的情況下,啟動Tomcat 伺服器的時候,我們編寫的 AServlet 對象並沒有被實例化(因為並沒有調用我們 AServlet 構造器創建對象)。
  • 當用戶訪問該資源,第一次向我們的 AServlet 發送請求的時候。我們的 Servlet對象就被實例化了(這裡是 AServlet 的構造器被執行了,並且執行的是該 AServlet 無參構造器。 )
  • 之後,當 Servlet 對象被創建出來了,也就是這裡的 AServlet 對象,Tomcat 伺服器馬上就調用了 AServlet 對象當中的 init()對象方法 ,(註意:init()方法在執行的時候,我們的 Servlet(這裡的 AServlet)對象就已經存在了,已經被創建出來了,因為 init() 是對象方法,需要通過創建對象才能被調用(特殊的 :反射除外))。
  • 用戶發送第一次請求的時候,init()方法執行完,Tomcat 伺服器馬上調用了 Servlet (這裡的AServlet 對象)當中的 servelt() 方法。
  • 註意:當用戶繼續發送第二次請求(刷新該,頁面的資源,重新發送新的請求)。或者第三次,或者第四次請求的時候,Servlet對象並沒有新建,還是使用之前創建好的Servlet對象,直接調用該Servlet對象的service方法結果如下:

在這裡插入圖片描述

  • 根據上述結果:我們可以知道如下信息:
    • Servlet 對象使單例的 (單實例的。但是要註意:Servlet 對象使單實例的,但是 Servlet 類並不符合單例模式 的設計。因為真正的單例模式的構造器private私有的 ,所以我們稱之為 假單例 ,之所以單例是因為 Servelt 對象的創建,我們 javaWeb程式員是管不著的,這個對象的創建只能是 Tomcat 伺服器或其他伺服器來說的算的。Tomcat 只會創建一個,所以導致了單例,但是屬於假單例。)
    • 無參構造器,只會執行一次,該無參構造器是通過反射機制調用的(通過從 web.xml 中的配置信息獲取到其中對應類的:全限定類名 創建對象),
    • init() 方法只在第一次用戶發送請求的時候執行,init對象方法也只被 Tomcat 伺服器調用一次。
    • 只要用戶發送一次請求:service() 方法必然會被 Tomcat 伺服器調用一次,發送 100 次請求,servlect() 方法就會被調用 100次。
  • 我們關閉 Tomcat 伺服器,控制臺上顯示如下結果

在這裡插入圖片描述

從上述控制臺上顯示的結果,我們可以知道:當我們關閉伺服器的時候 destroy()方法被調用了

  • destroy()方法是在什麼時候被調用的 ?
  • 在伺服器關閉的時候。

  • 因為伺服器關閉的時候:要銷毀 AServlet 對象的記憶體信息。

  • 所以伺服器在銷毀 AServlet 對象記憶體之前,Tomcat 伺服器會自動調用 AServlet 對象中的 destroy() 方法。

問題:destroy() 方法調用的時候,Servlet 對象銷毀了還是沒有銷毀 ???

答:destroy() 方法執行的時候,AServlet 對象還在,沒有被銷毀,因為 destroy() 方法是對象方法。調用該方法的話需要通過對象才能調用的(反射除外),destroy()方法執行結束之後,AServlet 對象的記憶體才會被 Tomcat 釋放。

註意點: 對應 Servlet 類我們不要編寫構造器。

當我們Servlet 類中編寫了一個有參數的構造器,那麼如果我們沒有手動再編寫一個無參構造器的話,無參構造器就會消失。

如果一個 Servlet 無參構造器消失的會,如何:結果如下:

在這裡插入圖片描述

報錯:500 錯誤。500 是一個 HTTP 協議的錯誤狀態碼。 500 一般情況下是因為伺服器端的 java 程式出現了異常 。(伺服器端的錯誤基本上都是 500 錯誤,伺服器內部錯誤)

  • 如果一個 Servlet 類沒有無參構造器的話,會導致 500 錯誤,反射機制無法通過 無參構造器創建對象 。導致 無法實例化 Servlet 對象。
  • 所以,一定要註意:在 Servlet 開發當中,不建議 程式員來定義構造器,因為定義不當,一不小心就會導致無法實例化 Servlet對象了。

思考:Servlet 的無參數構造器是在對象第一次創建的時候執行的,並且只會執行一次。而 init()方法也是在對象第一次拆創建的時候執行的,並且也只會執行一次。那麼這個無參構造器可以代替 init()方法嗎 ?

答:不能。

Servlet 規範中有要求,作為 javaWeb 程式員,編寫 Servlet 類的時候,不建議手動編寫構造器,因為編寫構造器,很容易讓無參數構造器消失。這個操作可能會導致 Servlet 對象無法實例化,所以 init() 方法是有存在的必要的。

1.2 Servlet 常用的三個方法使用總結

package com.RainbowSea.servlet;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class AServlet implements Servlet {
    // init 被翻譯為初始化
    // init 方法只會被執行一次,基本上和 Servlet構造器的調用同時執行,在Servlet 對象第一次被創建只會執行
    // init 方法通常是完成初始化操作的。
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("AServlet is init method execute!");
    }


    // service 方法:是處理用戶請求的核心方法
    // 只要用戶發送一次請求,service 方法必然會執行一次
    // 發送100次請求,service方法執行100次
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        System.out.println("AServlet is service method execute");
    }

    // destroy ()方法也是只執行一次
    // Tomcat 伺服器在銷毀AServlet 對象之前會調用一次destroy 方法
    // destroy()方法在執行的時候,AServlet 對象的記憶體還沒有被銷毀,即將被銷毀,因為destroy 是對象方法,需要通過對象調用
    // destroy 方法中可以編寫銷毀前的準備
    // 比如:伺服器關閉的時候,AServelt 對象開啟了一些資源,這些資源可能是流,也可能是資料庫連接
    // 那麼關閉伺服器的時候,要關閉這些流,關閉這些資料庫連接,那麼這些關閉資源的代碼舊可以寫到destroy()
    @Override
    public void destroy() {
        System.out.println("AServlet is destroy method execute");
    }
}

關於Servlet類中方法的說明:

  • 無參構造器:只會執行調用一次。
  • init() 方法:init 被翻譯為初始化,init ()方法只會被執行一次;基本上和 Servlet構造器的調用同時執行;在Servlet 對象第一次被創建只會執行。init 方法通常是完成初始化操作的。
  • service() 方法:service() 方法是處理用戶請求的核心方法。只要用戶發送一次請求,service 方法必然會執行一次;發送100次請求,service方法執行100次。
  • destroy() 方法:destroy 方法中可以編寫銷毀前的準備;比如:伺服器關閉的時候,AServelt 對象開啟了一些資源,這些資源可能是流,也可能是資料庫連接;那麼關閉伺服器的時候,要關閉這些流,關閉這些資料庫連接,那麼這些關閉資源的代碼舊可以寫到destroy()。

init、service、destroy方法中使用最多的是哪個方法?

  • 使用最多就是service()方法,service方法是一定要實現的,因為service方法是處理用戶請求的核心方法。

  • 什麼時候使用init方法呢?

    init方法很少用;通常在init方法當中做初始化操作,並且這個初始化操作只需要執行一次。例如:初始化資料庫連接池,初始化線程池....

  • 什麼時候使用destroy方法呢?

    destroy方法也很少用;通常在destroy方法當中,進行資源的關閉。馬上對象要被銷毀了,還有什麼沒有關閉的,抓緊時間關閉資源。還有什麼資源沒保存的,抓緊時間保存一下。

Servlet對象更像一個人的一生:

  • Servlet的無參數構造方法執行:標志著你出生了。
  • Servlet對象的init方法的執行:標志著你正在接受教育。
  • Servlet對象的service方法的執行:標志著你已經開始工作了,已經開始為人類提供服務了。
  • Servlet對象的destroy方法的執行:標志著臨終。有什麼遺言,抓緊的。要不然,來不及了。

1.3 補充:讓啟動Tomcat 伺服器的時候會調用構造器

從上述內容我們知道了,當 Tomcat 伺服器啟動的時候,我們的構造器實際上還沒有調用,也就是對象還沒有被實例化創建出來。但是我們想在啟動 Tomcat 伺服器的時候就調用構造器。可以使用如下方式:

在我們想讓Tomcat 伺服器啟動的時候調用哪個類的構造器,就在該類當中的 web.xml 加上如下:標簽 <load-on-startup>(填寫數值)</load-on-startup> : 的作用就是啟動伺服器的時候就會創建該對象:數值越小的越先被創建

<!--<load-on-startup> 的作用就是啟動伺服器的時候就會創建該對象:數值越小的越先被創建-->
<load-on-startup>2</load-on-startup>

舉例:

<?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_4_0.xsd"
         version="4.0">


    <servlet>
        <!--        兩個 name 值要保持一致-->
        <servlet-name>AServlet</servlet-name>
        <servlet-class>com.RainbowSea.servlet.AServlet</servlet-class>
        <!--        的作用就是啟動伺服器的時候就會創建該對象:數值越小的越先被創建-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>AServlet</servlet-name>
        <!--        註意: / 開始-->
        <url-pattern>/A</url-pattern>
    </servlet-mapping>


    <servlet>
        <!--        兩個 name 保持一致-->
        <servlet-name>BServlet</servlet-name>
        <servlet-class>com.RainbowSea.servlet.BServlet</servlet-class>
        <!--        的作用就是啟動伺服器的時候就會創建該對象:數值越小的越先被創建-->
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>BServlet</servlet-name>
        <!--        註意 / 開始-->
        <url-pattern>/B</url-pattern>
    </servlet-mapping>
</web-app>

在這裡插入圖片描述

在這裡插入圖片描述

2. GenericServlet

2.1 適配器

我們編寫一個Servlet 類直接實現了 這個 Servlet 介面 存在什麼缺點沒有:?
有的:我們編寫的 Servlet 類實現了該 Servlet 介面中所有的方法。但是我們其實只需要其中的 service()方法 ,其他方法大部分情況下是不需要使用的。簡單粗暴的實現了我們所有的方法,但是其中大部分的方法我們是不需要的,這樣就顯的我們的代碼很醜陋了:

在這裡插入圖片描述

所以:我們不想要重寫所以的方法,而是重寫我們需要用的方法。這樣該怎麼做呢?

我們可以使用 23種設計模式中的一種:適配器模式(Adapter)

適配器模式的理解:舉例

比如:如果我們的手機充電時直接插入到 220V 的電壓上,手機會直接報廢的,因為手機無法接受如此高的電壓,怎麼辦呢?
我們可以該手機配備一個電源適配器——充電器,手機連接充電器(適配器),適配器連接 220V的電壓,這樣就問題解決了。同理的還有很多:電腦的充當器。充電熱水壺等等。

編寫一個 GenericServlet (abstract )抽象類適配器。
這個適配器是一個abstract抽象類。:我們該類實現了 Servlet 介面中所有的方法,但是其中我們常用的方法 service() 定義為是抽象方法,因為如果不想實現的方法,可以定義將該方法定義為抽象方法就不用重寫了,而是交給繼承的子類重寫就好了。因為重寫方法只能存在於抽象類,介面當中,所以我們這個適配器就為了一個抽象類。這樣我們以後編寫的所以 Servlet 類就只需要繼承 GenericServlet 抽象類,並且只要重寫其中的 service() 抽象方法即可。但是我們又可以使用 Servlet 介面中的方法,因為我們的父類GenericServlet 抽象類 實現了 Servlet 介面中的所有方法。如有必要,我們編寫的Servlet 類也可以重寫GenericServlet 抽象類中的方法,該重寫的方法也是重寫Servlet 介面中的方法。

具體編寫如下:

abstract class GenericServlet 抽象類適配器

package com.RainbowSea.servlet;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;


/**
 * 編寫一個標準通用的Servlet
 * 以後所有的Servlet 類都不要直接實現Servlet 介面了。
 * 以後所有的Servlet 類都繼承 GenericServlet 類。
 * GenericServlet 就是一個適配器
 */
public abstract class GenericServlet implements Servlet {
    @Override
    public void init(ServletConfig config) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * 抽象方法:這個方法最常用,所以要求子類必須實現Service 方法。
     */
    @Override
    public abstract void service(ServletRequest request, ServletResponse response) throws ServletException, IOException;

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

以後編寫的Servlet 都繼承該 GenericServlet 抽象類適配器

package com.RainbowSea.servlet;


import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class AServlet extends GenericServlet {

    /**
     * 只需要重寫我們常用的 service 父類中的抽象方法即可
     */
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        System.out.println("AServlet 處理請求中....");
    }
}

package com.RainbowSea.servlet;


import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class BServlet extends GenericServlet {

    /**
     * 只需要重寫我們常用的 service 父類中的抽象方法即可
     */
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        System.out.println("BServlet 處理請求中....");
    }
}



在這裡插入圖片描述

問題:提供了 GenericServlet 類,init()方法還好執行嗎?

答:還會執行,執行 GenericServlet 中被重寫的 init()方法。

問題:init()方法是誰調用的 ?

Tomcat 伺服器調用的。

問題:init()方法中的 ServletConfig 對象是誰創建的 ? 有是誰傳過來的?

是由 Tomcat伺服器 創建的,也是由 Tomcat 伺服器傳過來的。都是Tomcat 伺服器乾的。

Tomcat 伺服器先創建了 ServleConfig 對象,然後調用 init()方法,將ServleConfig 對象傳給了 init()方法

Tomcat 執行init()方法的偽代碼

@Override
    public void init(ServletConfig config) throws ServletException {

    }
public class Tomcat {
    public static void main(String[] args){
        // .....
        // Tomcat伺服器偽代碼
        // 創建LoginServlet對象(通過反射機制,調用無參數構造方法來實例化LoginServlet對象)
        Class clazz = Class.forName("com.bjpowernode.javaweb.servlet.LoginServlet");
        Object obj = clazz.newInstance();
        
        // 向下轉型
        Servlet servlet = (Servlet)obj;
        
        // 創建ServletConfig對象
        // Tomcat伺服器負責將ServletConfig對象實例化出來。
        // 多態(Tomcat伺服器完全實現了Servlet規範)
        ServletConfig servletConfig = new org.apache.catalina.core.StandardWrapperFacade();
        
        // 調用Servlet的init方法
        servlet.init(servletConfig);
        
        // 調用Servlet的service方法
        // ....
        
    }
}

2.2 模板方法設計模式

思考: init 方法中的ServileConfig 對象是小貓咪創建好的,這個ServletConfig對象目前在init 方法的參數上,屬於局部變數。那麼ServletConfig 對象肯定以後要在Service 方法中使用,怎麼才能保證ServletConfig 對象在Service方法中能夠使用呢?

答: 可以定義一個類的屬性,再通過 init 方法的,將該ServleConfig 賦值上去

具體代碼如下:

package com.RainbowSea.servlet;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;


/**
 * 編寫一個標準通用的Servlet
 * 以後所有的Servlet 類都不要直接實現Servlet 介面了。
 * 以後所有的Servlet 類都繼承 GenericServlet 類。
 * GenericServlet 就是一個適配器
 */
public abstract class GenericServlet implements Servlet {
    private ServletConfig config ;

    // 這樣我們在我們的子類當中的 service()方法當中也可以使用 Tomcat 伺服器創建的
    // ServletConfig 對象了
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
    }

    // 獲取該 config 對象值
    public ServletConfig getConfig() {
        return config;
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * 抽象方法:這個方法最常用,所以要求子類必須實現Service 方法。
     */
    @Override
    public abstract void service(ServletRequest request, ServletResponse response) throws ServletException, IOException;

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

舉例:Servale 類在 service ()方法當中獲取到該 ServletConfig 對象值,並使用

package com.RainbowSea.servlet;


import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class AServlet extends GenericServlet {

    /**
     * 只需要重寫我們常用的 service 父類中的抽象方法即可
     */
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // 設置 response 在瀏覽器頁面當中顯示的格式類型
        // 註意需要在顯示之前,設置
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.print("AServlet 處理請求中...." + "<br>");
        ServletConfig config = super.getConfig();
        out.print("AServlet 當中 service 獲取到 Tomcat 伺服器創建的ServletConfig對象: " + config);
        
    }
}

在這裡插入圖片描述

重點: 我們通過上述定義了一個 private ServletConfig config 成員屬性,可以獲取到Tomcat 創建的 ServletConfig 對象了,併在 service() 方法當中使用。但是存在一個問題:如果我們的子類要是重寫了,我們父類適配器 GenericServlet 類當中的 init()方法的話。因為多態性的緣故:在實際的運行過程中,調用的會是我們子類重寫的的 init() 方法,這樣導致的結果就,我們父類中的 private ServletConfig config 成員屬性的值無法賦值上為 null 了。因為我們父類當中的 config 成員屬性是通過父類當中的 init()方法賦值的,現在我們的子類重寫了父類的 init()方法。不會調用我們父類的 init()進行賦值操作了,從而導致為了 null 值。

解決方式:將我們父類當中的 init()方法被 final 修飾,這樣我們的子類就無法重寫 init()方法了。

public abstract class GenericServlet implements Servlet {
    private ServletConfig config ;

    // 這樣我們在我們的子類當中的 service()方法當中也可以使用 Tomcat 伺服器創建的
    // ServletConfig 對象了
    @Override
    public final void init(ServletConfig config) throws ServletException {
        this.config = config;
    }
}

上述的解決方式仍然存在一個問題:就是如果我們的子類一定要重寫 init()方法該怎麼辦,現在你父類當中的 init()方法被 final 修飾了無法,被 子類重寫。

解決方式:
我們使用 23中設計模式當中的 模板方法設計模式:

因為子類想要重寫我們父類當中的 init()方法,那麼我們就在父類當中,再創建一個 無參數的init()方法(方法的重載),這個方法讓子類去重寫,但是我們父類當中還有一個 帶參數的 init(ServletConfig config) 方法,在該帶有參數的init()方法當中調用我們這個提供該子類重寫的 init()方法。具體代碼如下:

在這裡插入圖片描述

package com.RainbowSea.servlet;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;


/**
 * 編寫一個標準通用的Servlet
 * 以後所有的Servlet 類都不要直接實現Servlet 介面了。
 * 以後所有的Servlet 類都繼承 GenericServlet 類。
 * GenericServlet 就是一個適配器
 */
public abstract class GenericServlet implements Servlet {
    private ServletConfig config ;

    // 這樣我們在我們的子類當中的 service()方法當中也可以使用 Tomcat 伺服器創建的
    // ServletConfig 對象了,final 修飾的方法無法重寫
    @Override
    public final void init(ServletConfig config) throws ServletException {
        // 賦值依舊存在,config 不會為 null
        this.config = config;
        // 調用子類重寫後的 init()方法
        this.init();
    }
    
    // 用於提供給子類重寫
    public void init() {
        
    }

    public ServletConfig getConfig() {
        return config;
    }
}

舉例:在AServlet 類當中重寫 init()方法,並且執行該重寫的方法:

package com.RainbowSea.servlet;


import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class AServlet extends GenericServlet {

    /**
     * 只需要重寫我們常用的 service 父類中的抽象方法即可
     */
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // 設置 response 在瀏覽器頁面當中顯示的格式類型
        // 註意需要在顯示之前,設置
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.print("AServlet 處理請求中...." + "<br>");
        ServletConfig config = super.getConfig();
        out.print("AServlet 當中 service 獲取到 Tomcat 伺服器創建的ServletConfig對象: " + config);

    }

    // 重寫的父類當中的 init()無參方法
    @Override
    public void init() {
        System.out.println("AServlet 重寫的 init()方法執行了");
    }
}

在這裡插入圖片描述

2.3 補充:GenericServlet 不用我們自己編寫

註意:GenericServlet 抽象類適配器是不需要我們自己編寫的,Servlet 已經編寫好了,我們只需要使用就可以了。

上述是為了理解 GenericServlet 源碼,從而自己編寫的 GenericServlet 適配器的。如下是 Servlet 為我們編寫好的 GenericServlet 源碼,和我們上面自己編寫的設計結構是一樣的。

在這裡插入圖片描述

package javax.servlet;

import java.io.IOException;
import java.util.Enumeration;

public abstract class GenericServlet implements Servlet, ServletConfig,
        java.io.Serializable {

    private static final long serialVersionUID = 1L;

    private transient ServletConfig config;


    public GenericServlet() {
        // NOOP
    }


    @Override
    public void destroy() {
        // NOOP by default
    }

    @Override
    public String getInitParameter(String name) {
        return getServletConfig().getInitParameter(name);
    }

   
    @Override
    public Enumeration<String> getInitParameterNames() {
        return getServletConfig().getInitParameterNames();
    }

    @Override
    public ServletConfig getServletConfig() {
        return config;
    }

    
    @Override
    public ServletContext getServletContext() {
        return getServletConfig().getServletContext();
    }

   
    @Override
    public String getServletInfo() {
        return "";
    }

    
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    
    public void init() throws ServletException {
        // NOOP by default
    }

    
    public void log(String message) {
        getServletContext().log(getServletName() + ": " + message);
    }

    
    public void log(String message, Throwable t) {
        getServletContext().log(getServletName() + ": " + message, t);
    }

    
    @Override
    public abstract void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;

    
    @Override
    public String getServletName() {
        return config.getServletName();
    }
}

3. ServletConfig

從上面的知識我們知道了 : Tomcat 伺服器先創建了 ServletConfig 對象,然後調用init()方法,將ServletConfig對象傳給了init()方法。

先來一波,自問自答:

ServletConfig 是什麼?

package javax.servlet; 顯然 ServletConfig 是一個介面。

在這裡插入圖片描述

誰去實現了這個介面呢 ?

在這裡插入圖片描述

org.apache.catalina.core.StandardWrapperFacade 這個類實現了這個 ServletConfig 介面

StandardWrapperFacade 源碼如下:

package org.apache.catalina.core;


import java.util.Enumeration;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;


/**
 * Facade for the <b>StandardWrapper</b> object.
 *
 * @author Remy Maucherat
 */
public final class StandardWrapperFacade
    implements ServletConfig {


    // ----------------------------------------------------------- Constructors


    /**
     * Create a new facade around a StandardWrapper.
     * @param config the associated wrapper
     */
    public StandardWrapperFacade(StandardWrapper config) {

        super();
        this.config = config;

    }


    // ----------------------------------------------------- Instance Variables


    /**
     * Wrapped config.
     */
    private final ServletConfig config;


    /**
     * Wrapped context (facade).
     */
    private ServletContext context = null;


    // -------------------------------------------------- ServletConfig Methods


    @Override
    public String getServletName() {
        return config.getServletName();
    }


    @Override
    public ServletContext getServletContext() {
        if (context == null) {
            context = config.getServletContext();
            if (context instanceof ApplicationContext) {
                context = ((ApplicationContext) context).getFacade();
            }
        }
        return context;
    }


    @Override
    public String getInitParameter(String name) {
        return config.getInitParameter(name);
    }


    @Override
    public Enumeration<String> getInitParameterNames() {
        return config.getInitParameterNames();
    }


}

Tomcat 伺服器實現了ServletConfig 介面。

思考: 如果 Tomcat 伺服器換成 了其他的伺服器,比如換成是 Jetty 伺服器,輸出 ServletConfig 對象的時候,還是這個結果嗎?

不一定是一樣的結果了,包含的類名可能和Tomcat 不一樣了,但是他們都實現了 ServletConfig 這個規範。

問題:ServletConfig對象是誰創建的,在什麼時候創建的?

Tomcat 伺服器(WEB伺服器) 創建了ServletConfig 對象,在創建Servlet 對象的時候,同時創建ServletConfig 對象.

問題:ServletConfig 介面到底是乾什麼的? 有什麼用?

Config 是 Configuration單詞的縮寫:n. 佈局,構造;配置

  •  ServletConfig 對象翻譯為: Servlet 對象的配置信息對象。
    
  •  一個Servlet對象就有一個配置信息對象:`web.xml <servlet></servlet>`
    
  •  兩個Servlet 對象就有兩個配置信息對象。
    

ServletConfig對象中創建包裝了 web.xml <servlet></servlet> 標簽配置信息: Tomcat 小喵咪解析web.xml 文件,將web.xml文件中<servlet><init-param></init-param></servlet>標簽中的配置信息自動包裝到ServletConfig 對象當中

如下:註意:一個Servlet 對象就會有一個 <servlet></servlet> 配置信息標簽 。如下是 AServlet對象的 xml 配置信息。

<--以上是 <servlet-name>ConfigTestServlet</servlet-name> <init-param></init-param>
<--是初始化參數,這個初始化參數信息被小喵咪封裝到 ServletConfig 對象當中 -->
<servlet>
        <servlet-name>AServlet</servlet-name>
        <servlet-class>com.RainbowSea.servlet.AServlet</servlet-class>
        <!-- 這裡是可以配置一個Servlet對象的初始化信息的 註意:這裡的配置對應的是上述<servlet-name>
        為AServlet-->
        <init-param>
            <param-name>Driver</param-name>
            <param-value>com.mysql.cj.jdbc.Driver</param-value>
        </init-param>
    
        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost:3306/dbtest9</param-value>
        </init-param>
    
        <init-param>
            <param-name>user</param-name>
            <param-value>root</param-value>
        </init-param>
</servlet>

3.1 ServletConfig 當中常用的四個方法

public String getServletName();  // 獲取該Servlet對象的名稱
public String getInitParameter(String name); // 通過 <param-name>user</param-name> 標簽當中的name 值獲取到對應  <param-value>root</param-value> 標簽當中的 value 值
public Enumeration<String> getInitParameterNames();  // 一次性獲取到該Servlet對象<init-param>標簽當中配置信息的 name 值
public ServletContext getServletContext(); // 獲取到ServletContext對象
//  以上的4個方法: 在自己的編寫的Servlet表當中也可以使用this,/super去調用(這個Servlet繼承了GenericServet)

3.1.2 通過ServletConfig 對象獲取到 web.xml 配置文件 標簽當中的配置信息

舉例:獲取到 web.xml 配置文件當中 :<servlet><init-param></init-param></servlet>標簽中的配置信息。因為該配置信息是會被動包裝到ServletConfig 對象當中 。這裡我們分別獲取到 AServlet 對象和 BServlet 對象當中的配置信息。

對應web.xml 信息如下

<?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_4_0.xsd"
         version="4.0">


    <servlet>
        <servlet-name>AServlet</servlet-name>
        <servlet-class>com.RainbowSea.servlet.AServlet</servlet-class>
        <!-- 這裡是可以配置一個Servlet對象的初始化信息的 註意:這裡的配置對應的是上述<servlet-name>
        為AServlet-->
        <init-param>
            <param-name>Driver</param-name>
            <param-value>com.mysql.cj.jdbc.Driver</param-value>
        </init-param>
        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost:3306/dbtest9</param-value>
        </init-param>
        <init-param>
            <param-name>user</param-name>
            <param-value>root</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>AServlet</servlet-name>
        <url-pattern>/A</url-pattern>
    </servlet-mapping>


    <servlet>
        <servlet-name>BServlet</servlet-name>
        <servlet-class>com.RainbowSea.servlet.BServlet</servlet-class>
        <init-param>
            <param-name>password</param-name>
            <param-value>root123</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>BServlet</servlet-name>
        <url-pattern>/B</url-pattern>
    </servlet-mapping>
</web-app>

獲取BServlet 對象當中的 web.xml 標簽 的配置信息

package com.RainbowSea.servlet;


import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

public class BServlet extends GenericServlet {

    /**
     * 只需要重寫我們常用的 service 父類中的抽象方法即可
     */
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // 設置在瀏覽器頁面顯示的格式類型,必須在輸出前設置
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();

        // 獲取到 GenericServlet 父類適配器當中的  ServletConfig  config成員屬性
        ServletConfig servletConfig = super.getServletConfig();
        // 獲取該Servlet對象的名稱
        String ServletName = servletConfig.getServletName();
        writer.print(ServletName + "<br>");  // 輸出 Servlet 的對象的名稱

        String name = "password"; // 這裡如果我們直接知道name 為 pawword 
        
        // 通過 <param-name>user</param-name> 標簽當中的name 值獲取到對應  <param-value>root</param-value> 標簽當中的 value 值
        String value = servletConfig.getInitParameter(name);  
        writer.print(name + "--->" + value);  // 頁面輸出
    }
}



在這裡插入圖片描述

獲取AServlet 對象當中的 web.xml 標簽 的配置信息

package com.RainbowSea.servlet;


import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

public class AServlet extends GenericServlet {

    /**
     * 只需要重寫我們常用的 service 父類中的抽象方法即可
     */
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // 設置在瀏覽器頁面顯示的格式類型,必須在輸出前設置
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();

        // 獲取到 GenericServlet 父類適配器當中的  ServletConfig  config成員屬性
        ServletConfig servletConfig = super.getServletConfig();
        // 獲取該Servlet對象的名稱
        String ServletName = servletConfig.getServletName();
        writer.print(ServletName + "<br>");  // 輸出 Servlet 的對象的名稱

        // 一次性獲取到該Servlet對象<init-param>標簽當中配置信息的 name 值
        Enumeration<String> names = servletConfig.getInitParameterNames();

        while(names.hasMoreElements()) {  // 判斷是否還有 元素,有返回 true,沒有返回false。和集合當中的迭代器類似
            String name = names.nextElement(); // 取出name 值

            // 通過 <param-name>user</param-name> 標簽當中的name 值獲取到對應  <param-value>root</param-value> 標簽當中的 value 值
            String value = servletConfig.getInitParameter(name);
            writer.print(name + "--->" + value);  // 頁面輸出
            writer.print("<br>");  // 頁面換行

        }

    }

}

在這裡插入圖片描述

3.1.3 獲取到 ServletContext對象

方式一: 通過先獲取到 ServletConfig 對象,再通過 ServletConfig 對象獲取到 ServletContext 對象

使用 ServletConfig 當中的 public ServletContext getServletContext(); // 獲取到ServletContext對象

package com.RainbowSea.servlet;


import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class BServlet extends GenericServlet {

    /**
     * 只需要重寫我們常用的 service 父類中的抽象方法即可
     */
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // 獲取到 GenericServlet父類適配器當中的 private transient ServletConfig config; 成員屬性
        ServletConfig servletConfig = super.getServletConfig();
        // 獲取到 servletContext 對象
        ServletContext servletContext = servletConfig.getServletContext();
        System.out.println("servletContext的值: " + servletContext);

    }
}



在這裡插入圖片描述

方式二: 直接通過 父類 GenericServlet 適配器當中的。this,super.getServletContext() 方法 也可以獲取到ServletContext對象

package com.RainbowSea.servlet;


import javax.servlet.GenericServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class BServlet extends GenericServlet {

    /**
     * 只需要重寫我們常用的 service 父類中的抽象方法即可
     */
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {

        // 獲取到GenericServlet父類適配器當中的 servletContext 對象
        ServletContext servletContext = super.getServletContext();
        System.out.println("servletContext的值: " + servletContext);

    }
}



在這裡插入圖片描述

4. ServletContext

在這裡插入圖片描述

  • ServletContext 是介面,是Servlet規範中的一員。Tomcat伺服器(WEB伺服器) 實現了ServletContext 介面。
  • ServletContext 是一個介面,Tomcat 伺服器對 ServletContext 介面進行了實現。ServletContext 對象的創建也是 Tomcat 伺服器來完成的。啟動webapp的時候創建的。
  • ServletContext 對象在伺服器啟動階段創建的,在伺服器關閉的時候銷毀。這就是 ServletContext 對象的生命周期。
  • ServletContext 對象的環境對象,(Servlet對象的上下文對象)。

重點:

一個ServletContext 表示的就是一個 webapp 當中的 web.xml 文件當中下配置信息。而一個 webapp 一般只有一個 web.xml 文件。所以只要在同一個 webapp 當中,只要在同一個應用當中,所以的Servlet 對象都是共用同一個 ServletContext對象的 。舉例:Tomcat 伺服器中有一個 webapps ,這個 webapps 下存放了多個 webapp 。假設有 100 個 webapp ,那麼就會有 100 個 ServeltContext 對象,因為一個 webapp 對應一個 web.xml 文件。總之,一個應用,一個 webapp 只有一個 web.xml文件,則只有一個 ServletContext 對象,所有該應用下/webapp下的 Servlet 對象共用。

如下:我們一個 webapp 下有兩個 Servlet 對象,分別為 BServlet 和 AServlet 對象,但是他們兩個的同時在同一個 webapp下的,只有一個 web.xml 文件。所以這個兩個 Servlet 對象共用 一個 ServletContext 對象:

package com.RainbowSea.servlet;


import javax.servlet.GenericServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class BServlet extends GenericServlet {

    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // 設置在瀏覽器頁面顯示的格式類型,必須在輸出前設置
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();

        // 獲取到GenericServlet父類適配器當中的 servletContext 對象
        ServletContext servletContext = super.getServletContext();
        writer.print("BServlet 下的 servletContext的值: " + servletContext);

    }
}



package com.RainbowSea.servlet;


import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

public class AServlet extends GenericServlet {

    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // 設置在瀏覽器頁面顯示的格式類型,必須在輸出前設置
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();

        // 獲取到GenericServlet父類適配器當中的 servletContext 對象
        ServletContext servletContext = super.getServletContext();
        writer.print("AServlet 下的 servletContext的值: " + servletContext);

    }

}

在這裡插入圖片描述

ServletContext對應顯示生活中的什麼例子呢?

一個教室里有多個學生,那麼每一個學生就是一個Servlet,這些學生都在同一個教室當中,那麼我們可以把這個教室叫做ServletContext對象。那麼也就是說放在這個ServletContext對象(環境)當中的數據,在同一個教室當中,物品都是共用的。比如:教室中有一個空調,所有的學生都可以操作。可見,空調是共用的。因為空調放在教室當中。教室就是ServletContext對象。

4.1 ServletContext 獲取web.xml 配置文件當中的 <context-param>標簽當中的配置信息

我們想要獲取到web.xml 配置文件大當中的<context-param>標簽當中編寫的的配置信息,需要使用到如下兩個方法:

public String getInitParameter(String name); // 通過初始化參數的name獲取value
public Enumeration<String> getInitParameterNames(); // 獲取所有的初始化參數的name
<!--以上兩個方法是ServletContext對象的方法,這個方法獲取的是什麼信息?是以下的配置信息-->
<context-param>
    <param-name>pageSize</param-name>
    <param-value>10</param-value>
</context-param>
<context-param>
    <param-name>startIndex</param-name>
    <param-value>0</param-value>
</context-param>
<!--註意:以上的配置信息屬於應用級的配置信息,一般一個項目中共用的配置信息會放到以上的標簽當中。-->
<!--如果你的配置信息只是想給某一個servlet作為參考,那麼你配置到servlet標簽當中即可,使用ServletConfig對象來獲取。-->

舉例:

如下是 Servle 對象通過,ServletContext對象的上述兩個方法,獲取到 標簽當中編寫的配置信息:

<?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_4_0.xsd"
         version="4.0">

    <!--如下是: context-param 的編寫的配置信息,該配置信息,被所有的Servlet共用
可以使用ServletContext對象獲取到 -->
    <context-param>
        <param-name>pageSize</param-name>
        <param-value>10</param-value>
    </context-param>

    <context-param>
        <param-name>startIndex</param-name>
        <param-value>0</param-value>
    </context-param>

    <servlet>
        <servlet-name>AServlet</servlet-name>
        <servlet-class>com.RainbowSea.servlet.AServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AServlet</servlet-name>
        <url-pattern>/A</url-pattern>
    </servlet-mapping>
    
     <servlet>
        <servlet-name>BServlet</servlet-name>
        <servlet-class>com.RainbowSea.servlet.BServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>BServlet</servlet-name>
        <url-pattern>/B</url-pattern>
    </servlet-mapping>
</web-app>
package com.RainbowSea.servlet;


import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

public class AServlet extends GenericServlet {

    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // 設置在瀏覽器頁面顯示的格式類型,必須在輸出前設置
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();

        // 獲取到GenericServlet父類適配器當中的 servletContext 對象
        ServletContext servletContext = super.getServletContext();
        writer.print("AServlet 下的 servletContext的值: " + servletContext + "<br>");

        //  一次性獲取到 <context-param> </context-param>標簽當中所有的 name 值
        Enumeration<String> names = servletContext.getInitParameterNames();

        while(names.hasMoreElements()) { // 判斷該集合是否還有元素,有返回true,沒有返回false
            // 獲取到元素當中的 name值
            String name = names.nextElement();
            // 通過對象 <context-param> </context-param>標簽下的name獲取到對應的value值
            String value = servletContext.getInitParameter(name);
            writer.print(name + "--->" + value);
            writer.print("<br>");
        }

    }

}

package com.RainbowSea.servlet;


import javax.servlet.GenericServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import java

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • SwiftUI 2.0 備忘清單 IT寶庫SwiftUI 2.0開髮帶查該備忘單提供了使用 SwiftUI 的標簽的一些示例等入門,為開發人員分享快速參考備忘單。 開發速查表大綱 入門 介紹 SwiftUI 與 UIKit 效果一致 View(視圖) Text Text 設置文本格式 Label L ...
  • 前言 由於年底失業,以及身體上的原因,所以一直懶著,沒有找工作,也沒有背八股文。當然背八股文對於一些原本基礎較好的人來說,是一個回顧記憶的過程,但是對我這種基礎本身就不牢靠的人來說,無異於是一個很痛苦的過程。所以面試前的知識回顧,應該是以理解為主的一個活動。我上大學時,Java語言還是當時比較熱門的 ...
  • 一、技術棧 核心框架:spring boot2、spring cloud、spring cloud alibaba配置中心:nacos註冊中心:nacos負載均衡:Spring Cloud Load balancer服務調用:open feign服務容錯:Alibaba sentinel api網關 ...
  • Redis是一款開源的高性能key-value資料庫,廣泛應用於各種場景。在Redis中,數據類型(type)和編碼(encoding)是非常重要的概念。本篇博客將詳細介紹Redis支持的數據類型以及相應的編碼方式和底層實現原理。 ...
  • 教程簡介 IT寶庫MS項目入門教程 - 從基本概念開始,簡單易學地學習MS項目,包括設置,簡介,入門,創建新計劃,設置資源,為任務分配資源,計劃持續時間成本和時間,跟蹤進度,高級計劃,項目狀態報告。 Microsoft Project 是一個國際上享有盛譽的通用的項目管理工具軟體,凝集了許多成熟的項 ...
  • 本文已收錄至Github,推薦閱讀 👉 Java隨想錄 微信公眾號:Java隨想錄 摘要 空指針異常(NullPointerException)可以說是Java程式員最容易遇到的問題了,影響說大不大,說小也不小。為瞭解決這個問題,Java 8 版本中推出了 Optional 類。Optional ...
  • _pycache__文件夾可以看作該文件夾下文件已被python接管或者說編譯過。 在第一次執行代碼的時候,Python解釋器已經把編譯的位元組碼放在__pycache__文件夾中,這樣以後再次運行的話,如果被調用的模塊未發生改變,那就直接跳過編譯這一步,直接去__pycache__文件夾中去運行相關 ...
  • 目前由於功能比較簡單,沒有進行前後端拆分,主要技術點:SpringBoot、WebSocket、webjars、Vue、Element-UI、axios、jszip,核心壓縮庫是 Thumbnailator 提供壓縮支持。源代碼已經在 碼雲 star 公開。歡迎各位大佬批評。 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...