根据Tomcat源码来看一下Tomcat启动过程都做了什么

部分代码为主要流程代码,删去了try-catch以及一些校验逻辑,方便理解主流程

先来一张启动过程时序图,了解一下启动顺序

Tomcat启动的入口类:org.apache.catalina.startup.Bootstrap#main

main方法是整个tomcat启动时的入口。在main方法中,使用bootstrap.init()来初始化类加载器和创建Catalina实例,然后再启动Catalina线程。

  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.  
  27. if (command.equals("startd")) {
  28. args[args.length - 1] = "start";
  29. daemon.load(args);
  30. daemon.start();
  31. } else if (command.equals("stopd")) {
  32. args[args.length - 1] = "stop";
  33. daemon.stop();
  34. } else if (command.equals("start")) {
  35. daemon.setAwait(true);
  36. daemon.load(args);
  37. daemon.start();
  38. } else if (command.equals("stop")) {
  39. daemon.stopServer(args);
  40. } else if (command.equals("configtest")) {
  41. daemon.load(args);
  42. if (null==daemon.getServer()) {
  43. System.exit(1);
  44. }
  45. System.exit(0);
  46. } else {
  47. log.warn("Bootstrap: command \"" + command + "\" does not exist.");
  48. }
  49. } catch (Throwable t) {
  50. // Unwrap the Exception for clearer error reporting
  51. if (t instanceof InvocationTargetException &&
  52. t.getCause() != null) {
  53. t = t.getCause();
  54. }
  55. handleThrowable(t);
  56. t.printStackTrace();
  57. System.exit(1);
  58. }
  59.  
  60. }

bootstrap.init()方法,用于初始化容器相关,首先创建类加载器,然后通过反射创建org.apache.catalina.startup.Catalina实例:

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

之后Bootstrap的demon.start()方法就会调用Catalina的start方法。

Catalina实例执行start方法。这里有两个点,一个是load()加载server.xml配置、初始化Server的过程,一个是getServer().start()开启服务、初始化并开启一系列组件、子容器的过程。

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

load方法解析server.xml配置文件,并加载Server、Service、Connector、Container、Engine、Host、Context、Wrapper一系列的容器。加载完成后,调用getServer().start()来开启一个新的Server。

下面先看load方法怎么加载组件和容器的:

  1. /**
  2. * Start a new server instance.
  3. */
  4. public void load() {
  5.  
  6. long t1 = System.nanoTime();
  7.  
  8. initDirs();
  9.  
  10. // Before digester - it may be needed
  11. initNaming();
  12.  
  13. // Create and execute our Digester
  14. Digester digester = createStartDigester();
  15.  
  16. InputSource inputSource = null;
  17. InputStream inputStream = null;
  18. File file = null;
  19. file = configFile();
  20. inputStream = new FileInputStream(file);
  21. inputSource = new InputSource(file.toURI().toURL().toString());
  22. inputSource.setByteStream(inputStream);
  23. digester.push(this);
  24. digester.parse(inputSource);
  25.  
  26. getServer().setCatalina(this);
  27. getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
  28. getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
  29.  
  30. // Stream redirection
  31. initStreams();
  32.  
  33. // Start the new server
  34. getServer().init();
  35. }

首先利用Digester类解析server.xml文件,得到容器的配置,并创建相应的对象,并关联父子容器。依次创建的是StandardServer、StandardService、StandardEngine、StandardHost。

然后拿到StandardServer实例调用init()方法初始化Tomcat容器的一系列组件。一些容器初始化的的时候,都会调用其子容器的init()方法,初始化它的子容器。顺序是StandardServer、StandardService、StandardEngine、Connector。每个容器都在初始化自身相关设置的同时,将子容器初始化。

这里插入一个Tomcat中生命周期的概念。在初始化、开启一系列组件、容器的过程中,由tomcat'管理的组件和容器,都有一个共同的特点,都实现了org.apache.catalina.Lifecycle接口,由Tomcat管理其生命周期。Lifecycle提供一种统一的管理对象生命周期的接口。通过Lifecycle、LifecycleListener、LifecycleEvent,Catalina实现了对tomcat各种组件、容器统一的启动和停止的方式。

在Tomcat服务开启过程中启动的一些列组件、容器,都继承了org.apache.catalina.util.LifecycleBase这个抽象类,其中的init()、start() 方法、stop() 方法,为其子类实现了统一的start和stop管理。方法中具体的initInternal()、startInternal() 和stopInternal() 方法,交由子类自己实现。

看一下LifecycleBase的init()和start()的实现吧:

org.apache.catalina.util.LifecycleBase#start
  1. public final synchronized void init() throws LifecycleException {
  2. if (!state.equals(LifecycleState.NEW)) {
  3. invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
  4. }
  5.  
  6. try {
  7. setStateInternal(LifecycleState.INITIALIZING, null, false);
  8. initInternal();
  9. setStateInternal(LifecycleState.INITIALIZED, null, false);
  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.  
  18. public final synchronized void start() throws LifecycleException {
  19.  
  20. if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
  21. LifecycleState.STARTED.equals(state)) {
  22.  
  23. if (log.isDebugEnabled()) {
  24. Exception e = new LifecycleException();
  25. log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
  26. } else if (log.isInfoEnabled()) {
  27. log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
  28. }
  29.  
  30. return;
  31. }
  32.  
  33. if (state.equals(LifecycleState.NEW)) {
  34. init();
  35. } else if (state.equals(LifecycleState.FAILED)) {
  36. stop();
  37. } else if (!state.equals(LifecycleState.INITIALIZED) &&
  38. !state.equals(LifecycleState.STOPPED)) {
  39. invalidTransition(Lifecycle.BEFORE_START_EVENT);
  40. }
  41.  
  42. try {
  43. setStateInternal(LifecycleState.STARTING_PREP, null, false);
  44. startInternal();
  45. if (state.equals(LifecycleState.FAILED)) {
  46. stop();
  47. } else if (!state.equals(LifecycleState.STARTING)) {
  48. invalidTransition(Lifecycle.AFTER_START_EVENT);
  49. } else {
  50. setStateInternal(LifecycleState.STARTED, null, false);
  51. }
  52. } catch (Throwable t) {
  53. ExceptionUtils.handleThrowable(t);
  54. setStateInternal(LifecycleState.FAILED, null, false);
  55. throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
  56. }
  57. }

可以看到,init()和start()方法里,调用了initInternal()方法、startInternal()方法和stop()方法,这三者最终会走子类的具体实现。

上面的StandardServer的初始化过程就是一个活生生的例子。在Catalina的load过程中,getServer().init()方法就是LifecycleBase中的init()方法,调用initInternal()时是走的StandardServer的实现,StandardServer的initInternal()中会调用StandardServer的init()方法,进行子容器的初始化。然后依次初始化。

看一下代码,了解一下StandardServer中的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. }

再举一个具体的例子:

回到刚才的启动过程中,getServer().start()开启服务的方法,实际就是上面提到的LifecycleBase中的start()方法。其中,会调用org.apache.catalina.core.StandardServer#initInternal方法,初始化Server并调用Service的init方法。org.apache.catalina.core.StandardServer在其实现的startInternal() 中,开启naming resources和services,调用service的start方法,开启所有service,调用其service的startInternal()方法。

下面看一下StandardServer中的startInternal()的实现:

org.apache.catalina.core.StandardServer#startInternal
  1. protected void startInternal() throws LifecycleException {
  2.  
  3. fireLifecycleEvent(CONFIGURE_START_EVENT, null);
  4. setState(LifecycleState.STARTING);
  5.  
  6. globalNamingResources.start();
  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. }

这里的service,是org.apache.catalina.core.StandardService的实例。

总结一下启动的Tomcat启动的过程

在Catalina的load方法里,就已经调用了StandardServer里的init方法,一层一层初始化了globalNamingResources,StandardService--》StandardEngine,executors,MapperListener,Connector--》CoyoteAdapter,protocolHandler。至此就将tomcat的catalina中的组件、容器初始化完成。 接下来就是调用start方法一层一层开启,StandardServer的startInternal方法,按层次start:globalNamingResources,StandardService--》StandardEngine,executors,MapperListener,Connector--》StandardHost,StandardContext,protocolHandler。顺序基本同init过程。StandardEngine在start时,会init子容器,并调用子容器的start方法。子容器依次这样init、start,就开启了StandardHost和StandardContext。

 

参考文章:

tomcat源码分析-Connector初始化与启动

tomcat源码分析-Container初始化与加载

tomcat源码分析-http请求在Container中的执行路线

tomcat源码解析(一)--启动与Server.xml文件的解析

Tomcat启动过程源码解读的更多相关文章

  1. Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段

    目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...

  2. Flask框架整个流程源码解读

    Flask框架整个流程源码解读 一.总的流程 运行Flask其本质是运行Flask对象中的__call__,而__call__本质调用wsgi_app的方法 wsgi_app方法 def wsgi_a ...

  3. Spark(五十一):Spark On YARN(Yarn-Cluster模式)启动流程源码分析(二)

    上篇<Spark(四十九):Spark On YARN启动流程源码分析(一)>我们讲到启动SparkContext初始化,ApplicationMaster启动资源中,讲解的内容明显不完整 ...

  4. Spring IOC 容器预启动流程源码探析

    Spring IOC 容器预启动流程源码探析 在应用程序中,一般是通过创建ClassPathXmlApplicationContext或AnnotationConfigApplicationConte ...

  5. Android系统默认Home应用程序(Launcher)的启动过程源码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还须要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  6. Android Content Provider的启动过程源码分析

    本文參考Android应用程序组件Content Provider的启动过程源码分析http://blog.csdn.net/luoshengyang/article/details/6963418和 ...

  7. Elasticsearch6.3.2启动过程源码阅读记录

    Elasticsearch6.3.2启动过程源码阅读记录 网上有很多关于es的源码分析,觉得自己技术深度还不够,所以这些文章只是看源码过程中的一个笔记,谈不上分析. 整个启动过程以类名.方法名,按顺序 ...

  8. Android Activity启动流程源码全解析(1)

    前言 Activity是Android四大组件的老大,我们对它的生命周期方法调用顺序都烂熟于心了,可是这些生命周期方法到底是怎么调用的呢?在启动它的时候会用到startActivty这个方法,但是这个 ...

  9. Android Activity启动流程源码全解析(2)

    接上之前的分析 ++Android Activity启动流程源码全解析(1)++ 1.正在运行的Activity调用startPausingLocked 一个一个分析,先来看看startPausing ...

随机推荐

  1. Struts2如何搭建?

    如何搭建Struts2:   1.导入jar包 commons-fileupload-1.3.jar commons-io-2.0.1.jar commons-lang3-3.1.jar freema ...

  2. shell中数组讲解

    定义数组 在Shell中,用括号来表示数组,数组元素用"空格"符号分割开.定义数组的一般形式为: 代码如下: 数组名=(值1 值2 ... 值n) 例如: 代码如下: array_ ...

  3. WTF小程序之wxs

    前言 对于从VUE过来的前端同学来说,见到小程序的第一眼一定是熟悉-感觉就像是把vue的单文件拆成了3个文件.但是,随着慢慢入坑.马上会发现,这样怎么不行?wxs文件又是什么鬼?template和vu ...

  4. JavaScript 基本语法 -- 运算符的优先级

    在所有的运算里,都是有运算顺序的.小时候学四则运算的时候,我们都知道这么一个规则:先乘除后加减,有括号要先算括号! 同样的,在JavaScript里面,运算符也是有相应的优先级的.其优先级如下表所示, ...

  5. Selenium UI自动化解决iframe定位问题

      更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6735116.html 一个阴雨霏霏 ...

  6. Springboot security cas整合方案-原理篇

    前言:网络中关于Spring security整合cas的方案有很多例,对于Springboot security整合cas方案则比较少,且有些仿制下来运行也有些错误,所以博主在此篇详细的分析cas原 ...

  7. Python基础篇(三)

    元组是序列的一种,与列表的区别是,元组是不能修改的. 元组一般是用圆括号括起来进行定义,如下: >>> (1,2,3)[1:2]     (2,) 如果元组中只有一个元素,元组的表示 ...

  8. 【OCR技术系列之四】基于深度学习的文字识别(3755个汉字)

    上一篇提到文字数据集的合成,现在我们手头上已经得到了3755个汉字(一级字库)的印刷体图像数据集,我们可以利用它们进行接下来的3755个汉字的识别系统的搭建.用深度学习做文字识别,用的网络当然是CNN ...

  9. 理解Java Integer的缓存策略【转】

    本文由 ImportNew - 挖坑的张师傅 翻译自 javapapers.欢迎加入翻译小组.转载请见文末要求. 本文将介绍 Java 中 Integer 缓存的相关知识.这是 Java 5 中引入的 ...

  10. selenium自动化测试学习(一)

    在学习selenium自动化测试前,我们需要先了解一点自动化测试的相关知识. (一)什么是自动化测试 (二)为什么要做自动化测试 (三)自动化测试优缺点 (1)什么是自动化测试 自动化测试是把以人为驱 ...