欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報邀請碼778899分享:java Tomcat基礎(chǔ)詳解

柚子快報邀請碼778899分享:java Tomcat基礎(chǔ)詳解

http://yzkb.51969.com/

第一篇:Tomcat基礎(chǔ)篇

lecture:鄧澎波

一、構(gòu)建Tomcat源碼環(huán)境

??工欲善其事必先利其器,為了學(xué)好Tomcat源碼,我們需要先在本地構(gòu)建一個Tomcat的運行環(huán)境。

1.源碼環(huán)境下載

源碼有兩種下載方式:

1.1 官網(wǎng)下載

https://tomcat.apache.org/

1.2 GitHub下載

當(dāng)然你也可以通過GitHub來拉取源代碼

https://github.com/apache/tomcat

2.Maven環(huán)境搭建

2.1 環(huán)境準備

打開IEDA導(dǎo)入項目,然后在項目中創(chuàng)建一個新的pom.xml文件,里面的內(nèi)容為:

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">

4.0.0

org.apache.tomcat

apache-tomcat

8.5

org.apache.ant

ant

1.10.4

wsdl4j

wsdl4j

1.6.2

javax.xml

jaxrpc-api

1.1

org.eclipse.jdt.core.compiler

ecj

4.5.1

junit

junit

4.13

apache-tomcat

java

java

org.apache.maven.plugins

maven-compiler-plugin

3.8.1

1.8

1.8

UTF-8

然后設(shè)置項目為Maven項目,選中pom.xml文件,鼠標右點。選擇 Add as Maven Project .

在右側(cè)出現(xiàn)的Maven菜單中選擇編譯項目(compile)

2.2 項目啟動

編譯成功后進入 Bootstrap中,啟動main方法

出現(xiàn)如下提示,說明啟動成功,只是中文亂碼了

2.3 解決中文亂碼問題

中文亂碼問題的解決方案,修改兩處地方即可

1.修改org.apache.jasper.compiler.Localizer#getMessage(java.lang.String)方法

public static String getMessage(String errCode) {

String errMsg = errCode;

try {

if (bundle != null) {

errMsg = bundle.getString(errCode);

}

} catch (MissingResourceException e) {

}

try{

errMsg = new String(errMsg.getBytes("ISO-8859-1"),"UTF-8");

}catch (UnsupportedEncodingException e){

e.printStackTrace();

}

return errMsg;

}

2.修改org.apache.tomcat.util.res.StringManager#getString(java.lang.String)

重啟服務(wù)

啟動正常,但是訪問的時候出現(xiàn)了問題

2.4 解決不支持JSP的問題

啟動成功后,在訪問首頁的時候,出現(xiàn)了500錯誤,而且提示 無法為JSP編譯類。

原因是無法編譯jsp。解決也很簡單,按照下面步驟操作即可

上面的報錯解決方式,可以在org.apache.catalina.startup.ContextConfig類中的configureStart方法中,添加一下JSP解析器初始化即可

context.addServletContainerInitializer(new JasperInitializer(), null);

重啟服務(wù):訪問搞定

到此Tomcat的源碼環(huán)境我們就已經(jīng)準備好了,接下來就可以開始我們的Tomcat源碼之旅了!!!

二、Tomcat源碼結(jié)構(gòu)介紹

??在分析Tomcat源碼之前,我們先來看下Tomcat源碼的結(jié)構(gòu)組成,這樣會更加的有利于我們更好的來分析源碼。

1.項目源碼結(jié)構(gòu)

我們先從源碼結(jié)構(gòu)開始。Tomcat 服務(wù)器相關(guān)的代碼在 java 文件夾下面,后面我們在進入這個文件夾去分析:

之前如何手動在Tomcat中部署過項目的話,這塊應(yīng)該會比較清楚點。

2.Tomcat源碼結(jié)構(gòu)

Tomcat 源碼位于 java 文件夾下面。這個java文件夾中的每個包的作用,我們簡單的來介紹下,后面在分析核心源碼的時候會重點講解。

我們可以看到在java目錄下,分為了兩個結(jié)構(gòu),一個是javax另一個是org.apache這兩個包

2.1 javax

在javax中保存的是新的JavaEE規(guī)范??梢跃唧w來看看每個目錄的作用。

模塊作用說明annotationannotation 這個模塊的作用是定義了一些公用的注解,避免在不同的規(guī)范中定義相同的注解。ejbejb是個古老的傳說,我們不管el在jsp中可以使用EL表達式,這么模塊解析EL表達式的mail和郵件相關(guān)的規(guī)范persistence持久化相關(guān)的security和安全相關(guān)的內(nèi)容servlet這個指定的是Servlet的開發(fā)規(guī)范,Tomcat本質(zhì)上就是一個實現(xiàn)了Servlet規(guī)范的一個容器,Servlet定義了服務(wù)端處理Http請求和響應(yīng)的方式(規(guī)范)websocket定義了使用 websocket 協(xié)議的服務(wù)端和客戶端 APIxml.ws定義了基于 SOAP 協(xié)議的 xml 方式的 web 服務(wù)

2.2 org.apache

org.apache這個包是Tomcat的源碼包,也是針對上面的JavaEE規(guī)范的部分實現(xiàn),Tomcat的本質(zhì)就是對JavaEE的某些規(guī)范的實現(xiàn)合集,首先肯定實現(xiàn)了Servlet規(guī)范

模塊作用catalinacatalina是Tomcat的核心模塊,里面完整的實現(xiàn)了Servlet規(guī)范,Tomcat啟動的主方法也在里面,后面我們分析的重點。coyotetomcat 的核心代碼,負責(zé)將網(wǎng)絡(luò)請求轉(zhuǎn)化后和 Catalina 進行通信。el這個是上面javax中的el規(guī)范的實現(xiàn)jasper主要負責(zé)把jsp代碼轉(zhuǎn)換為java代碼。juli日志相關(guān)的工具naming命名空間相關(guān)的內(nèi)容tomcat各種輔助工具,包括 websocket 的實現(xiàn)。

3.Tomcat模塊設(shè)計

連接器的作用:

連接器功能· 監(jiān)聽網(wǎng)絡(luò)端口。接受網(wǎng)絡(luò)連接請求。根據(jù)具體應(yīng)用層協(xié)議(http/ajp)解析字節(jié)流,生成統(tǒng)一的Tomcat Request對象。將Tomcat Request對象轉(zhuǎn)成標準的ServletRequest。調(diào)用Servlet容器,得到ServletResponse。將ServletResponse轉(zhuǎn)成Tomcat Response對象。將Tomcat Response轉(zhuǎn)成網(wǎng)絡(luò)字節(jié)流。將響應(yīng)字節(jié)流寫回給瀏覽器。

三、Tomcat的架構(gòu)設(shè)計

1.Servlet規(guī)范

1.1 Servlet作用講解

??Servlet是JavaEE規(guī)范中的一種,主要是為了擴展Java作為Web服務(wù)的功能,統(tǒng)一定義了對應(yīng)的接口,比如Servlet接口,HttpRequest接口,HttpResponse接口,F(xiàn)ilter接口。然后由具體的服務(wù)廠商來實現(xiàn)這些接口功能,比如Tomcat,jetty等。

?&ems;在規(guī)范里面并不會有具體的實現(xiàn)。可以自行看下源碼,而在Servlet規(guī)范中規(guī)定了一個http請求到來的執(zhí)行處理流程:對應(yīng)的服務(wù)器容器會接收到對應(yīng)的Http請求,然后解析該請求,然后創(chuàng)建對應(yīng)的Servlet實例,調(diào)用對應(yīng)init方法來完成初始化,把請求的相關(guān)信息封裝為HttpServletRequest對象來調(diào)用Servlet的service方法來處理請求,然后通過HttpServletResponse封裝響應(yīng)的信息交給容器,響應(yīng)給客戶端。

1.2 Servlet核心API

??我們再來回顧下Servlet中的核心API,這塊對我們更好的掌握Tomcat的內(nèi)容還是非常有幫助的。

API描述ServletConfig獲取servlet初始化參數(shù)和servletContext對象。ServletContext在整個Web應(yīng)用的動態(tài)資源之間共享數(shù)據(jù)。ServletRequest封裝Http請求信息,在請求時創(chuàng)建。ServletResponse封裝Http響應(yīng)信息,在請求時創(chuàng)建。

ServletConfig:

??容器在初始化servlet時,為該servlet創(chuàng)建一個servletConfig對象,并將這個對象通過init()方法來傳遞并保存在此Servlet對象中。核心作用:

獲取初始化信息;獲取ServletContext對象。

ServletContext

??一個項目只有一個ServletContext對象,可以在多個Servlet中來獲取這個對象,使用它可以給多個Servlet傳遞數(shù)據(jù),該對象在Tomcat啟動時就創(chuàng)建,在Tomcat關(guān)閉時才會銷毀!作用是在整個Web應(yīng)用的動態(tài)資源之間共享數(shù)據(jù)。

??在實際的Servlet開發(fā)中,我們會實現(xiàn)HttpServlet接口,在該接口中會實現(xiàn)GenericServlet,而在GenericServlet會實現(xiàn)ServiceConfig接口,從而可以獲取ServletContext容器對象

所以在Servlet中我們可以很容易的獲取到ServletContext對象,從而完成對應(yīng)的操作。

public class ServletTwoImpl extends HttpServlet {

@Override

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

response.setContentType("text/html;charset=utf-8");

// 1、參數(shù)傳遞

ServletContext servletContext = this.getServletContext() ;

String value = String.valueOf(servletContext.getAttribute("name")) ;

System.out.println("value="+value);

// 2、獲取初始化參數(shù)

String userName= servletContext.getInitParameter("user-name") ;

System.out.println("userName="+userName);

// 3、獲取應(yīng)用信息

String servletContextName = servletContext.getServletContextName() ;

System.out.println("servletContextName="+servletContextName);

// 4、獲取路徑

String pathOne = servletContext.getRealPath("/") ;

String pathTwo = servletContext.getRealPath("/WEB-INF/") ;

System.out.println("pathOne="+pathOne+";pathTwo="+pathTwo);

response.getWriter().print("執(zhí)行:doGet; value:"+value);

}

}

1.3 ServletRequest

??HttpServletRequest接口繼承ServletRequest接口,用于封裝請求信息,該對象在用戶每次請求servlet時創(chuàng)建并傳入servlet的service()方法,在該方法中,傳入的servletRequest將會被強制轉(zhuǎn)化為HttpservletRequest 對象來進行HTTP請求信息的處理。核心作用:

獲取請求報文信息;獲取網(wǎng)絡(luò)連接信息;獲取請求域?qū)傩孕畔ⅰ?/p>

1.4 ServletResponse

??HttpServletResponse繼承自ServletResponse,封裝了Http響應(yīng)信息??蛻舳嗣總€請求,服務(wù)器都會創(chuàng)建一個response對象,并傳入給Servlet.service()方法。核心作用:

設(shè)置響應(yīng)頭信息;發(fā)送狀態(tài)碼;設(shè)置響應(yīng)正文;重定向;

2.Tomcat的設(shè)計

??通過上面Servlet規(guī)范的介紹,其實我們發(fā)下我們要實現(xiàn)Servlet規(guī)范的話,很重要的就得提供一個服務(wù)容器來獲取請求,解析封裝數(shù)據(jù),并調(diào)用Servlet實例相關(guān)的方法。也就是如下圖中的部分

??這塊的內(nèi)容其實就是Tomcat,具體的我們來看看。

2.1 什么是Tomcat

??Tomcat是一個容器,用于承載Servlet,那么我們說Tomcat就是一個實現(xiàn)了部分J2EE規(guī)范的服務(wù)器。J2 EE和Jakarta EE(Eclipse基金會)這兩是啥?用于Tomcat10以后都是Jakarta EE,而9之前就是J2EE.

2.2 Tomcat的架構(gòu)結(jié)構(gòu)

??我們通過上面的分析,知道Tomcat是一個Servlet規(guī)范的實現(xiàn),要接收請求和響應(yīng)請求,那么具體是如何實現(xiàn)的呢?這塊我們可以通過conf下的server.xml得出對應(yīng)的結(jié)論。

??server.xml是Tomcat中最重要的配置文件,server.xml 的每一個元素都對應(yīng)了Tomcat 中的一個組件 ;通過對xml文件中元素的配置,可以實現(xiàn)對Tomcat中各個組件的控制。因此,學(xué)習(xí)server.xml文件的配置,對于了解和使用Tomcat至關(guān)重要.

官方文檔:https://tomcat.apache.org/tomcat-8.5-doc/config/server.html

maxThreads="150" minSpareThreads="4"/>

connectionTimeout="20000"

redirectPort="8443" />

port="8080" protocol="HTTP/1.1"

connectionTimeout="20000"

redirectPort="8443" />

resourceName="UserDatabase"/>

unpackWARs="true" autoDeploy="true">

prefix="localhost_access_log" suffix=".txt"

pattern="%h %l %u %t "%r" %s %b" />

極簡模式

梳理出的結(jié)構(gòu)

對應(yīng)的每個組件的作用。

2.3 組件分類

??官網(wǎng)其實對上面的組件也做了分類:

頂級元素:

Server:是整個配置文件的根元素Service:代表一個Engine元素以及一組與之相連的Connector元素

連接器:

代表了外部客戶端發(fā)送請求到特定Service的接口;同時也是外部客戶端從特定Service接收響應(yīng)的接口。

容器:

??容器的作用是處理Connector接收進來的請求,并產(chǎn)生對應(yīng)的響應(yīng),Engine,Host和Context都是容器,他們不是平行關(guān)系,而是父子關(guān)系。

每個組件的作用:

Engine:可以處理所有請求Host:可以處理發(fā)向一個特定虛擬主機的所有請求Context:可以處理一個特定Web應(yīng)用的所有請求

核心組件的串聯(lián)關(guān)系:

??當(dāng)客戶端請求發(fā)送過來后其實是通過這些組件相互之間配合完成了對應(yīng)的操作。

Server元素在最頂層,代表整個Tomcat容器;一個Server元素中可以有一個或多個Service元素Service在Connector和Engine外面包了一層,把它們組裝在一起,對外提供服務(wù)。一個Service可以包含多個Connector,但是只能包含一個Engine;Connector接收請求,Engine處理請求。Engine、Host和Context都是容器,且Engine包含Host,Host包含Context。每個Host組件代表Engine中的一個虛擬主機;每個Context組件代表在特定Host上運行的一個Web應(yīng)用.

整體Tomcat的運行架構(gòu)圖

四、Tomcat生命周期

??在上篇文章中我們看到了Tomcat架構(gòu)中的核心組件,而且各個組件都有各自的作用,各司其職,而且相互之間也有對應(yīng)的父子關(guān)系,那么這些對象的創(chuàng)建,調(diào)用,銷毀等操作是怎么處理呢?

??也就是在Tomcat中的組件的對象生命周期是怎么管理的呢?針對這個問題,在Tomcat中設(shè)計了Lifecycle接口來統(tǒng)一管理Tomcat中的核心組件的生命周期,所以本文我們就系統(tǒng)的來介紹下Lifecycle接口的設(shè)計

1、LifeCycle接口設(shè)計

??為了統(tǒng)一管理Tomcat中的核心組件的生命周期,而專門設(shè)計了LifeCycle接口來統(tǒng)一管理,我們來看看在LifeCycle接口中聲明了哪些內(nèi)容。

1.1 生命周期的方法

??在LifeCycle中聲明了和生命周期相關(guān)的方法,包括init(),start(),stop(),destory()等方法。

??在聲明的方法執(zhí)行的過程中會涉及到對應(yīng)的狀態(tài)的轉(zhuǎn)換,在LifeCycle接口的頭部文檔中很清楚的說了。

1.2 相關(guān)的狀態(tài)處理

??通過上圖我們可以很清楚的看到相關(guān)的方法執(zhí)行會涉及到的相關(guān)狀態(tài)的轉(zhuǎn)換,比如init()會從New這個狀態(tài)開始,然后會進入 INITIALIZING 和 INITIALIZED 等。因為這塊涉及到了對應(yīng)的狀態(tài)轉(zhuǎn)換,在Lifecycle中聲明了相關(guān)的狀態(tài)和事件的生命周期字符串。

public static final String BEFORE_START_EVENT = "before_start";

public static final String AFTER_START_EVENT = "after_start";

public static final String STOP_EVENT = "stop";

public static final String BEFORE_STOP_EVENT = "before_stop";

public static final String AFTER_STOP_EVENT = "after_stop";

public static final String AFTER_DESTROY_EVENT = "after_destroy";

public static final String BEFORE_DESTROY_EVENT = "before_destroy";

/**

* The LifecycleEvent type for the "periodic" event.

* 周期性事件(后臺線程定時執(zhí)行一些事情,比如:熱部署、熱替換)

*/

public static final String PERIODIC_EVENT = "periodic";

public static final String CONFIGURE_START_EVENT = "configure_start";

public static final String CONFIGURE_STOP_EVENT = "configure_stop";

在LifecycleState中建立了對應(yīng)關(guān)系

??針對特定的事件就會有相關(guān)的監(jiān)聽器來監(jiān)聽處理。在Lifecycle中定義了相關(guān)的處理方法。

public void addLifecycleListener(LifecycleListener listener);

public LifecycleListener[] findLifecycleListeners();

public void removeLifecycleListener(LifecycleListener listener);

??通過方法名稱我們就能很清楚該方法的相關(guān)作用,就不過程介紹了。然后來看下對應(yīng)的監(jiān)聽器和事件接口的對應(yīng)設(shè)計。

2.監(jiān)聽器和事件的設(shè)計

??接下來看下LifecycleListener的設(shè)計。其實代碼非常簡單。

public interface LifecycleListener {

/**

* Acknowledge the occurrence of the specified event.

* 觸發(fā)監(jiān)聽器后要執(zhí)行邏輯的方法

* @param event LifecycleEvent that has occurred

*/

public void lifecycleEvent(LifecycleEvent event);

}

??然后來看下事件的接口

public final class LifecycleEvent extends EventObject {

private static final long serialVersionUID = 1L;

/**

* Construct a new LifecycleEvent with the specified parameters.

*

* @param lifecycle Component on which this event occurred

* @param type Event type (required)

* @param data Event data (if any)

*/

public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {

super(lifecycle); // 向上轉(zhuǎn)型,可接受一切實現(xiàn)了生命周期的組件

this.type = type;

this.data = data;

}

/**

* The event data associated with this event.

* 攜帶的額外的數(shù)據(jù),傳遞給監(jiān)聽器的數(shù)據(jù)

*/

private final Object data;

/**

* The event type this instance represents.

* 事件類型

*/

private final String type;

/**

* @return the event data of this event.

*/

public Object getData() {

return data;

}

/**

* @return the Lifecycle on which this event occurred.

*/

public Lifecycle getLifecycle() {

return (Lifecycle) getSource();

}

/**

* @return the event type of this event.

*/

public String getType() {

return this.type;

}

}

??也是非常簡單,不過多的贅述。

3.LifecycleBase

??通過上面的介紹我們可以看到在Tomcat中設(shè)計了Lifecycle和LifecycleListener和LifecycleEvent來管理核心組件的生命周期,那么我們就需要讓每一個組件都實現(xiàn)相關(guān)的接口。這時你會發(fā)現(xiàn)交給子類的工作量其實是比較大的,不光要完成各個組件的核心功能,還得實現(xiàn)生命周期的相關(guān)處理,耦合性很強,這時在Tomcat中給我們提供了一個LifecycleBase的抽象類,幫助我們實現(xiàn)了很多和具體業(yè)務(wù)無關(guān)的處理,來簡化了具體組件的業(yè)務(wù)。

3.1 事件處理

??在上面的接口設(shè)計中對于監(jiān)聽對應(yīng)的事件處理是沒有實現(xiàn)的,在LifecycleBase把這塊很好的實現(xiàn)了,我們來看下。首先定義了一個容器來存儲所有的監(jiān)聽器

// 存儲了所有的實現(xiàn)了LifecycleListener接口的監(jiān)聽器

private final List lifecycleListeners = new CopyOnWriteArrayList<>();

??同時提供了觸發(fā)監(jiān)聽的相關(guān)的方法,綁定了對應(yīng)的事件。

/**

* Allow sub classes to fire {@link Lifecycle} events.

* 監(jiān)聽器觸發(fā)相關(guān)的事件

* @param type Event type 事件類型

* @param data Data associated with event.

*/

protected void fireLifecycleEvent(String type, Object data) {

LifecycleEvent event = new LifecycleEvent(this, type, data);

for (LifecycleListener listener : lifecycleListeners) {

listener.lifecycleEvent(event);

}

}

??已經(jīng)針對Listener相關(guān)的處理方法

// 添加監(jiān)聽器

@Override

public void addLifecycleListener(LifecycleListener listener) {

lifecycleListeners.add(listener);

}

// 查找所有的監(jiān)聽并轉(zhuǎn)換為了數(shù)組類型

@Override

public LifecycleListener[] findLifecycleListeners() {

return lifecycleListeners.toArray(new LifecycleListener[0]);

}

// 移除某個監(jiān)聽器

@Override

public void removeLifecycleListener(LifecycleListener listener) {

lifecycleListeners.remove(listener);

}

3.2 生命周期方法

??在LifecycleBase中最核心的還是實現(xiàn)了Lifecycle中的生命周期方法,以init方法為例我們來看。

/**

* 實現(xiàn)了 Lifecycle 中定義的init方法

* 該方法和對應(yīng)的組件的狀態(tài)產(chǎn)生的關(guān)聯(lián)

* @throws LifecycleException

*/

@Override

public final synchronized void init() throws LifecycleException {

if (!state.equals(LifecycleState.NEW)) {

// 無效的操作 只有狀態(tài)為 New 的才能調(diào)用init方法進入初始化

invalidTransition(Lifecycle.BEFORE_INIT_EVENT);

}

try {

// 設(shè)置狀態(tài)為初始化進行中....同步在方法中會觸發(fā)對應(yīng)的事件

setStateInternal(LifecycleState.INITIALIZING, null, false);

initInternal(); // 交給子類具體的實現(xiàn) 初始化操作

// 更新狀態(tài)為初始化完成 同步在方法中會觸發(fā)對應(yīng)的事件

setStateInternal(LifecycleState.INITIALIZED, null, false);

} catch (Throwable t) {

handleSubClassException(t, "lifecycleBase.initFail", toString());

}

}

源碼解析:

我們看到首先會判斷當(dāng)前對象的state狀態(tài)是否為NEW,因為init方法只能在NEW狀態(tài)下才能開始初始化如果1條件滿足則會更新state的狀態(tài)為 INITIALIZED 同時會觸發(fā)這個事件然后initInternale()方法會交給子類具體去實現(xiàn),等待子類處理完成后會把狀態(tài)更新為 INITIALIZED。

我們可以進入setStateInternal方法查看最后的關(guān)鍵代碼:

// ....

this.state = state; // 更新狀態(tài)

// 根據(jù)狀態(tài)和事件的綁定關(guān)系獲取對應(yīng)的事件

String lifecycleEvent = state.getLifecycleEvent();

if (lifecycleEvent != null) {

// 發(fā)布對應(yīng)的事件

fireLifecycleEvent(lifecycleEvent, data);

}

??可以看到和對應(yīng)的事件關(guān)聯(lián)起來了。init方法的邏輯弄清楚后,你會發(fā)現(xiàn)start方法,stop方法,destory方法的處理邏輯都是差不多的,可自行觀看。而對應(yīng)的 initInternal()方法的邏輯我們需要在 Server Service Engine Connector等核心組件中再看,這個我們會結(jié)合Tomcat的啟動流程來帶領(lǐng)大家一起查看。下一篇給大家介紹。

五、Tomcat的啟動核心流程

??前面給大家介紹了Tomcat中的生命周期的設(shè)計,掌握了這塊對于我們分析Tomcat的核心流程是非常有幫助的,也就是我們需要創(chuàng)建相關(guān)的核心組件,比如Server,Service肯定都繞不開生命周期的方法。

1.啟動的入口

??你可以通過腳本來啟動Tomcat服務(wù)(startup.bat),但如果你看過腳本的命令,你會發(fā)現(xiàn)最終調(diào)用的還是Bootstrap中的main方法,所以我們需要從main方法來開始

??然后我們?nèi)タ磎ain方法中的代碼,我們需要重點關(guān)注的方法有三個

bootstrap.init()方法load()方法start()方法

??也就是在這三個方法中會完成Tomcat的核心操作。

2.init方法

??我們來看下init方法中的代碼,非核心的我們直接去掉

public void init() throws Exception {

// 創(chuàng)建相關(guān)的類加載器

initClassLoaders();

// 省略部分代碼...

// 通過反射創(chuàng)建了 Catalina 類對象

Class startupClass = catalinaLoader

.loadClass("org.apache.catalina.startup.Catalina");

// 創(chuàng)建了 Catalina 實例

Object startupInstance = startupClass.getConstructor().newInstance();

// 省略部分代碼...

String methodName = "setParentClassLoader";

Class paramTypes[] = new Class[1];

paramTypes[0] = Class.forName("java.lang.ClassLoader");

Object paramValues[] = new Object[1];

paramValues[0] = sharedLoader;

// 把 sharedLoader 設(shè)置為了 commonLoader的父加載器

Method method =

startupInstance.getClass().getMethod(methodName, paramTypes);

method.invoke(startupInstance, paramValues);

// Catalina 實例 賦值給了 catalinaDaemon

catalinaDaemon = startupInstance;

}

首先是調(diào)用了initClassLoaders()方法,這個方法會完成對應(yīng)的ClassLoader的創(chuàng)建,這個比較重要,后面專門寫一篇文章來介紹。通過反射的方式創(chuàng)建了Catalina的類對象,并通過反射創(chuàng)建了Catalina的實例設(shè)置了類加載器的父子關(guān)系用過成員變量catalinaDaemon記錄了我們創(chuàng)建的Catalina實例

??這個是通過bootstrap.init()方法我們可以獲取到的有用的信息。然后我們繼續(xù)往下面看。

3.load方法

??然后我們來看下load方法做了什么事情,代碼如下:

private void load(String[] arguments) throws Exception {

// Call the load() method

String methodName = "load"; // load方法的名稱

Object param[];

Class paramTypes[];

if (arguments==null || arguments.length==0) {

paramTypes = null;

param = null;

} else {

paramTypes = new Class[1];

paramTypes[0] = arguments.getClass();

param = new Object[1];

param[0] = arguments;

}

// catalinaDaemon 就是在 init中創(chuàng)建的 Catalina 對象

Method method =

catalinaDaemon.getClass().getMethod(methodName, paramTypes);

if (log.isDebugEnabled()) {

log.debug("Calling startup class " + method);

}

// 會執(zhí)行 Catalina的load方法

method.invoke(catalinaDaemon, param);

}

??上面的代碼非常簡單,通過注釋我們也可以看出該方法的作用是調(diào)用 Catalina的load方法。所以我們還需要加入到Catalina的load方法中來查看,代碼同樣比較長,只留下關(guān)鍵代碼

public void load() {

if (loaded) {

return; // 只能被加載一次

}

loaded = true;

initDirs(); // 廢棄的方法

// Before digester - it may be needed

initNaming(); // 和JNDI 相關(guān)的內(nèi)容 忽略

// Create and execute our Digester

// 創(chuàng)建并且執(zhí)行我們的 Digester 對象 Server.xml

Digester digester = createStartDigester();

// 省略掉了 Digester文件處理的代碼

getServer().setCatalina(this); // Server對象綁定 Catalina對象

getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());

getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

// Stream redirection

initStreams();

// 省略掉了部分代碼...

getServer().init(); // 完成 Server Service Engine Connector等組件的init操作

}

把上面的代碼簡化后我們發(fā)現(xiàn)這個Load方法其實也是蠻簡單的,就做了兩件事。

通過Apache下的Digester組件完成了Server.xml文件的解析通過getServer().init() 方法完成了Server,Service,Engin,Connector等核心組件的初始化操作,這塊和前面的LifecycleBase呼應(yīng)起來了。

??如果生命周期的內(nèi)容不清楚,請看上一篇文章的介紹。

4.start方法

??最后我們來看下start方法的代碼。

public void start() throws Exception {

if (catalinaDaemon == null) {

init(); // 如果 catalinaDaemon 為空 初始化操作

}

// 獲取的是 Catalina 中的 start方法

Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);

// 執(zhí)行 Catalina 的start方法

method.invoke(catalinaDaemon, (Object [])null);

}

??上面的代碼邏輯也很清楚,就是通過反射的方式調(diào)用了Catalina對象的start方法。所以進入Catalina的start方法中查看。

public void start() {

if (getServer() == null) {

load(); // 如果Server 為空 重新 init 相關(guān)的組件

}

if (getServer() == null) {

log.fatal("Cannot start server. Server instance is not configured.");

return;

}

// Start the new server 關(guān)鍵方法--->啟動Server

try {

getServer().start();

} catch (LifecycleException e) {

// 省略...

}

// 省略...

// Register shutdown hook 注冊關(guān)閉的鉤子

if (useShutdownHook) {

// 省略...

}

if (await) {

await();

stop();

}

}

??通過上面的代碼我們可以發(fā)現(xiàn)核心的代碼還是getServer.start()方法,也就是通過Server對象來嵌套的調(diào)用相關(guān)注解的start方法。

5.核心流程的總結(jié)

我們可以通過下圖來總結(jié)下Tomcat啟動的核心流程

??從圖中我們可以看到Bootstrap其實沒有做什么核心的事情,主要還是Catalina來完成的。

柚子快報邀請碼778899分享:java Tomcat基礎(chǔ)詳解

http://yzkb.51969.com/

參考鏈接

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19351949.html

發(fā)布評論

您暫未設(shè)置收款碼

請在主題配置——文章設(shè)置里上傳

掃描二維碼手機訪問

文章目錄