Tomcat8.5原始碼分析-StandardHost
StandardHost被啟動過程
1. protected void startInternal() throws LifecycleException {
2.
3. fireLifecycleEvent(CONFIGURE_START_EVENT, null);
4. setState(LifecycleState.STARTING);
5.
6. globalNamingResources.start();//JNDI
7.
8. // Start our defined Services
9. synchronized (servicesLock) {
10. for (int i = 0; i < services.length; i++) {
11. services[i].start();
12. }
13. }
14. }
這段程式碼是StandardServer裡面的,可以看到,裡面啟動了services,然後直接轉到StandService,然後StandardService的
startInternal啟動了StandardEngine程式碼見下圖。
1. protected void startInternal() throws LifecycleException {
2.
3. if(log.isInfoEnabled())
4. log.info(sm.getString("standardService.start.name", this.name));
5. setState(LifecycleState.STARTING);
6.
7. // Start our defined Container first
8. if (engine != null) {
9. synchronized (engine) {
10. engine.start();
11. }
12. }
然後再看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. }
可以看到這個地方有super.startInternal(); 這個super繼承了containerBase這個抽象基礎類,這個抽象基礎類是container介面的實現,我們可以看一下下面這段程式碼:
1. protected synchronized void startInternal() throws LifecycleException {
2.
3. // Start our subordinate components, if any
4. //開始下一級的元件,如果有的話
5. logger = null;
6. getLogger();
7. //如果配置了叢集元件,則啟動
8. Cluster cluster = getClusterInternal();
9. if (cluster instanceof Lifecycle) {
10. ((Lifecycle) cluster).start();
11. }
12. //如果配置了安全元件,則啟動
13. Realm realm = getRealmInternal();
14. if (realm instanceof Lifecycle) {
15. ((Lifecycle) realm).start();
16. }
17.
18. // Start our child containers, if any
19. //catalina構造server例項時,server.xml中如果存在host的子容器context,呼叫addChild方法
20. Container children[] = findChildren();
21. List<Future<Void>> results = new ArrayList<>();
22. for (int i = 0; i < children.length; i++) {
23. results.add(startStopExecutor.submit(new StartChild(children[i])));
24. }
25.
26. boolean fail = false;
27. for (Future<Void> result : results) {
28. try {
29. result.get();
30. } catch (Exception e) {
31. log.error(sm.getString("containerBase.threadedStartFailed"), e);
32. fail = true;
33. }
34.
35. }
36. if (fail) {
37. throw new LifecycleException(
38. sm.getString("containerBase.threadedStartFailed"));
39. }
40.
41. // Start the Valves in our pipeline (including the basic), if any
42. //啟動host所持有的Pipeline元件
43. if (pipeline instanceof Lifecycle)
44. ((Lifecycle) pipeline).start();
45.
46.
47. // Start our thread
48. //開始執行緒,啟動後臺執行緒
49. threadStart();
50.
51. }
- 關注這一句results.add(startStopExecutor.submit(new StartChild(children[i])));這個地方用了執行緒的submit方法,看下面這段程式碼:
private static class StartChild implements Callable<Void> {
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. }
這是StartChild類,可以看到他實現了callable介面,當執行緒使用submit方法時,call()方法會被呼叫,子容器將會呼叫start方法,即child.start(),在這個地方啟動了StandardHost。而且result是一個數組,遍歷啟動子容器,就是說一個容器可以啟動多個子容器,如一個StandardEngine可以有多個StandardHost。可以看到catalina的架構圖很明顯表明了這一點。
StandardHost原始碼分析(部分)
1. protected synchronized void startInternal() throws LifecycleException {
2.
3. // Set error report valve
4. //該類主要用於Tomcat發生異常時輸出錯誤頁面
5. String errorValve = getErrorReportValveClass();
6. //檢視pipleLine裡是否有error report valve
7. if ((errorValve != null) && (!errorValve.equals(""))) {
8. try {
9. boolean found = false;
10. Valve[] valves = getPipeline().getValves();
11. for (Valve valve : valves) {
12. if (errorValve.equals(valve.getClass().getName())) {
13. found = true;
14. break;
15. }
16. }
17. if(!found) {
18. Valve valve =
19. (Valve) Class.forName(errorValve).getConstructor().newInstance();/*如果沒有找到利用反射建立物件*/
20. getPipeline().addValve(valve);/*把valve加到管道里*/
21. }
22. } catch (Throwable t) {
23. ExceptionUtils.handleThrowable(t);
24. log.error(sm.getString(
25. "standardHost.invalidErrorReportValveClass",
26. errorValve), t);
27. }
28. }
29. super.startInternal();//啟動虛擬主機,轉到了ContainBase
30. }
可以看到,這個啟動方法裡面就只是做了兩件事:
1.添加了一個ErrorReportValve
2.呼叫containerBase.StartInternal()
關於containerBase.StartInternal()方法,前面已經添加了程式碼和註釋,不在贅述。
此時有兩種情況
1.catalina構造server例項時,server.xml中如果存在host的子容器context,呼叫addChild方法,此時直接呼叫StandardContext.Start()
2.沒有子容器context,觸發fireLifeCycleEvent(),轉到HostConfig。
把lifecycle的狀態改變了。而此時有監聽器,setState是基礎抽象類lifecycleBase的方法,我們看一下它的相關程式碼。
setState:
1. protected synchronized void setState(LifecycleState state)
2. throws LifecycleException {
3. setStateInternal(state, null, true);
4. }
在看這個setStateInternal:
1. private synchronized void setStateInternal(LifecycleState state,
2. Object data, boolean check) throws LifecycleException {
3.
4. if (log.isDebugEnabled()) {
5. log.debug(sm.getString("lifecycleBase.setState", this, state));
6. }
7.
8. if (check) {
9. // Must have been triggered by one of the abstract methods (assume
10. // code in this class is correct)
11. // null is never a valid state
12. if (state == null) {
13. invalidTransition("null");
14. // Unreachable code - here to stop eclipse complaining about
15. // a possible NPE further down the method
16. return;
17. }
18.
19. // Any method can transition to failed
20. // startInternal() permits STARTING_PREP to STARTING
21. // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
22. // STOPPING
23. if (!(state == LifecycleState.FAILED ||
24. (this.state == LifecycleState.STARTING_PREP &&
25. state == LifecycleState.STARTING) ||
26. (this.state == LifecycleState.STOPPING_PREP &&
27. state == LifecycleState.STOPPING) ||
28. (this.state == LifecycleState.FAILED &&
29. state == LifecycleState.STOPPING))) {
30. // No other transition permitted
31. invalidTransition(state.name());
32. }
33. }
34.
35. this.state = state;
36. String lifecycleEvent = state.getLifecycleEvent();
37. if (lifecycleEvent != null) {
38. fireLifecycleEvent(lifecycleEvent, data);
39. }
40. }
可以在看到在最後觸發了這個生命週期事件。
1. protected void fireLifecycleEvent(String type, Object data) {
2. LifecycleEvent event = new LifecycleEvent(this, type, data);
3. for (LifecycleListener listener : lifecycleListeners) {
4. listener.lifecycleEvent(event);
5. }
6. }
在Digester建立物件時,listener為HostConfig。
接下來我們分析HostConfig的程式碼。
Tomcat8.5原始碼分析-HostConfig