[关闭]
@w1992wishes 2018-02-23T13:54:54.000000Z 字数 17600 阅读 951

Tomcat源码分析 -- Tomcat的启动过程(二)

源码分析 tomcat


本篇结构:

一、前言

上一篇中介绍了startup.bat和catalina.bat脚本。了解到日常双击startup.bat启动tomcat,其实是来到catalina.bat脚本中,由catalina.bat脚本去执行org.apache.catalina.startup.Bootstrap这个类中的main方法。

这里就来看看Bootstrap类的main方法做了些什么。

二、总览一下main方法

  1. public static void main(String args[]) {
  2. //上部分
  3. if (daemon == null) {
  4. // Don't set daemon until init() has completed
  5. Bootstrap bootstrap = new Bootstrap();
  6. try {
  7. bootstrap.init();
  8. } catch (Throwable t) {
  9. handleThrowable(t);
  10. t.printStackTrace();
  11. return;
  12. }
  13. daemon = bootstrap;
  14. } else {
  15. // When running as a service the call to stop will be on a new
  16. // thread so make sure the correct class loader is used to prevent
  17. // a range of class not found exceptions.
  18. Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
  19. }
  20. //下部分
  21. try {
  22. String command = "start";
  23. if (args.length > 0) {
  24. command = args[args.length - 1];
  25. }
  26. if (command.equals("startd")) {
  27. args[args.length - 1] = "start";
  28. daemon.load(args);
  29. daemon.start();
  30. } else if (command.equals("stopd")) {
  31. args[args.length - 1] = "stop";
  32. daemon.stop();
  33. } else if (command.equals("start")) {
  34. daemon.setAwait(true);
  35. daemon.load(args);
  36. daemon.start();
  37. } else if (command.equals("stop")) {
  38. daemon.stopServer(args);
  39. } else if (command.equals("configtest")) {
  40. daemon.load(args);
  41. if (null==daemon.getServer()) {
  42. System.exit(1);
  43. }
  44. System.exit(0);
  45. } else {
  46. log.warn("Bootstrap: command \"" + command + "\" does not exist.");
  47. }
  48. } catch (Throwable t) {
  49. // Unwrap the Exception for clearer error reporting
  50. if (t instanceof InvocationTargetException &&
  51. t.getCause() != null) {
  52. t = t.getCause();
  53. }
  54. handleThrowable(t);
  55. t.printStackTrace();
  56. System.exit(1);
  57. }
  58. }

可以看出main方法大致可以分为两部分。

上部分是实例化一个Bootstrap对象,并调用init方法,然后赋值给daemon变量,当然如果daemon已经不是空了,说明已经初始化过了,就将daemon.catalinaLoader直接设置到当前线程(daemon.catalinaLoader是用来加载tomcat内部服务器所需类的类加载器)。

下部分是根据传递进来的参数决定走哪一步,当双击startup.bat时,传进来的是start,所以会来到这段:

  1. else if (command.equals("start")) {
  2. daemon.setAwait(true);
  3. daemon.load(args);
  4. daemon.start();
  5. }

这里主要是调用三个方法,setAwait,load和start。

所以对于Bootstrap重要关注的就是init,setAwait,load和start这四个方法。

三、init

  1. public void init() throws Exception {
  2. initClassLoaders();
  3. Thread.currentThread().setContextClassLoader(catalinaLoader);
  4. SecurityClassLoad.securityClassLoad(catalinaLoader);
  5. // Load our startup class and call its process() method
  6. if (log.isDebugEnabled())
  7. log.debug("Loading startup class");
  8. Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
  9. Object startupInstance = startupClass.getConstructor().newInstance();
  10. // Set the shared extensions class loader
  11. if (log.isDebugEnabled())
  12. log.debug("Setting startup class properties");
  13. String methodName = "setParentClassLoader";
  14. Class<?> paramTypes[] = new Class[1];
  15. paramTypes[0] = Class.forName("java.lang.ClassLoader");
  16. Object paramValues[] = new Object[1];
  17. paramValues[0] = sharedLoader;
  18. Method method =
  19. startupInstance.getClass().getMethod(methodName, paramTypes);
  20. method.invoke(startupInstance, paramValues);
  21. catalinaDaemon = startupInstance;
  22. }

init方法调用initClassLoaders初始化类加载器,然后将初始化好的catalinaLoader设置到当前线程,接着通过反射调用org.apache.catalina.startup.Catalina类的setParentClassLoader,将sharedLoader传入。

3.1、首先看initClassLoaders方法:

  1. private void initClassLoaders() {
  2. try {
  3. commonLoader = createClassLoader("common", null);
  4. if( commonLoader == null ) {
  5. // no config file, default to this loader - we might be in a 'single' env.
  6. commonLoader=this.getClass().getClassLoader();
  7. }
  8. catalinaLoader = createClassLoader("server", commonLoader);
  9. sharedLoader = createClassLoader("shared", commonLoader);
  10. } catch (Throwable t) {
  11. handleThrowable(t);
  12. log.error("Class loader creation threw exception", t);
  13. System.exit(1);
  14. }
  15. }

在类加载器篇中有提到,这里初始化三个类加载器,分别是commonLoader,catalinaLoader,sharedLoader并建立他们之间的关系,catalinaLoader和sharedLoader的parent是commonLoader。

这三个类加载器都是通过createClassLoader建立的:

  1. private ClassLoader createClassLoader(String name, ClassLoader parent)
  2. throws Exception {
  3. String value = CatalinaProperties.getProperty(name + ".loader");
  4. if ((value == null) || (value.equals("")))
  5. return parent;
  6. value = replace(value);
  7. List<Repository> repositories = new ArrayList<>();
  8. String[] repositoryPaths = getPaths(value);
  9. for (String repository : repositoryPaths) {
  10. // Check for a JAR URL repository
  11. try {
  12. @SuppressWarnings("unused")
  13. URL url = new URL(repository);
  14. repositories.add(
  15. new Repository(repository, RepositoryType.URL));
  16. continue;
  17. } catch (MalformedURLException e) {
  18. // Ignore
  19. }
  20. // Local repository
  21. if (repository.endsWith("*.jar")) {
  22. repository = repository.substring
  23. (0, repository.length() - "*.jar".length());
  24. repositories.add(
  25. new Repository(repository, RepositoryType.GLOB));
  26. } else if (repository.endsWith(".jar")) {
  27. repositories.add(
  28. new Repository(repository, RepositoryType.JAR));
  29. } else {
  30. repositories.add(
  31. new Repository(repository, RepositoryType.DIR));
  32. }
  33. }
  34. return ClassLoaderFactory.createClassLoader(repositories, parent);
  35. }

createClassLoader首先回去读取CatalinaProperties中的common.loader,server.loader,shared.loader三个属性,进入CatalinaProperties类中会发现这三个属性来自conf/catalina.properties文件。

接着往下createClassLoader会将common.loader,server.loader,shared.loader三个属性中的值获取然后解析成Repository,然后交给ClassLoaderFactory.createClassLoader方法去创建类加载器,最后可以实现三个不同的类加载器分别加载不同目录下的类。

当然要说清楚的是,默认情况下,catalina.properties中server.loader,shared.loader并没有配置值,三个类加载是同一个,默认加载{catalina.home}/lib目录下的类和jar包。

如果想配置对所有web应用都可见但对tomcat内部服务器不可见的类,此时应该在catalina.properties文件中的shared.loader下进行配置。

Thread.currentThread().setContextClassLoader(catalinaLoader)将catalinaLoader设置为Tomcat主线程的线程上下文类加载器。

3.2、SecurityClassLoad.securityClassLoad(catalinaLoader)

SecurityClassLoad.securityClassLoad用于线程安全的加载tomcat容器所需的class。

当然,要使这个方法真正起作用,需要启动tomcat安全管理器,由代码可知:

  1. public static void securityClassLoad(ClassLoader loader) throws Exception {
  2. securityClassLoad(loader, true);
  3. }
  4. static void securityClassLoad(ClassLoader loader, boolean requireSecurityManager)
  5. throws Exception {
  6. if (requireSecurityManager && System.getSecurityManager() == null) {
  7. return;
  8. }
  9. loadCorePackage(loader);
  10. loadCoyotePackage(loader);
  11. loadLoaderPackage(loader);
  12. loadRealmPackage(loader);
  13. loadServletsPackage(loader);
  14. loadSessionPackage(loader);
  15. loadUtilPackage(loader);
  16. loadJavaxPackage(loader);
  17. loadConnectorPackage(loader);
  18. loadTomcatPackage(loader);
  19. }

如果没启用安全管理器,System.getSecurityManager()=null,直接return。

可以通过命令行的方式启功安全管理器:
catalina.bat run -security或者startup.bat -security

一旦启动了安全管理器,就会根据conf/catalina.policy文件定义的提供默认的安全策略,securityClassLoad方法中System.getSecurityManager()不再等于null,于是就会去执行一系列加载方法,将tomcat的class加载进来。

想了解to吗tomcat的安全策略,可以参考下这篇博文:

你很少使用的安全管理SecurityManager

3.3、通过反射实例化catalina

  1. Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
  2. Object startupInstance = startupClass.getConstructor().newInstance();
  3. // Set the shared extensions class loader
  4. String methodName = "setParentClassLoader";
  5. Class<?> paramTypes[] = new Class[1];
  6. paramTypes[0] = Class.forName("java.lang.ClassLoader");
  7. Object paramValues[] = new Object[1];
  8. paramValues[0] = sharedLoader;
  9. Method method =
  10. startupInstance.getClass().getMethod(methodName, paramTypes);
  11. method.invoke(startupInstance, paramValues);
  12. catalinaDaemon = startupInstance;

可以看到这部分代码是通过反射来实例化catalina,然后反射调用setParentClassLoader将sharedLoader传入到catalina实例中。

为什么不直接通过Bootstrap类直接启动tomcat,而是通过反射生成Catalina实例启动?

再查看tomcat目录结构时应该发现,Bootstrap并不在CATALINA_HOME/bin目录中,Bootstrap和Catalina松耦合(通过反射调用Catalina),它直接依赖JRE运行并为Tomcat应用服务器创建共享类加载器,用于构造Catalina和整个tomcat服务器。实现了启动入口和核心环境的解耦,简化了启动。

四、setAwait

来看setAwait:

  1. public void setAwait(boolean await)
  2. throws Exception {
  3. Class<?> paramTypes[] = new Class[1];
  4. paramTypes[0] = Boolean.TYPE;
  5. Object paramValues[] = new Object[1];
  6. paramValues[0] = Boolean.valueOf(await);
  7. Method method =
  8. catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
  9. method.invoke(catalinaDaemon, paramValues);
  10. }

通过反射调用catalina的setAwait方法,其设置的值是留给后面用的,当Catalina将Tomcat的所有组件启动之后,会检查await属性,如果为true,会调用Catalina.await(),而Catalina.await()又会调用其内部的Server的await()。

  1. public void start() {
  2. ...
  3. if (await) {
  4. await();
  5. stop();
  6. }
  7. }

Server.await()包含一个while循环,此循环用于监听指定socket端口(默认为8005)的连接,当某个连接传入的参数为”SHUTDOWN”(默认为”SHUTDOWN”)时,终止此while循环(端口号和终止while循环的参数,在server.xml的Server标签设置)。

Server.await()用来维持Bootstrap的main方法(main thread)处于运行状态,而线程池中监听http请求的线程是守护线程(daemon thread)。

当Tomcat的指定端口接收到关闭命令时,Server.await()内的while循环终止,然后Catalina会调用stop()方法,关闭Tomcat的所有组件,最终Bootstrap的main thread终止,Tomcat关闭。

五、load

  1. private void load(String[] arguments)
  2. throws Exception {
  3. // Call the load() method
  4. String methodName = "load";
  5. Object param[];
  6. Class<?> paramTypes[];
  7. if (arguments==null || arguments.length==0) {
  8. paramTypes = null;
  9. param = null;
  10. } else {
  11. paramTypes = new Class[1];
  12. paramTypes[0] = arguments.getClass();
  13. param = new Object[1];
  14. param[0] = arguments;
  15. }
  16. Method method =
  17. catalinaDaemon.getClass().getMethod(methodName, paramTypes);
  18. if (log.isDebugEnabled())
  19. log.debug("Calling startup class " + method);
  20. method.invoke(catalinaDaemon, param);
  21. }

该方法通过反射调用catalina的load方法。

catalina的load方法首先初始化目录(initDirs)和初始化命名服务(initNaming),然后是createStartDigester(要了解这个方法,应该先了解一下Digester,它是apache的一个开源组件,通过它可以很方便的从xml文件生成java对象),该方法初始化Digester,为Xml的标签即解析模式增加处理规则rule。

关于Digester,可以参考这篇博文:

利用Digester解析xml文件

来看createStartDigester方法:

  1. protected Digester createStartDigester() {
  2. long t1=System.currentTimeMillis();
  3. // Initialize the digester
  4. Digester digester = new Digester();
  5. digester.setValidating(false);
  6. digester.setRulesValidation(true);
  7. Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
  8. List<String> attrs = new ArrayList<>();
  9. attrs.add("className");
  10. fakeAttributes.put(Object.class, attrs);
  11. digester.setFakeAttributes(fakeAttributes);
  12. digester.setUseContextClassLoader(true);
  13. // Configure the actions we will be using
  14. digester.addObjectCreate("Server",
  15. "org.apache.catalina.core.StandardServer",
  16. "className");
  17. digester.addSetProperties("Server");
  18. digester.addSetNext("Server",
  19. "setServer",
  20. "org.apache.catalina.Server");
  21. digester.addObjectCreate("Server/GlobalNamingResources",
  22. "org.apache.catalina.deploy.NamingResourcesImpl");
  23. digester.addSetProperties("Server/GlobalNamingResources");
  24. digester.addSetNext("Server/GlobalNamingResources",
  25. "setGlobalNamingResources",
  26. "org.apache.catalina.deploy.NamingResourcesImpl");
  27. digester.addObjectCreate("Server/Listener",
  28. null, // MUST be specified in the element
  29. "className");
  30. digester.addSetProperties("Server/Listener");
  31. digester.addSetNext("Server/Listener",
  32. "addLifecycleListener",
  33. "org.apache.catalina.LifecycleListener");
  34. digester.addObjectCreate("Server/Service",
  35. "org.apache.catalina.core.StandardService",
  36. "className");
  37. digester.addSetProperties("Server/Service");
  38. digester.addSetNext("Server/Service",
  39. "addService",
  40. "org.apache.catalina.Service");
  41. digester.addObjectCreate("Server/Service/Listener",
  42. null, // MUST be specified in the element
  43. "className");
  44. digester.addSetProperties("Server/Service/Listener");
  45. digester.addSetNext("Server/Service/Listener",
  46. "addLifecycleListener",
  47. "org.apache.catalina.LifecycleListener");
  48. //Executor
  49. digester.addObjectCreate("Server/Service/Executor",
  50. "org.apache.catalina.core.StandardThreadExecutor",
  51. "className");
  52. digester.addSetProperties("Server/Service/Executor");
  53. digester.addSetNext("Server/Service/Executor",
  54. "addExecutor",
  55. "org.apache.catalina.Executor");
  56. digester.addRule("Server/Service/Connector",
  57. new ConnectorCreateRule());
  58. digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(
  59. new String[]{"executor", "sslImplementationName", "protocol"}));
  60. digester.addSetNext("Server/Service/Connector",
  61. "addConnector",
  62. "org.apache.catalina.connector.Connector");
  63. digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
  64. "org.apache.tomcat.util.net.SSLHostConfig");
  65. digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
  66. digester.addSetNext("Server/Service/Connector/SSLHostConfig",
  67. "addSslHostConfig",
  68. "org.apache.tomcat.util.net.SSLHostConfig");
  69. digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
  70. new CertificateCreateRule());
  71. digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
  72. new SetAllPropertiesRule(new String[]{"type"}));
  73. digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
  74. "addCertificate",
  75. "org.apache.tomcat.util.net.SSLHostConfigCertificate");
  76. digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
  77. "org.apache.tomcat.util.net.openssl.OpenSSLConf");
  78. digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
  79. digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
  80. "setOpenSslConf",
  81. "org.apache.tomcat.util.net.openssl.OpenSSLConf");
  82. digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
  83. "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
  84. digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
  85. digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
  86. "addCmd",
  87. "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
  88. digester.addObjectCreate("Server/Service/Connector/Listener",
  89. null, // MUST be specified in the element
  90. "className");
  91. digester.addSetProperties("Server/Service/Connector/Listener");
  92. digester.addSetNext("Server/Service/Connector/Listener",
  93. "addLifecycleListener",
  94. "org.apache.catalina.LifecycleListener");
  95. digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
  96. null, // MUST be specified in the element
  97. "className");
  98. digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
  99. digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
  100. "addUpgradeProtocol",
  101. "org.apache.coyote.UpgradeProtocol");
  102. // Add RuleSets for nested elements
  103. digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
  104. digester.addRuleSet(new EngineRuleSet("Server/Service/"));
  105. digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
  106. digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
  107. addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
  108. digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
  109. // When the 'engine' is found, set the parentClassLoader.
  110. digester.addRule("Server/Service/Engine",
  111. new SetParentClassLoaderRule(parentClassLoader));
  112. addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");
  113. long t2=System.currentTimeMillis();
  114. if (log.isDebugEnabled()) {
  115. log.debug("Digester for server.xml created " + ( t2-t1 ));
  116. }
  117. return digester;
  118. }

该方法初始化digester,创建一系列解析规则,然后在load方法中会调用:

  1. digester.parse(inputSource);

可见digester解析的源是inputSource,而inputSource是来自于conf/server.xml:

  1. file = configFile();
  2. inputStream = new FileInputStream(file);
  3. inputSource = new InputSource(file.toURI().toURL().toString());

configFile:

  1. protected File configFile() {
  2. //protected String configFile = "conf/server.xml";
  3. File file = new File(configFile);
  4. if (!file.isAbsolute()) {
  5. file = new File(Bootstrap.getCatalinaBase(), configFile);
  6. }
  7. return file;
  8. }

这样便创建出了StandardServer对象,接着便调用getServer().init();

init方法来自StandardServer的父类LifecycleBase:

  1. public final synchronized void init() throws LifecycleException {
  2. if (!state.equals(LifecycleState.NEW)) {
  3. invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
  4. }
  5. try {
  6. setStateInternal(LifecycleState.INITIALIZING, null, false);
  7. initInternal();
  8. setStateInternal(LifecycleState.INITIALIZED, null, false);
  9. } catch (Throwable t) {
  10. handleSubClassException(t, "lifecycleBase.initFail", toString());
  11. }
  12. }

具体实现是在子类的initInternal方法中,在调用initInternal方法前后都会设置状态,LifecycleState.INITIALIZING代表正在初始化,LifecycleState.INITIALIZED表示初始化完成,相应会触发生命周期事件。

在StandardServer的initInternal方法中会调用子组件Services的init方法,并依次传递下去,完成所有组件的init。

可见catalina的load方法主要是根据conf/server.xml配置文件利用Digester创建服务器组件,然后调用Server的init方法,逐层次的实现所有组件的初始化。

六、start

最后看下start方法:

  1. public void start()
  2. throws Exception {
  3. if( catalinaDaemon==null ) init();
  4. Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
  5. method.invoke(catalinaDaemon, (Object [])null);
  6. }

同样也是通过反射调用catalina的start方法:

  1. public void start() {
  2. if (getServer() == null) {
  3. load();
  4. }
  5. if (getServer() == null) {
  6. log.fatal("Cannot start server. Server instance is not configured.");
  7. return;
  8. }
  9. long t1 = System.nanoTime();
  10. // Start the new server
  11. try {
  12. getServer().start();
  13. } catch (LifecycleException e) {
  14. log.fatal(sm.getString("catalina.serverStartFail"), e);
  15. try {
  16. getServer().destroy();
  17. } catch (LifecycleException e1) {
  18. log.debug("destroy() failed for failed Server ", e1);
  19. }
  20. return;
  21. }
  22. long t2 = System.nanoTime();
  23. if(log.isInfoEnabled()) {
  24. log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
  25. }
  26. // Register shutdown hook
  27. if (useShutdownHook) {
  28. if (shutdownHook == null) {
  29. shutdownHook = new CatalinaShutdownHook();
  30. }
  31. Runtime.getRuntime().addShutdownHook(shutdownHook);
  32. // If JULI is being used, disable JULI's shutdown hook since
  33. // shutdown hooks run in parallel and log messages may be lost
  34. // if JULI's hook completes before the CatalinaShutdownHook()
  35. LogManager logManager = LogManager.getLogManager();
  36. if (logManager instanceof ClassLoaderLogManager) {
  37. ((ClassLoaderLogManager) logManager).setUseShutdownHook(
  38. false);
  39. }
  40. }
  41. if (await) {
  42. await();
  43. stop();
  44. }
  45. }

该方法主要触发StandardServer的start方法,StandardServer的start方法同init方法一样来自LifecycleBase,主要是改变生命周期的状态,同时触发相应的生命周期时间,具体的执行逻辑交由具体的子类startInternal方法实现:

  1. public final synchronized void start() throws LifecycleException {
  2. if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
  3. LifecycleState.STARTED.equals(state)) {
  4. return;
  5. }
  6. if (state.equals(LifecycleState.NEW)) {
  7. init();
  8. } else if (state.equals(LifecycleState.FAILED)) {
  9. stop();
  10. } else if (!state.equals(LifecycleState.INITIALIZED) &&
  11. !state.equals(LifecycleState.STOPPED)) {
  12. invalidTransition(Lifecycle.BEFORE_START_EVENT);
  13. }
  14. try {
  15. setStateInternal(LifecycleState.STARTING_PREP, null, false);
  16. startInternal();
  17. if (state.equals(LifecycleState.FAILED)) {
  18. // This is a 'controlled' failure. The component put itself into the
  19. // FAILED state so call stop() to complete the clean-up.
  20. stop();
  21. } else if (!state.equals(LifecycleState.STARTING)) {
  22. // Shouldn't be necessary but acts as a check that sub-classes are
  23. // doing what they are supposed to.
  24. invalidTransition(Lifecycle.AFTER_START_EVENT);
  25. } else {
  26. setStateInternal(LifecycleState.STARTED, null, false);
  27. }
  28. } catch (Throwable t) {
  29. // This is an 'uncontrolled' failure so put the component into the
  30. // FAILED state and throw an exception.
  31. handleSubClassException(t, "lifecycleBase.startFail", toString());
  32. }
  33. }

在StandardServer的startInternal方法中会调用子组件service的start方法,并依次调用其他组件的start方法。

  1. protected void startInternal() throws LifecycleException {
  2. fireLifecycleEvent(CONFIGURE_START_EVENT, null);
  3. setState(LifecycleState.STARTING);
  4. globalNamingResources.start();
  5. // Start our defined Services
  6. synchronized (servicesLock) {
  7. for (int i = 0; i < services.length; i++) {
  8. services[i].start();
  9. }
  10. }
  11. }

所以同load方法很相似,start方法主要是实现各组件的start方法依次调用,可以用一张图来理解:

还应该看到Catalina的start方法会使用前面的setAwait方法传递的值,为true时,会在8005端口监听,保证主线程一直在运行,直到收到SHUTDOWN命令。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注