HostConfig
1. public void lifecycleEvent(LifecycleEvent event) {
2.
3. // Identify the host we are associated with
4. try {
5. host = (Host) event.getLifecycle();
6. if (host instanceof StandardHost) {
7. setCopyXML(((StandardHost) host).isCopyXML());
8. setDeployXML(((StandardHost) host).isDeployXML());
9. setUnpackWARs(((StandardHost) host).isUnpackWARs());
10. setContextClass(((StandardHost) host).getContextClass());
11. }
12. } catch (ClassCastException e) {
13. log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
14. return;
15. }
16.
17. // Process the event that has occurred
18. if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
19. check();
20. } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
21. beforeStart();
22. } else if (event.getType().equals(Lifecycle.START_EVENT)) {
23. start();//StandHost.startInternal() 触发了这个事件
24. } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
25. stop();
26. }
27. }
首先这个方法会检验Host是否是我们需要的,然后由于我们改变了生命周期的状态,所以调用它的start()方法。我们接着看代码:
1. public void start() {
2. //进行日志的相关处理和jmx的注册
3. if (log.isDebugEnabled())
4. log.debug(sm.getString("hostConfig.start"));
5.
6. try {
7. ObjectName hostON = host.getObjectName();
8. oname = new ObjectName
9. (hostON.getDomain() + ":type=Deployer,host=" + host.getName());
10. Registry.getRegistry(null, null).registerComponent
11. (this, oname, this.getClass().getName());
12. } catch (Exception e) {
13. log.error(sm.getString("hostConfig.jmx.register", oname), e);
14. }
15.
16. if (!host.getAppBaseFile().isDirectory()) {
17. log.error(sm.getString("hostConfig.appBase", host.getName(),
18. host.getAppBaseFile().getPath()));
19. host.setDeployOnStartup(false);
20. host.setAutoDeploy(false);
21. }
22. /*部署应用*/
23. if (host.getDeployOnStartup())
24. deployApps();//context描述文件的部署,web目录的部署,WAR包的部署
25.
26. }
接着我们看deployApps():
1. protected void deployApps() {
2.
3. File appBase = host.getAppBaseFile();
4. File configBase = host.getConfigBaseFile();
5. String[] filteredAppPaths = filterAppPaths(appBase.list());
6. // Deploy XML descriptors from configBase
7. deployDescriptors(configBase, configBase.list());
8. // Deploy WARs
9. deployWARs(appBase, filteredAppPaths);
10. // Deploy expanded folders
11. deployDirectories(appBase, filteredAppPaths);
12.
13. }
我们只看这一个deployDescriptors描述文件的部署:
1. protected void deployDescriptors(File configBase, String[] files) {
2.
3. if (files == null)
4. return;
5.
6. ExecutorService es = host.getStartStopExecutor();
7. List<Future<?>> results = new ArrayList<>();
8.
9. for (int i = 0; i < files.length; i++) {/*扫描host配置文件的基础目录*/
10. File contextXml = new File(configBase, files[i]);
11.
12. if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) {/*对于每个配置文件*/
13. ContextName cn = new ContextName(files[i], true);
14.
15. if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
16. continue;
17.
18. results.add(
19. es.submit(new DeployDescriptor(this, cn, contextXml)));/*由线程池完成解析部署,调用run()*/
20. }
21. }
22.
23. for (Future<?> result : results) {
24. try {
25. result.get();
26. } catch (Exception e) {
27. log.error(sm.getString(
28. "hostConfig.deployDescriptor.threaded.error"), e);
29. }
30. }
31. }
接着看它的代码:
2. private static class DeployDescriptor implements Runnable {
2.
3. private HostConfig config;
4. private ContextName cn;
5. private File descriptor;
6.
7. public DeployDescriptor(HostConfig config, ContextName cn,
8. File descriptor) {
9. this.config = config;
10. this.cn = cn;
11. this.descriptor= descriptor;
12. }
13.
14. @Override
15. public void run() {
16. config.deployDescriptor(cn, descriptor);/*线程调用方法*/
17. }
18. }
继续看run方法:
1. protected void deployDescriptor(ContextName cn, File contextXml) {
2.
3. DeployedApplication deployedApp =
4. new DeployedApplication(cn.getName(), true);
5.
6. long startTime = 0;
7. // Assume this is a configuration descriptor and deploy it
8. if(log.isInfoEnabled()) {
9. startTime = System.currentTimeMillis();
10. log.info(sm.getString("hostConfig.deployDescriptor",
11. contextXml.getAbsolutePath()));
12. }
13.
14. Context context = null;
15. boolean isExternalWar = false;
16. boolean isExternal = false;
17. File expandedDocBase = null;
18.
19. try (FileInputStream fis = new FileInputStream(contextXml)) {
20. synchronized (digesterLock) {
21. try {
22. context = (Context) digester.parse(fis);/*使用digester创建context实例*/
23. } catch (Exception e) {
24. log.error(sm.getString(
25. "hostConfig.deployDescriptor.error",
26. contextXml.getAbsolutePath()), e);
27. } finally {
28. digester.reset();
29. if (context == null) {
30. context = new FailedContext();
31. }
32. }
33. }
digster解析的时候创建了对象:
1. Class<?> clazz = Class.forName(host.getConfigClass());
2. LifecycleListener listener =
3. (LifecycleListener) clazz.newInstance();
4. context.addLifecycleListener(listener);//为context添加生命周期监听器
5.
6. context.setConfigFile(contextXml.toURI().toURL());//设置context实例的ConfigFile
7. context.setName(cn.getName());//设置context实例的名称
8. context.setPath(cn.getPath());//设置context实例的路径
9. context.setWebappVersion(cn.getVersion());
10. //设置context实例的版本
11. // Add the associated docBase to the redeployed list if it's a WAR
12. if (context.getDocBase() != null) {
13. File docBase = new File(context.getDocBase());
14. if (!docBase.isAbsolute()) {
15. docBase = new File(host.getAppBaseFile(), context.getDocBase());
16. }
17. // If external docBase, register .xml as redeploy first
18. if (!docBase.getCanonicalPath().startsWith(
19. host.getAppBaseFile().getAbsolutePath() + File.separator)) {
20. isExternal = true;
21. deployedApp.redeployResources.put(
22. contextXml.getAbsolutePath(),
23. Long.valueOf(contextXml.lastModified()));
24. deployedApp.redeployResources.put(docBase.getAbsolutePath(),
25. Long.valueOf(docBase.lastModified()));
26. if (docBase.getAbsolutePath().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
27. isExternalWar = true;
28. }
29. } else {
30. log.warn(sm.getString("hostConfig.deployDescriptor.localDocBaseSpecified",
31. docBase));
32. // Ignore specified docBase
33. context.setDocBase(null);
34. }
35. }
36.
37. host.addChild(context);//把context的实例添加到host
38. } catch (Throwable t) {
39. ExceptionUtils.handleThrowable(t);
40. log.error(sm.getString("hostConfig.deployDescriptor.error",
41. contextXml.getAbsolutePath()), t);
42. } finally {
43. // Get paths for WAR and expanded WAR in appBase
44. //获得WAR包的路径并且展开WAR包在appBase里面
45. // default to appBase dir + name
46. expandedDocBase = new File(host.getAppBaseFile(), cn.getBaseName());
47. if (context.getDocBase() != null
48. && !context.getDocBase().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
49. // first assume docBase is absolute
50. //先假定这个docBase的路径是绝对的
51. expandedDocBase = new File(context.getDocBase());
52. if (!expandedDocBase.isAbsolute()) {
53. // if docBase specified and relative, it must be relative to appBase
54. //如果路径是相对的,那么它一定是相对于appBase
55. expandedDocBase = new File(host.getAppBaseFile(), context.getDocBase());
56. }
57. }
58.
59. boolean unpackWAR = unpackWARs;
60. if (unpackWAR && context instanceof StandardContext) {
61. unpackWAR = ((StandardContext) context).getUnpackWAR();
62. }
63.
64. // Add the eventual unpacked WAR and all the resources which will be
65. // watched inside it
66. //添加最终解开的WAR包
67. if (isExternalWar) {
68. if (unpackWAR) {
69. deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
70. Long.valueOf(expandedDocBase.lastModified()));
71. addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), context);
72. } else {
73. addWatchedResources(deployedApp, null, context);
74. }
75. } else {
76. // Find an existing matching war and expanded folder
77. //找到已存在的匹配的war包并且展开文件夹
78. if (!isExternal) {
79. File warDocBase = new File(expandedDocBase.getAbsolutePath() + ".war");
80. if (warDocBase.exists()) {
81. deployedApp.redeployResources.put(warDocBase.getAbsolutePath(),
82. Long.valueOf(warDocBase.lastModified()));
83. } else {
84. // Trigger a redeploy if a WAR is added
85. //触发再次解析如果WAR包被添加进去
86. deployedApp.redeployResources.put(
87. warDocBase.getAbsolutePath(),
88. Long.valueOf(0));
89. }
90. }
91. if (unpackWAR) {
92. deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
93. Long.valueOf(expandedDocBase.lastModified()));
94. addWatchedResources(deployedApp,
95. expandedDocBase.getAbsolutePath(), context);
96. } else {
97. addWatchedResources(deployedApp, null, context);
98. }
99. if (!isExternal) {
100. // For external docBases, the context.xml will have been
101. // added above.
102. deployedApp.redeployResources.put(
103. contextXml.getAbsolutePath(),
104. Long.valueOf(contextXml.lastModified()));
105. }
106. }
107. // Add the global redeploy resources (which are never deleted) at
108. // the end so they don't interfere with the deletion process
109. addGlobalRedeployResources(deployedApp);
110. }
111.
112. if (host.findChild(context.getName()) != null) {
113. deployed.put(context.getName(), deployedApp);
114. }
115.
116. if (log.isInfoEnabled()) {
117. log.info(sm.getString("hostConfig.deployDescriptor.finished",
118. contextXml.getAbsolutePath(), Long.valueOf(System.currentTimeMillis() - startTime)));
119. }
120. }
再看这个方法:
1. public void addChild(Container child) {
2.
3. child.addLifecycleListener(new MemoryLeakTrackingListener());
4.
5. if (!(child instanceof Context))
6. throw new IllegalArgumentException
7. (sm.getString("standardHost.notContext"));
8. super.addChild(child);/*最终会调用child.start()方法,在containerBase里面*/
9.
10. }
再看super.addChild():
public void addChild(Container child) {
2. if (Globals.IS_SECURITY_ENABLED) {
3. PrivilegedAction<Void> dp =
4. new PrivilegedAddChild(child);
5. AccessController.doPrivileged(dp);
6. } else {
7. addChildInternal(child);
8. }
9. }
其实不管怎么样都会调用addChildInternal(child),
继续看这个里面的代码:
1. private void addChildInternal(Container child) {
2.
3. if( log.isDebugEnabled() )
4. log.debug("Add child " + child + " " + this);
5. synchronized(children) {
6. if (children.get(child.getName()) != null)
7. throw new IllegalArgumentException("addChild: Child name '" +
8. child.getName() +
9. "' is not unique");
10. child.setParent(this); // May throw IAE
11. children.put(child.getName(), child);
12. }
13.
14. // Start child
15. // Don't do this inside sync block - start can be a slow process and
16. // locking the children object can cause problems elsewhere
17. try {
18. if ((getState().isAvailable() ||
19. LifecycleState.STARTING_PREP.equals(getState())) &&
20. startChildren) {
21. child.start();/*在这个地方调用的*/
22. }
23. } catch (LifecycleException e) {
24. log.error("ContainerBase.addChild: start: ", e);
25. throw new IllegalStateException("ContainerBase.addChild: start: " + e);
26. } finally {
27. fireContainerEvent(ADD_CHILD_EVENT, child);
28. }
29. }
可以看到最终child.start(),在这个地方终于转向了StandardContext,
其他两个部署方法省略,感兴趣可以查看相关资料。
Tomcat8.5源码分析-StandardContext