Tomcat學習記錄

1、官網下載Tomcat 的源碼導入IDEA

一、地址:http://tomcat.apache.org/ 左側 Download Tomcat 7,在網頁最下面下載Tomcat源碼;
在這裏插入圖片描述
二、下載完成後打開 , 選擇File->Open->選擇tomcat的源碼目錄(我下載的是apache-tomcat-7.0.106);

java

三、① 在項目配置中設置JDK和源碼目錄:File->Project Structure->project->project SDK
      ②設置 java包設置爲Tomcat項目的Sources文件:File->Project Structure->Modules
在這裏插入圖片描述

web

2、Tomcat啓動重要文件

startup源碼分析

我以爲要研究一個技術的源碼要從它是怎麼啓動運行的開始,特別是很複雜的源碼,全部我就從Tomcat的啓動開始。
在這裏插入圖片描述
apache

在Tomcat的bin目錄下有兩個啓動Tomcat的文件,一個是startup.bat,它用於windows環境下啓動Tomcat;另外一個是startup.sh,它用於Linux環境下Tomcat的啓動。大概看了下這兩個文件中的實現思路差很少同樣的,我就看了startup.bat(windows啓動文件)bootstrap

如下是startup.bat文件,我加了一些註解:
在這裏插入圖片描述
在這裏插入圖片描述
經過以上閱讀能夠獲得一個結論: startup.bat文件實際上就作了一件事情 -> 啓動catalina.bat
這樣我也明白了,爲何在此以前我在windows下配置了catalina.bat就可使用catalina run 啓動Tomcat了。因此未必必定要經過startup.bat來啓動Tomcat,用catalina.bat start也是能夠的。



windows

catalina.bat源碼分析

既然在 startup.bat有關聯到 catalina.bat ,那麼就確定要看看這個文件是幹嗎的了。api

因爲註解比代碼多,我就梳理一下大概的執行邏輯tomcat

首先會直接跳到mainEntry代碼段 -> 在肯定CATALINA_HOME下有catalina.bat後再把CATALINA_HOME賦給變量CATALINA_BASE -> 以後再去得到CLASSPATH(就是咱們配置的環境變量)-> 系統拿到classpath路徑後把它和CATALINA_HOME拼接在一塊兒,最終定位到一個叫bootstrap.jar的文件;
在這裏插入圖片描述
而後到 doStart代碼塊(固然還有doDebug和doRun)並設定參數,最終跳轉到execCmd代碼段;

app

經過以上閱讀能夠獲得一個結論: catalina.bat最終執行了Bootstrap類中的main方法
讀完catalina.bat會發現,咱們能夠經過設定不一樣的參數讓Tomcat以不一樣的方式運行。好比說:在IDEA中能夠選擇debug等模式啓動Tomcat的,也能夠爲其配置參數,在catalina.bat中咱們能夠看到了啓動Tomcat背後的運做流程。
eclipse

//public final class Bootstrap Bootstrap類的main方法
 public static void main(String args[]) { 

        synchronized (daemonLock) { 
            if (daemon == null) { 
                // Don't set daemon until init() has completed
                Bootstrap bootstrap = new Bootstrap();
                try { 
                    bootstrap.init();  //注意這個init()方法
                } catch (Throwable t) { 
                    handleThrowable(t);
                    t.printStackTrace();
                    return;
                }
                daemon = bootstrap;
            } else { 
                // When running as a service the call to stop will be on a new
                // thread so make sure the correct class loader is used to
                // prevent a range of class not found exceptions.
                Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
            }
        }

剛剛說了,既然啓動是靠Bootstrap的main()方法,那麼不妨這麼設置一下來運行項目:
首先:新建 catalina-home和像下圖同樣新建第一個pom.xml文件,並在apache-tomcat-9.0.39-src文件裏新建第二個pom.xml(這兩個pom文件能夠在IDEA新建,也能夠去其餘項目中引入)
在這裏插入圖片描述
將apache-tomcat-9.0.39-src中的conf文件複製到新建的catalina-home目錄之下,再將如下內容複製到apache-tomcat-9.0.39-src中的pom.xml中


webapp

<?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>Tomcat9.0</artifactId>
    <name>Tomcat9</name>
    <version>9.0.20</version>

    <build>
        <finalName>Tomcat9</finalName>
        <sourceDirectory>java</sourceDirectory>
        <resources>
            <resource>
                <directory>java</directory>
            </resource>
        </resources>
        <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>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.10.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant-apache-log4j</artifactId>
            <version>1.9.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant-commons-logging</artifactId>
            <version>1.9.5</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.rpc</groupId>
            <artifactId>javax.xml.rpc-api</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.6.1</version>
        </dependency>


        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>4.0.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

再將如下內容複製到外層pom.xml中:

<?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/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.yang</groupId>
    <artifactId>apache-tomcat-study</artifactId>
    <name>Tomcat 9.0 Study</name>
    <version>1.0</version>
    <packaging>pom</packaging>

     <!--表示外層項目包含內層項目-->
    <modules>
        <module>apache-tomcat-9.0.39-src</module>
    </modules>

</project>

而後:配置 Edit Configrations
在這裏插入圖片描述
Main class:

org.apache.catalina.startup.Bootstrap

vm options配置:

-Dcatalina.home="E:\Code\apache-tomcat-9.0.39-src\catalina-home"
-Dfile.encoding=UTF8
-Duser.language=en
-Duser.region=US

在這裏插入圖片描述
在這裏插入圖片描述
保存運行便可便可。
在這裏插入圖片描述


3、Tomcat啓動流程分析

啓動的分析思路就先看到這裏,先來看看service.xml到底配置了什麼,因而就延伸出來了如下知識。我先說一下Tomcat 部署項目的方式,而後再來引入Tomcat的四大Servlet容器

一、部署項目的方式

背景:我把Tomcat源碼這個項目跑在了IDEA中

有三種方式:這三種方式並非憑空產生,是有源碼對應的。
在這裏插入圖片描述
(1)方式一:將web項目(應用)打包成 war 包,以後直接把這個war包放在webapps文件裏
這種方式很好理解,就是把應用交給Tomcat去執行。
那麼問題是Tomcat是怎麼知道這個war包在webapps下,並它就是一個應用呢?
這個問題一會看到四大容器之一的Host就知道了。
在這裏插入圖片描述
在這裏插入圖片描述






(2)方式二:文件夾部署(只要有.class字節碼便可運行項目)
第一種部署方式運行項目時,就是解壓 war 包而且放到當前webapps目錄下,此時是能夠刪除掉 war包的,只存在這個解壓過來的文件也是能夠獨立運行項目的,這就引出了第二種部署方式,文件夾部署。

(3)方式二:配置應用指定的位置
把應用的位置指定在Host配置中。
在這裏插入圖片描述

二、Tomcat中Container管理四大Servlet容器

先來看看有哪一些,分別是
在這裏插入圖片描述

(1)Engine 表示整個Catalina Servlet引擎,是Container 組件的最高層,用來管理Host 或者Context的實現,是指定默認的Host爲 localhost,名字指定爲 Catalina,這就是爲何咱們不指定Host也可使用localhost虛擬主機來訪問到應用。
在這裏插入圖片描述
也就是說一個 Engine 對應一個

List<Host> hosts;

(2)Host:我理解它是一個Engine管理下的一個虛擬主機,默認的虛擬主機是 localhost,也就是使用這個虛擬的主機來告訴咱們要訪問的 Tomcat 服務的位置,好比說使用 localhost://8080這個就是對應一個虛擬主機,而後再這個Host 裏面來配置相應的應用,這樣就是順利訪問到咱們指定要訪問的應用了。

固然能夠存在多個虛擬主機,雖然它們的都是對應同一個 Tomcat,可是對應的應用不同。一個虛擬主機裏面也能夠對應不一樣的應用。說白了,多個Host就是來作一個多個應用的指定位置的
在這裏插入圖片描述
這也解釋了爲何 Tomcat 會去webapps裏找應用。
也就是說一個 Host 對應一個


List<Context> contexts;

(3)Context:直接理解的話,是上下文,可是我理解它是一個應用,或者是一個應用的配置,使用文件描述符配置文件的話,就會使用到 Context 容器。一個Context能夠對應一個應用。
也就是說一個 Context 對應一個

List<Servlet> servlets;

(4)Wrapper:我理解它就是來對咱們同一個 Servlet 的不一樣實例來進行分類管理的,也就是屢次請求Tomcat 中同一個 Servlet 資源就會產生不一樣的 Servlet 實例,然而這些實例不可能任意在容器中,這樣就不要管理,會形成混亂,因此就用 Weapper 來對同一類Servlet 的不一樣實例進行分類。Wrapper 還用來管理一個 Servlet 的生命週期。
也就是一個 Wrapper 對應一個 Servlet 類型。

List<Servlet> servlets;

而一個Context 就對應了一個Wrapper了

List<Wrapper> wrappers;

Http請求在Container中的傳遞流程
在這裏插入圖片描述

好了,以上就是對 Container 接口的四大子接口的分析,它們分別對應四大 Servlet容器。

花了一個晚上搞清楚了這些接口和容器的關係了,直接看這個時序圖:
在這裏插入圖片描述

三、分析啓動時序圖

從Bootstrap類的main方法開始,Tomcat會以鏈的方式逐級調用各個模塊的init()方法進行初始化。待各個模塊都初始化後,又會逐級調用各個模塊的start()方法啓動各個模塊。
在這裏插入圖片描述
Bootstrap類首先會建立一個本類對象,而後調用init()方法進行初始化。

這裏說一下,實例化是在堆空間中開闢相應的空間並賦默認值,初始化是調用 init() 方法賦實際值的一個過程。
在這裏插入圖片描述
假如是正常執行 start 的方式的話,能夠看到在設置等待後,調用了本類對象的load()方法。查看load()方法的源碼:
在這裏插入圖片描述
能夠看到方法的最後經過反射的方式調用了成員變量catalinaDaemon的load()方法。經過跟蹤源碼能夠看到catalinaDaemon是Bootstrap類的一個私有成員變量。



public final class Bootstrap { 
    private static final Log log = LogFactory.getLog(Bootstrap.class);
    /** * Daemon object used by main.di */
    private static final Object daemonLock = new Object();
    private static volatile Bootstrap daemon = null;
    /** * Daemon reference. */
     //在init()方法中使用反射機制建立catalina賦給catalinaDaemon
    private Object catalinaDaemon = null;

它會在Bootstrap的init()方法中經過反射的方式完成初始化。下面咱們回過頭來看init()方法的源碼

在這裏插入圖片描述
能夠看到init()方法建立了一個Catalina對象, 並把該對象賦給了catalinaDaemon。

以後再執行 getServer 方法來建立 Server 容器。

public interface Service extends Lifecycle { 
    /** * @return the <code>Container</code> that handles requests for all * <code>Connectors</code> associated with this Service. */
    public Container getContainer();

來到了 Service 裏,你會看到第一句就定義一個獲取 Container 的方法,也就是能夠獲取一個惟一的Container。 在這個 Service 裏,你會發現有 ExecutorConnctor

此時也就是說能夠走 Container 被繼承的四個 Servlet 容器了。

到此也就能夠畫出簡單的一個UML圖。來清晰展現這些接口之間的大概關係。

在這裏插入圖片描述
實現與繼承關係:
在這裏插入圖片描述

4、Request請求過來Tomcat在幹嗎

一、Pipeline

相關文章
相關標籤/搜索