一:Tomcat核心元件及應用架構詳解

2021-05-27 08:00:08

目錄

Web 容器是什麼?

HTTP 的本質

HTTP 請求響應範例

Cookie 和 Session

Servlet規範

Servlet 容器

Web 應用

擴充套件機制

一、Tomcat各元件認知

2.Tomcat 各元件及關係

二、Tomcat server.xml 設定詳解

三、Tomcat 部署指令碼編寫


Web 容器是什麼?

讓我們先來簡單回顧一下 Web 技術的發展歷史,可以幫助你理解 Web 容器的由來。

早期的 Web 應用主要用於瀏覽新聞等靜態頁面,HTTP 伺服器(比如 Apache、Nginx)向瀏覽器返回靜態 HTML,瀏覽器負責解析 HTML,將結果呈現給使用者。

隨著網際網路的發展,我們已經不滿足於僅僅瀏覽靜態頁面,還希望通過一些互動操作,來獲取動態結果,因此也就需要一些擴充套件機制能夠讓 HTTP 伺服器呼叫伺服器端程式。

於是 Sun 公司推出了 Servlet 技術。你可以把 Servlet 簡單理解為執行在伺服器端的 Java 小程式,但是 Servlet 沒有 main 方法,不能獨立執行,因此必須把它部署到 Servlet 容器中,由容器來範例化並呼叫 Servlet。

而 Tomcat 就是一個 Servlet 容器。為了方便使用,它們也具有 HTTP 伺服器的功能,因此 Tomcat 就是一個「HTTP 伺服器 + Servlet 容器」,我們也叫它們 Web 容器。

 

HTTP 的本質

HTTP 協定是瀏覽器與伺服器之間的資料傳送協定。作為應用層協定,HTTP 是基於 TCP/IP 協定來傳遞資料的(HTML 檔案、圖片、查詢結果等),HTTP 協定不涉及封包(Packet)傳輸,主要規定了使用者端和伺服器之間的通訊格式。

假如瀏覽器需要從遠端 HTTP 伺服器獲取一個 HTML 文字,在這個過程中,瀏覽器實際上要做兩件事情。

  • 與伺服器建立 Socket 連線。
  • 生成請求資料並通過 Socket 傳送出去。

0

HTTP 請求響應範例

使用者在登陸頁面輸入使用者名稱和密碼,點選登陸後,瀏覽器發出了這樣的 HTTP 請求:

0

HTTP 請求資料由三部分組成,分別是請求行、請求報頭、請求正文。當這個 HTTP 請求資料到達 Tomcat 後,Tomcat 會把 HTTP 請求資料位元組流解析成一個 Request 物件,這個 Request 物件封裝了 HTTP 所有的請求資訊。接著 Tomcat 把這個 Request 物件交給 Web 應用去處理,處理完後得到一個 Response 物件,Tomcat 會把這個 Response 物件轉成 HTTP 格式的響應資料並行送給瀏覽器。

0

HTTP 的響應也是由三部分組成,分別是狀態行、響應報頭、報文主體。同樣,我還以極客時間登陸請求的響應為例。

Cookie 和 Session

我們知道,HTTP 協定有個特點是無狀態,請求與請求之間是沒有關係的。這樣會出現一個很尷尬的問題:Web 應用不知道你是誰。因此 HTTP 協定需要一種技術讓請求與請求之間建立起聯絡,並且伺服器需要知道這個請求來自哪個使用者,於是 Cookie 技術出現了。

Cookie 是 HTTP 報文的一個請求頭,Web 應用可以將使用者的標識資訊或者其他一些資訊(使用者名稱等)儲存在 Cookie 中。使用者經過驗證之後,每次 HTTP 請求報文中都包含 Cookie,這樣伺服器讀取這個 Cookie 請求頭就知道使用者是誰了。Cookie 本質上就是一份儲存在使用者原生的檔案,裡面包含了每次請求中都需要傳遞的資訊。

由於 Cookie 以明文的方式儲存在本地,而 Cookie 中往往帶有使用者資訊,這樣就造成了非常大的安全隱患。而 Session 的出現解決了這個問題,Session 可以理解為伺服器端開闢的儲存空間,裡面儲存了使用者的狀態,使用者資訊以 Session 的形式儲存在伺服器端。當使用者請求到來時,伺服器端可以把使用者的請求和使用者的 Session 對應起來。那麼 Session 是怎麼和請求對應起來的呢?答案是通過 Cookie,瀏覽器在 Cookie 中填充了一個 Session ID 之類的欄位用來標識請求。

具體工作過程是這樣的:伺服器在建立 Session 的同時,會為該 Session 生成唯一的 Session ID,當瀏覽器再次傳送請求的時候,會將這個 Session ID 帶上,伺服器接受到請求之後就會依據 Session ID 找到相應的 Session,找到 Session 後,就可以在 Session 中獲取或者新增內容了。而這些內容只會儲存在伺服器中,發到使用者端的只有 Session ID,這樣相對安全,也節省了網路流量,因為不需要在 Cookie 中儲存大量使用者資訊。

那麼 Session 在何時何地建立呢?當然還是在伺服器端程式執行的過程中建立的,不同語言實現的應用程式有不同的建立 Session 的方法。在 Java 中,是 Web 應用程式在呼叫 HttpServletRequest 的 getSession 方法時,由 Web 容器(比如 Tomcat)建立的。

Tomcat 的 Session 管理器提供了多種持久化方案來儲存 Session,通常會採用高效能的儲存方式,比如 Redis,並且通過叢集部署的方式,防止單點故障,從而提升高可用。同時,Session 有過期時間,因此 Tomcat 會開啟後臺執行緒定期的輪詢,如果 Session 過期了就將 Session 失效。

Servlet規範

HTTP 伺服器怎麼知道要呼叫哪個 Java 類的哪個方法呢。最直接的做法是在 HTTP 伺服器程式碼裡寫一大堆 if else 邏輯判斷:如果是 A 請求就調 X 類的 M1 方法,如果是 B 請求就調 Y 類的 M2 方法。但這樣做明顯有問題,因為 HTTP 伺服器的程式碼跟業務邏輯耦合在一起了,如果新加一個業務方法還要改 HTTP 伺服器的程式碼。

那該怎麼解決這個問題呢?我們知道,面向介面程式設計是解決耦合問題的法寶,於是有一夥人就定義了一個介面,各種業務類都必須實現這個介面,這個介面就叫 Servlet 介面,有時我們也把實現了 Servlet 介面的業務類叫作 Servlet。

但是這裡還有一個問題,對於特定的請求,HTTP 伺服器如何知道由哪個 Servlet 來處理呢?Servlet 又是由誰來範例化呢?顯然 HTTP 伺服器不適合做這個工作,否則又和業務類耦合了。

於是,還是那夥人又發明了 Servlet 容器,Servlet 容器用來載入和管理業務類。HTTP 伺服器不直接跟業務類打交道,而是把請求交給 Servlet 容器去處理,Servlet 容器會將請求轉發到具體的 Servlet,如果這個 Servlet 還沒建立,就載入並範例化這個 Servlet,然後呼叫這個 Servlet 的介面方法。因此 Servlet 介面其實是 Servlet 容器跟具體業務類之間的介面。下面我們通過一張圖來加深理解。

0

Servlet 介面和 Servlet 容器這一整套規範叫作 Servlet 規範。Tomcat 和 Jetty 都按照 Servlet 規範的要求實現了 Servlet 容器,同時它們也具有 HTTP 伺服器的功能。作為 Java 程式設計師,如果我們要實現新的業務功能,只需要實現一個 Servlet,並把它註冊到 Tomcat(Servlet 容器)中,剩下的事情就由 Tomcat 幫我們處理了。

Servlet 介面定義了下面五個方法:

public interface Servlet {
    void init(ServletConfig config) throws ServletException;
    
    ServletConfig getServletConfig();
    
    void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;
    
    String getServletInfo();
    
    void destroy();
}

其中最重要是的 service 方法,具體業務類在這個方法裡實現處理邏輯。這個方法有兩個引數:ServletRequest 和 ServletResponse。ServletRequest 用來封裝請求資訊,ServletResponse 用來封裝響應資訊,因此本質上這兩個類是對通訊協定的封裝。

HTTP 協定中的請求和響應就是對應了 HttpServletRequest 和 HttpServletResponse 這兩個類。你可以通過 HttpServletRequest 來獲取所有請求相關的資訊,包括請求路徑、Cookie、HTTP 頭、請求引數等。此外, 我們還可以通過 HttpServletRequest 來建立和獲取 Session。而 HttpServletResponse 是用來封裝 HTTP 響應的。

你可以看到介面中還有兩個跟生命週期有關的方法 init 和 destroy,這是一個比較貼心的設計,Servlet 容器在載入 Servlet 類的時候會呼叫 init 方法,在解除安裝的時候會呼叫 destroy 方法。我們可能會在 init 方法裡初始化一些資源,並在 destroy 方法裡釋放這些資源,比如 Spring MVC 中的 DispatcherServlet,就是在 init 方法裡建立了自己的 Spring 容器。

你還會注意到 ServletConfig 這個類,ServletConfig 的作用就是封裝 Servlet 的初始化引數。你可以在web.xml給 Servlet 設定引數,並在程式裡通過 getServletConfig 方法拿到這些引數。

我們知道,有介面一般就有抽象類,抽象類用來實現介面和封裝通用的邏輯,因此 Servlet 規範提供了 GenericServlet 抽象類,我們可以通過擴充套件它來實現 Servlet。雖然 Servlet 規範並不在乎通訊協定是什麼,但是大多數的 Servlet 都是在 HTTP 環境中處理的,因此 Servet 規範還提供了 HttpServlet 來繼承 GenericServlet,並且加入了 HTTP 特性。這樣我們通過繼承 HttpServlet 類來實現自己的 Servlet,只需要重寫兩個方法:doGet 和 doPost。

Servlet 容器

當客戶請求某個資源時,HTTP 伺服器會用一個 ServletRequest 物件把客戶的請求資訊封裝起來,然後呼叫 Servlet 容器的 service 方法,Servlet 容器拿到請求後,根據請求的 URL 和 Servlet 的對映關係,找到相應的 Servlet,如果 Servlet 還沒有被載入,就用反射機制建立這個 Servlet,並呼叫 Servlet 的 init 方法來完成初始化,接著呼叫 Servlet 的 service 方法來處理請求,把 ServletResponse 物件返回給 HTTP 伺服器,HTTP 伺服器會把響應傳送給使用者端

0

Web 應用

Servlet 容器會範例化和呼叫 Servlet,那 Servlet 是怎麼註冊到 Servlet 容器中的呢?一般來說,我們是以 Web 應用程式的方式來部署 Servlet 的,而根據 Servlet 規範,Web 應用程式有一定的目錄結構,在這個目錄下分別放置了 Servlet 的類檔案、組態檔以及靜態資源,Servlet 容器通過讀取組態檔,就能找到並載入 Servlet。Web 應用的目錄結構大概是下面這樣的:

| -  MyWebApp
      | -  WEB-INF/web.xml        -- 組態檔,用來設定Servlet等
      | -  WEB-INF/lib/           -- 存放Web應用所需各種JAR包
      | -  WEB-INF/classes/       -- 存放你的應用類,比如Servlet類
      | -  META-INF/              -- 目錄存放工程的一些資訊

Servlet 規範裡定義了 ServletContext 這個介面來對應一個 Web 應用。Web 應用部署好後,Servlet 容器在啟動時會載入 Web 應用,併為每個 Web 應用建立唯一的 ServletContext 物件。你可以把 ServletContext 看成是一個全域性物件,一個 Web 應用可能有多個 Servlet,這些 Servlet 可以通過全域性的 ServletContext 來共用資料,這些資料包括 Web 應用的初始化引數、Web 應用目錄下的檔案資源等。由於 ServletContext 持有所有 Servlet 範例,你還可以通過它來實現 Servlet 請求的轉發。

擴充套件機制

引入了 Servlet 規範後,你不需要關心 Socket 網路通訊、不需要關心 HTTP 協定,也不需要關心你的業務類是如何被範例化和呼叫的,因為這些都被 Servlet 規範標準化了,你只要關心怎麼實現的你的業務邏輯。這對於程式設計師來說是件好事,但也有不方便的一面。所謂規範就是說大家都要遵守,就會千篇一律,但是如果這個規範不能滿足你的業務的個性化需求,就有問題了,因此設計一個規範或者一箇中介軟體,要充分考慮到可延伸性。Servlet 規範提供了兩種擴充套件機制:Filter 和 Listener

Filter 是過濾器,這個介面允許你對請求和響應做一些統一的客製化化處理,比如你可以根據請求的頻率來限制存取,或者根據國家地區的不同來修改響應內容。過濾器的工作原理是這樣的:Web 應用部署完成後,Servlet 容器需要範例化 Filter 並把 Filter 連結成一個 FilterChain。當請求進來時,獲取第一個 Filter 並呼叫 doFilter 方法,doFilter 方法負責呼叫這個 FilterChain 中的下一個 Filter。

Listener 是監聽器,這是另一種擴充套件機制。當 Web 應用在 Servlet 容器中執行時,Servlet 容器內部會不斷的發生各種事件,如 Web 應用的啟動和停止、使用者請求到達等。 Servlet 容器提供了一些預設的監聽器來監聽這些事件,當事件發生時,Servlet 容器會負責呼叫監聽器的方法。當然,你可以定義自己的監聽器去監聽你感興趣的事件,將監聽器設定在web.xml中。比如 Spring 就實現了自己的監聽器,來監聽 ServletContext 的啟動事件,目的是當 Servlet 容器啟動時,建立並初始化全域性的 Spring 容器。

Tomcat下載地址:https://tomcat.apache.org/download-80.cgi

0

/bin:存放 Windows 或 Linux 平臺上啟動和關閉 Tomcat 的指令碼檔案。
/conf:存放 Tomcat 的各種全域性組態檔,其中最重要的是server.xml。
/lib:存放 Tomcat 以及所有 Web 應用都可以存取的 JAR 檔案。
/logs:存放 Tomcat 執行時產生的紀錄檔檔案。
/work:存放 JSP 編譯後產生的 Class 檔案。
/webapps:Tomcat 的 Web 應用目錄,預設情況下把 Web 應用放在這個目錄下。

開啟 Tomcat 的紀錄檔目錄,也就是 Tomcat 安裝目錄下的 logs 目錄。Tomcat 的紀錄檔資訊分為兩類 :一是執行紀錄檔,它主要記錄執行過程中的一些資訊,尤其是一些異常錯誤紀錄檔資訊 ;二是存取紀錄檔,它記錄存取的時間、IP 地址、存取的路徑等相關資訊。

  • catalina.***.log 主要是記錄 Tomcat 啟動過程的資訊,在這個檔案可以看到啟動的 JVM 引數以及作業系統等紀錄檔資訊。
  • catalina.out是 Tomcat 的標準輸出(stdout)和標準錯誤(stderr),這是在 Tomcat 的啟動指令碼裡指定的,如果沒有修改的話 stdout 和 stderr 會重定向到這裡。所以在這個檔案裡可以看到我們在MyServlet.java程式裡列印出來的資訊
  • localhost.**.log主要記錄 Web 應用在初始化過程中遇到的未處理的異常,會被 Tomcat 捕獲而輸出這個紀錄檔檔案。
  • localhost_access_log.**.txt存放存取 Tomcat 的請求紀錄檔,包括 IP 地址以及請求的路徑、時間、請求協定以及狀態碼等資訊。
  • manager.***.log/host-manager.***.log存放 Tomcat 自帶的 Manager 專案的紀錄檔資訊。

概要:

  1. Tomcat各核心元件認知
  2. server.xml 設定詳解

一、Tomcat各元件認知

知識點:

  1. Tomcat架構說明
  2. Tomcat元件及關係詳情介紹
  3. Tomcat啟動引數說明
  4. Tomcat架構說明

Tomcat是一個基於JAVA的WEB容器,其實現了JAVA EE中的 Servlet 與 jsp 規範,與Nginx apache 伺服器不同在於一般用於動態請求處理。在架構設計上採用面向元件的方式設計。即整體功能是通過元件的方式拼裝完成。另外每個元件都可以被替換以保證靈活性。

0

2.Tomcat 各元件及關係

  • Server 和 Service
  • Connector 聯結器
    • HTTP 1.1
    • SSL https
    • AJP( Apache JServ Protocol) apache 私有協定,用於apache 反向代理Tomcat
  • Container 
    • Engine 引擎 catalina
    • Host 虛擬機器器 基於域名 分發請求
    • Context 隔離各個WEB應用 每個Context的 ClassLoader都是獨立
  • Component 
    • Manager (管理器)
    • logger (紀錄檔管理)
    • loader (載入器)
    • pipeline (管道)
    • valve (管道中的閥)

0

二、Tomcat server.xml 設定詳解


server

root元素:server 的頂級設定 主要屬性: port:執行關閉命令的埠號 shutdown:關閉命令

  •  演示shutdown的用法  #基於telent 執行SHUTDOWN 命令即可關閉(必須大寫) telnet 127.0.0.1 8005 SHUTDOWN 

service

服務:將多個connector 與一個Engine組合成一個服務,可以設定多個服務。

Connector

聯結器:用於接收 指定協定下的連線 並指定給唯一的Engine 進行處理。 主要屬性:

  • protocol 監聽的協定,預設是http/1.1
  • port 指定伺服器端要建立的埠號
  • minSpareThreads伺服器啟動時建立的處理請求的執行緒數
  • maxThreads 最大可以建立的處理請求的執行緒數
  • enableLookups 如果為true,則可以通過呼叫request.getRemoteHost()進行DNS查詢來得到遠端使用者端的實際主機名,若為false則不進行DNS查詢,而是返回其ip地址
  • redirectPort 指定伺服器正在處理http請求時收到了一個SSL傳輸請求後重定向的埠號
  • acceptCount 指定當所有可以使用的處理請求的執行緒數都被使用時,可以放到處理佇列中的請求數,超過這個數的請求將不予處理
  • connectionTimeout 指定超時的時間數(以毫秒為單位)
  • SSLEnabled 是否開啟 sll 驗證,在Https 存取時需要開啟。生成證書: keytool -genkey -v -alias testKey -keyalg RSA -validity 3650 -keystore D:\test.keystore
  • [ ] 演示設定多個Connector
<Connector port="8860" protocol="org.apache.coyote.http11.Http11NioProtocol"
                connectionTimeout="20000"
                redirectPort="8862"
                URIEncoding="UTF-8"
                useBodyEncodingForURI="true"
                compression="on" compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/plain,text/javascript,text/css,application/x-json,application/json,application/x-javascript"
                maxThreads="1024" minSpareThreads="200"
                acceptCount="800"
                maxConnections="10000"
                enableLookups="false"
        />

Engine

引擎:用於處理連線的執行器,預設的引擎是catalina。一個service 中只能設定一個Engine。 主要屬性:name 引擎名稱 defaultHost 預設host

Host

虛擬機器器:基於域名匹配至指定虛擬機器器。類似於nginx 當中的server,預設的虛擬機器器是localhost. 主要屬性:

  •  演示設定多個Host
<Host name="www.wukong.com"  appBase="/usr/www/wukong"  unpackWARs="true" autoDeploy="true"> 
	<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="www.wukong.com.access_log" 
			suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" /> 
</Host>

Context

應用上下文:一個host 下可以設定多個Context ,每個Context 都有其獨立的classPath。相互隔離,以免造成ClassPath 衝突。 主要屬性:

  •  演示設定多個Context
 <Context path="/testweb" docBase="testweb.war"  reloadbale="true"/>

Valve 閥門:可以理解成

的過濾器,具體設定要基於具體的Valve 介面的子類。以下即為一個存取紀錄檔的Valve

<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="www.wukong.com.access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

三、Tomcat 部署指令碼編寫

Tomcat啟動引數說明

我們平時啟動Tomcat過程是怎麼樣的?

  1. 複製WAR包至Tomcat webapp 目錄。
  2. 執行starut.bat 指令碼啟動。
  3. 啟動過程中war 包會被自動解壓裝載。

但是我們在Eclipse 或idea 中啟動WEB專案的時候 也是把War包複雜至webapps 目錄解壓嗎?顯然不是,其真正做法是在Tomcat程式檔案之外建立了一個部署目錄,在一般生產環境中也是這麼做的 即:Tomcat 程式目錄和部署目錄分開 。 我們只需要在啟動時指定CATALINA_HOME 與 CATALINA_BASE 引數即可實現。

啟動引數 | 描述說明 | |:----|:----| | JAVA_OPTS | jvm 啟動引數 , 設定記憶體 編碼等 -Xms100m -Xmx200m -Dfile.encoding=UTF-8 | | JAVA_HOME | 指定jdk 目錄,如果未設定從java 環境變數當中去找。 | | CATALINA_HOME | Tomcat 程式根目錄 | | CATALINA_BASE | 應用部署目錄,預設為$CATALINA_HOME | | CATALINA_OUT | 應用紀錄檔輸出目錄:預設$CATALINA_BASE/log | | CATALINA_TMPDIR | 應用臨時目錄:預設:$CATALINA_BASE/temp |

可以編寫一個指令碼 來實現自定義設定:

ln -s /home/wukong/apache-tomcat-8.5.56 apache-tomcat

更新 啟動 指令碼

#!/bin/bash 
export JAVA_OPTS="-Xms100m -Xmx200m"
export CATALINA_HOME=/home/wukong/apache-tomcat
export CATALINA_BASE="`pwd`"

case $1 in
        start)
        $CATALINA_HOME/bin/catalina.sh start
                echo start success!!
        ;;
        stop)
                $CATALINA_HOME/bin/catalina.sh stop
                echo stop success!!
        ;;
        restart)
        $CATALINA_HOME/bin/catalina.sh stop
                echo stop success!!
                sleep 3
        $CATALINA_HOME/bin/catalina.sh start
        echo start success!!
        ;;
        version)
        $CATALINA_HOME/bin/catalina.sh version
        ;;
        configtest)
        $CATALINA_HOME/bin/catalina.sh configtest
        ;;
        esac
exit 0

docker 啟動tomcat

docker run -id --name=test_tomcat -e JAVA_OPTS='-Xmx128m' -p 8888:8080 -v /usr/local/tuling-project/tomcat-test/webapps:/usr/local/tomcat/webapps -v /usr/local/tuling-project/tomcat-test/logs:/usr/local/tomcat/logs -v /usr/local/tuling-project/tomcat-test/conf:/usr/local/tomcat/conf --privileged=true tomcat:8

原始碼構建

下載地址:https://tomcat.apache.org/download-80.cgi

設定

1.解壓原始碼 apache-tomcat-8.5.57-src

2.apache-tomcat-8.5.57-src目錄下新增pom檔案

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>Tomcat8.0</artifactId>
    <name>Tomcat8.0</name>
    <version>8.0</version>

    <build>
        <finalName>Tomcat8.0</finalName>
        <sourceDirectory>java</sourceDirectory>
        <testSourceDirectory>test</testSourceDirectory>
        <resources>
            <resource>
                <directory>java</directory>
            </resource>
        </resources>
        <testResources>
            <testResource>
                <directory>test</directory>
            </testResource>
        </testResources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>javax.xml</groupId>
            <artifactId>jaxrpc</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.5.1</version>
        </dependency>

    </dependencies>
</project>

3.在apache-tomcat-8.5.57-src 同級目錄新建 catalina-home並保證目錄下面檔案如下

0

注意: 上面資料夾apache-tomcat-8.5.57-src裡面有的,就剪下過來,沒有的就新建一個, bin conf webapps 應該是從apache-tomcat-8.5.57-src剪下過來的

4.idea引入專案

File->Open 選擇解壓的C:\Users\wukong\Downloads\apache-tomcat-8.5.57-src\apache-tomcat-8.5.57-src

設定啟動

  1. MainClass: org.apache.catalina.startup.Bootstrap
  2. VmOptions: -Dcatalina.home=C:\Users\wukong\Downloads\apache-tomcat-8.5.57-src\apache-tomcat-8.5.57-src\catalina-home

0

啟動報錯

TestCookieFilter 報錯找不到這個類CookieFilter

解決方法:

1. 刪除:TestCookieFilter

啟動後,存取localhost:8080 報錯 org.apache.jasper.JasperException: java.lang.NullPointerException

解決方案:

org.apache.catalina.startup.Bootstrap 新增程式碼塊

{
        JasperInitializer initializer =new JasperInitializer();
         }