Tomcat 7 啟動分析(四)各元件 init、start 方法呼叫
在正常啟動 Tomcat 7 的情況下,上篇文章分析到了執行 org.apache.catalina.core.StandardServer 的 init 和 start 方法這兒,那麼就來看看這兩個方法裡面到底幹了些什麼。
但是在 StandardServer 類裡面並沒有發現這兩個方法:
由此推知這兩方法必定是在該類的父類中已實現了,在 StandardServer 類的父類 LifecycleMBeanBase 類的父類 LifecycleBase 類裡面終於找到了這兩個方法的實現,下面先來看下 init 方法:
1 @Override
2 public final synchronized void init() throws LifecycleException {
3 if (!state.equals(LifecycleState.NEW)) {
4 invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
5 }
6 setStateInternal(LifecycleState.INITIALIZING, null, false);
7
8 try {
9 initInternal();
10 } catch (Throwable t) {
11 ExceptionUtils.handleThrowable(t);
12 setStateInternal(LifecycleState.FAILED, null, false);
13 throw new LifecycleException(
14 sm.getString("lifecycleBase.initFail",toString()), t);
15 }
16
17 setStateInternal(LifecycleState.INITIALIZED, null, false);
18 }
19
20
21 protected abstract void initInternal() throws LifecycleException;
先將干擾程式閱讀視線的 setStateInternal 方法呼叫忽略掉(下一篇文章會詳細講解該方法),發現這裡面就做了一件事情,呼叫了一下接下來定義的抽象方法 initInternal()(第 21 行)。實際上看下 LifecycleBase 的實現類就會發現,所有的元件類幾乎都繼承了 LifecycleBase 類,所以這些元件類一般只會有 initInternal 方法的定義。(這裡所說的元件類就是前面我們分析 Digester 解析時發現的 StandardServer、StandardService、StandardEngine、StandardHost、StandardContext 等類)
這裡所說的元件可以將其理解為我們最開始分析 server.xml 時 xml 檔案裡的各個節點,父子關係也即 xml 檔案裡的父子節點。瀏覽下 LifecycleBase 的子類就會發現節點的實現類都是這個類的子類(記住這點,後面會提到)。
同樣分析 start 方法:
1 /**
2 * {@inheritDoc}
3 */
4 @Override
5 public final synchronized void start() throws LifecycleException {
6
7 if (LifecycleState.STARTING_PREP.equals(state) ||
8 LifecycleState.STARTING.equals(state) ||
9 LifecycleState.STARTED.equals(state)) {
10
11 if (log.isDebugEnabled()) {
12 Exception e = new LifecycleException();
13 log.debug(sm.getString("lifecycleBase.alreadyStarted",
14 toString()), e);
15 } else if (log.isInfoEnabled()) {
16 log.info(sm.getString("lifecycleBase.alreadyStarted",
17 toString()));
18 }
19
20 return;
21 }
22
23 if (state.equals(LifecycleState.NEW)) {
24 init();
25 } else if (state.equals(LifecycleState.FAILED)){
26 stop();
27 } else if (!state.equals(LifecycleState.INITIALIZED) &&
28 !state.equals(LifecycleState.STOPPED)) {
29 invalidTransition(Lifecycle.BEFORE_START_EVENT);
30 }
31
32 setStateInternal(LifecycleState.STARTING_PREP, null, false);
33
34 try {
35 startInternal();
36 } catch (Throwable t) {
37 ExceptionUtils.handleThrowable(t);
38 setStateInternal(LifecycleState.FAILED, null, false);
39 throw new LifecycleException(
40 sm.getString("lifecycleBase.startFail",toString()), t);
41 }
42
43 if (state.equals(LifecycleState.FAILED) ||
44 state.equals(LifecycleState.MUST_STOP)) {
45 stop();
46 } else {
47 // Shouldn't be necessary but acts as a check that sub-classes are
48 // doing what they are supposed to.
49 if (!state.equals(LifecycleState.STARTING)) {
50 invalidTransition(Lifecycle.AFTER_START_EVENT);
51 }
52
53 setStateInternal(LifecycleState.STARTED, null, false);
54 }
55 }
56
57
58 /**
59 * Sub-classes must ensure that the state is changed to
60 * {@link LifecycleState#STARTING} during the execution of this method.
61 * Changing state will trigger the {@link Lifecycle#START_EVENT} event.
62 *
63 * If a component fails to start it may either throw a
64 * {@link LifecycleException} which will cause it's parent to fail to start
65 * or it can place itself in the error state in which case {@link #stop()}
66 * will be called on the failed component but the parent component will
67 * continue to start normally.
68 *
69 * @throws LifecycleException
70 */
71 protected abstract void startInternal() throws LifecycleException;
第 7 到 21 行是 start 功能的前置校驗,這裡如果發現 start 方法已經呼叫過了,將會記錄日誌並直接返回。第 23 到 30 行如果發現 start 放的需要做的前置方法沒有呼叫完,或者呼叫出錯,將會先呼叫這些前置方法。第 32 行暫時先不管,不影響程式閱讀,第 35 行是該方法的實質,將會呼叫本類中定義的抽象方法 startInternal()(第 71 行)。下面的程式碼同上述一樣,都是一些 start 方法呼叫過程中可能出現的錯誤的錯誤處理。
從以上 init 和 start 方法的定義可以看到這兩個方法最終將會呼叫子類中定義的 initInternal 和 startInternal 。
接回本文開頭,一開始在找 StandardServer 類中 init 和 start 方法的定義,看完了上面的分析發現最終會呼叫 StandardServer 類的 initInternal 和 startInternal 兩個方法。那這兩個方法裡面幹了些什麼?
initInternal 方法:
1 /**
2 * Invoke a pre-startup initialization. This is used to allow connectors
3 * to bind to restricted ports under Unix operating environments.
4 */
5 @Override
6 protected void initInternal() throws LifecycleException {
7
8 super.initInternal();
9
10 // Register global String cache
11 // Note although the cache is global, if there are multiple Servers
12 // present in the JVM (may happen when embedding) then the same cache
13 // will be registered under multiple names
14 onameStringCache = register(new StringCache(), "type=StringCache");
15
16 // Register the MBeanFactory
17 MBeanFactory factory = new MBeanFactory();
18 factory.setContainer(this);
19 onameMBeanFactory = register(factory, "type=MBeanFactory");
20
21 // Register the naming resources
22 globalNamingResources.init();
23
24 // Populate the extension validator with JARs from common and shared
25 // class loaders
26 if (getCatalina() != null) {
27 ClassLoader cl = getCatalina().getParentClassLoader();
28 // Walk the class loader hierarchy. Stop at the system class loader.
29 // This will add the shared (if present) and common class loaders
30 while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
31 if (cl instanceof URLClassLoader) {
32 URL[] urls = ((URLClassLoader) cl).getURLs();
33 for (URL url : urls) {
34 if (url.getProtocol().equals("file")) {
35 try {
36 File f = new File (url.toURI());
37 if (f.isFile() &&
38 f.getName().endsWith(".jar")) {
39 ExtensionValidator.addSystemResource(f);
40 }
41 } catch (URISyntaxException e) {
42 // Ignore
43 } catch (IOException e) {
44 // Ignore
45 }
46 }
47 }
48 }
49 cl = cl.getParent();
50 }
51 }
52 // Initialize our defined Services
53 for (int i = 0; i < services.length; i++) {
54 services[i].init();
55 }
56 }
init 方法裡面做了好幾件事情,牽涉的話題比較多,這裡重點關注最後第 53 到 55 行的程式碼,這裡將迴圈呼叫 Server 類裡內建的 Service 陣列的 init 方法。
startInternal 方法:
1 /**
2 * Start nested components ({@link Service}s) and implement the requirements
3 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
4 *
5 * @exception LifecycleException if this component detects a fatal error
6 * that prevents this component from being used
7 */
8 @Override
9 protected void startInternal() throws LifecycleException {
10
11 fireLifecycleEvent(CONFIGURE_START_EVENT, null);
12 setState(LifecycleState.STARTING);
13
14 globalNamingResources.start();
15
16 // Start our defined Services
17 synchronized (services) {
18 for (int i = 0; i < services.length; i++) {
19 services[i].start();
20 }
21 }
22 }
重點關注第 17 到 21 行,同上一段所分析的程式碼類似,將迴圈呼叫 Sever 類裡內建的 Service 陣列的 start 方法。
那麼這裡提到的 Service 的物件到底是什麼?
上篇文章分析 Digester 時提到“經過對 xml 檔案的解析將會產生 org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext 等等一系列物件,這些物件從前到後前一個包含後一個物件的引用(一對一或一對多的關係)。”
所以正常情況下這裡的 Service 將會是 org.apache.catalina.core.StandardService 的物件(該程式碼見 org.apache.catalina.startup.Catalina 類的 339 到 341 行)。
所以按上面的分析,接下來將會呼叫 StandardService 類的 init 和 start 方法,實際上這個類也是 LifecycleBase 類的子類,所以最終的也會呼叫本類中的 initInternal 和 startInternal 方法。
initInternal 方法:
1 /**
2 * Invoke a pre-startup initialization. This is used to allow connectors
3 * to bind to restricted ports under Unix operating environments.
4 */
5 @Override
6 protected void initInternal() throws LifecycleException {
7
8 super.initInternal();
9
10 if (container != null) {
11 container.init();
12 }
13
14 // Initialize any Executors
15 for (Executor executor : findExecutors()) {
16 if (executor instanceof LifecycleMBeanBase) {
17 ((LifecycleMBeanBase) executor).setDomain(getDomain());
18 }
19 executor.init();
20 }
21
22 // Initialize our defined Connectors
23 synchronized (connectors) {
24 for (Connector connector : connectors) {
25 try {
26 connector.init();
27 } catch (Exception e) {
28 String message = sm.getString(
29 "standardService.connector.initFailed", connector);
30 log.error(message, e);
31
32 if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
33 throw new LifecycleException(message);
34 }
35 }
36 }
37 }
這裡將會呼叫 Service 下的各類子元件中的 init 方法。
startInternal 方法:
1 /**
2 * Start nested components ({@link Executor}s, {@link Connector}s and
3 * {@link Container}s) and implement the requirements of
4 * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
5 *
6 * @exception LifecycleException if this component detects a fatal error
7 * that prevents this component from being used
8 */
9 @Override
10 protected void startInternal() throws LifecycleException {
11
12 if(log.isInfoEnabled())
13 log.info(sm.getString("standardService.start.name", this.name));
14 setState(LifecycleState.STARTING);
15
16 // Start our defined Container first
17 if (container != null) {
18 synchronized (container) {
19 container.start();
20 }
21 }
22
23 synchronized (executors) {
24 for (Executor executor: executors) {
25 executor.start();
26 }
27 }
28
29 // Start our defined Connectors second
30 synchronized (connectors) {
31 for (Connector connector: connectors) {
32 try {
33 // If it has already failed, don't try and start it
34 if (connector.getState() != LifecycleState.FAILED) {
35 connector.start();
36 }
37 } catch (Exception e) {
38 log.error(sm.getString(
39 "standardService.connector.startFailed",
40 connector), e);
41 }
42 }
43 }
44 }
同理,將會呼叫 service 下各類子元件中的 start 方法。
StandardService 的子容器是 StandardEngine ,看下 StandardEngine 的 startInternal 方法:
1 protected synchronized void startInternal() throws LifecycleException {
2
3 // Log our server identification information
4 if(log.isInfoEnabled())
5 log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
6
7 // Standard container startup
8 super.startInternal();
9 }
這裡不同於上面兩級容器的實現,而是直接呼叫了父類的 startInternal 方法:
1 protected synchronized void startInternal() throws LifecycleException {
2
3 // Start our subordinate components, if any
4 if ((loader != null) && (loader instanceof Lifecycle))
5 ((Lifecycle) loader).start();
6 logger = null;
7 getLogger();
8 if ((manager != null) && (manager instanceof Lifecycle))
9 ((Lifecycle) manager).start();
10 if ((cluster != null) && (cluster instanceof Lifecycle))
11 ((Lifecycle) cluster).start();
12 Realm realm = getRealmInternal();
13 if ((realm != null) && (realm instanceof Lifecycle))
14 ((Lifecycle) realm).start();
15 if ((resources != null) && (resources instanceof Lifecycle))
16 ((Lifecycle) resources).start();
17
18 // Start our child containers, if any
19 Container children[] = findChildren();
20 List> results = new ArrayList>();
21 for (int i = 0; i < children.length; i++) {
22 results.add(startStopExecutor.submit(new StartChild(children[i])));
23 }
24
25 boolean fail = false;
26 for (Future result : results) {
27 try {
28 result.get();
29 } catch (Exception e) {
30 log.error(sm.getString("containerBase.threadedStartFailed"), e);
31 fail = true;
32 }
33
34 }
35 if (fail) {
36 throw new LifecycleException(
37 sm.getString("containerBase.threadedStartFailed"));
38 }
39
40 // Start the Valves in our pipeline (including the basic), if any
41 if (pipeline instanceof Lifecycle)
42 ((Lifecycle) pipeline).start();
43
44
45 setState(LifecycleState.STARTING);
46
47 // Start our thread
48 threadStart();
49
50 }
第 19 到 34 行即啟動當前容器下的子容器的程式碼,這裡採用了分執行緒分別啟動的方式。核心的呼叫子容器的 start 方法的程式碼在 StartChild 類的 call 方法中:
1 private static class StartChild implements Callable {
2
3 private Container child;
4
5 public StartChild(Container child) {
6 this.child = child;
7 }
8
9 @Override
10 public Void call() throws LifecycleException {
11 child.start();
12 return null;
13 }
14 }
這裡使用了JDK 5 的執行執行緒的方式,不理解的話請參考相關文件說明。
StandardHost 中的 startInternal 與 StandardEngine 類似,這裡不再贅述,建議有興趣的朋友逐一分析 Review 一遍,碰到元件裡面巢狀的變數不知道具體實現類的就從上篇文章裡面提到的 createStartDigester 那邊開始找起,這裡不能直接找到的就在裡面提到的 new *RuleSet 的 addRuleInstances 方法裡面找。通過這種呼叫將會最終執行完所有在 server.xml 裡配置的節點的實現類中 initInternal 和 startInternal 方法。
最後上面提到的 org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext 等等元件的這兩個方法都會呼叫到。
就這樣,Tomcat 7 在記憶體中為這一連串元件產生物件,建立物件呼叫關係,呼叫它們各自的初始化和啟動方法,啟動的總體過程就介紹完了,這些物件 start 之後將會響應客戶端的請求,為使用者服務了。當然,這裡還沒有涉及到對於具體的釋出到 tomcat 裡面的沒有應用的載入過程,web 應用中配置的 servlet、filter、listener 等的載入、啟動服務過程,瀏覽器發起的一個請求如何經過 Tomcat 內各元件的流轉呼叫到具體應用裡去的,這一系列問題都還沒談到。因為 Tomcat 本身龐大繁雜,需要找一個視角切入進去,為了敘述的簡單,先從整體上對 Tomcat 內包含的各元件產生機制有一個大體輪廓的瞭解,這樣為後面的介紹提供一個統一的背景。
相關文章
- Tomcat 7 啟動分析(三)Digester 的使用Tomcat
- 為啥呼叫new Thread().start()方法會呼叫run()方法?thread
- Tomcat 7 啟動分析(五)Lifecycle 機制和實現原理Tomcat
- JNI-Thread中start方法的呼叫與run方法的回撥分析thread
- Java中多執行緒啟動,為什麼呼叫的是start方法,而不是run方法?Java執行緒
- Tomcat原始碼分析--啟動流程Tomcat原始碼
- centos7 設定tomcat自啟動CentOSTomcat
- 認識Tomcat核心元件及其啟動引數Tomcat元件
- 【Android】【init】解析init程式啟動過程Android
- 詳解Tomcat系列(一)-從原始碼分析Tomcat的啟動Tomcat原始碼
- tomcat無法啟動的解決方法Tomcat
- SpringBoot 使用外部Tomcat方法及啟動原理Spring BootTomcat
- Android 9.0 init 啟動流程Android
- 【Kafka】Kafka-Server-start.sh 啟動指令碼分析(Ver 2.7.2)KafkaServer指令碼
- Angular元件——父元件呼叫子元件方法Angular元件
- OpenHarmony的init程式、init配置與啟動項配置
- tomcat原始碼分析(第二篇 tomcat啟動過程詳解)Tomcat原始碼
- 另一種方式啟動tomcat catalina.sh run 前臺 catalina.sh start 後臺Tomcat
- Tomcat 7 中 NIO 處理分析Tomcat
- springboot2.0使用外部tomcat進行啟動方法Spring BootTomcat
- SpringBootApplication是如何啟動Tomcat的? | 破解SpringBoot Tomcat啟動之謎 !Spring BootAPPTomcat
- maven外掛 tomcat7啟動報錯,異常如下MavenTomcat
- vue 父子元件的方法呼叫Vue元件
- Tomcat原始碼分析 (四)----- Pipeline和ValveTomcat原始碼
- tomcat 啟動失敗Tomcat
- Vue 子元件呼叫父元件方法總結Vue元件
- Tomcat啟動報錯:Error starting static Resources解決方法TomcatError
- Android 8.0 原始碼分析 (四) Activity 啟動Android原始碼
- CentOS 7 下Tomcat啟動超慢的原因及解決方案CentOSTomcat
- vue子元件怎麼呼叫父元件的方法Vue元件
- hooks父元件怎麼呼叫子元件的方法?Hook元件
- 埋坑一: vue中子元件呼叫兄弟元件方法Vue元件
- Ubuntu移除cloud init元件UbuntuCloud元件
- CentOS的System V init啟動指令碼CentOS指令碼
- Linux的啟動過程及init程式Linux
- 深入淺出Tomcat/2 - Tomcat啟動和停止Tomcat
- Tomcat啟動流程簡析Tomcat
- Linux下Tomcat重新啟動LinuxTomcat