首先,说明tomcat8和tomcat7的启动过程不一样,这篇是针对tomcat7的。

Tomcat启动的总过程

通过上面的介绍,我们总体上清楚了各个组件的生命周期的各个阶段具体都是如何运作的。接下来我们就来看看,Tomcat具体是如何一步步启动起来的。我们都知道任何Java程序都有一个main函数入口,Tomcat中的main入口是org.apache.catalina.startup.Bootstrap#main,下面我们就来分析一下它的代码:

org.apache.catalina.startup.Bootstrap#main

public static void main(String args[]) {if (daemon == null) {// Don't set daemon until init() has completed// 1 Bootstrap bootstrap = new Bootstrap();try {// 2
            bootstrap.init();} catch (Throwable t) {handleThrowable(t);t.printStackTrace();return;}// 3daemon = bootstrap;} else {// When running as a service the call to stop will be on a new// thread so make sure the correct class loader is used to prevent// a range of class not found exceptions.
        Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);}try {String command = "start";if (args.length > 0) {command = args[args.length - 1];}if (command.equals("startd")) {args[args.length - 1] = "start";daemon.load(args);daemon.start();} else if (command.equals("stopd")) {args[args.length - 1] = "stop";daemon.stop();} else if (command.equals("start")) {// 4daemon.setAwait(true);daemon.load(args);daemon.start();} else if (command.equals("stop")) {daemon.stopServer(args);} else if (command.equals("configtest")) {daemon.load(args);if (null==daemon.getServer()) {System.exit(1);}System.exit(0);} else {log.warn("Bootstrap: command \"" + command + "\" does not exist.");}} catch (Throwable t) {// Unwrap the Exception for clearer error reportingif (t instanceof InvocationTargetException &&t.getCause() != null) {t = t.getCause();}handleThrowable(t);t.printStackTrace();System.exit(1);}}

下面我们逐一来分析一下上述代码中标注了数字的地方:

  1. 标注1的代码初始化了自举类的实例,标注2的代码对BootStrap实例进行了初始化,标注3的代码将实例赋值给了daemon。
  2. 标注4的代码首先调用了BootStrap的load方法,然后调用了start方法。

接下来我们分别分析一下BootStrap的init,load,start方法具体做了哪些工作。

BootStrap#init方法

首先来看org.apache.catalina.startup.Bootstrap#init方法,它的代码如下:

org.apache.catalina.startup.Bootstrap#init

public void init()throws Exception{// Set Catalina path
    setCatalinaHome();setCatalinaBase();initClassLoaders();Thread.currentThread().setContextClassLoader(catalinaLoader);SecurityClassLoad.securityClassLoad(catalinaLoader);// Load our startup class and call its process() methodif (log.isDebugEnabled())log.debug("Loading startup class");// 1Class<?> startupClass =catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");Object startupInstance = startupClass.newInstance();// Set the shared extensions class loaderif (log.isDebugEnabled())log.debug("Setting startup class properties");String methodName = "setParentClassLoader";Class<?> paramTypes[] = new Class[1];paramTypes[0] = Class.forName("java.lang.ClassLoader");Object paramValues[] = new Object[1];paramValues[0] = sharedLoader;Method method =startupInstance.getClass().getMethod(methodName, paramTypes);// 2
    method.invoke(startupInstance, paramValues);// 3catalinaDaemon = startupInstance;}

下面我们重点逐一来分析一下上述代码中标注了数字的地方:

  1. 标注1的代码通过反射实例化了org.apache.catalina.startup.Catalina类的实例;
  2. 标注2的代码调用了Catalina实例的setParentClassLoader方法设置了父亲ClassLoader,对于ClassLoader方面的内容,我们在本系列的后续文章再来看看。标注3的代码将Catalina实例赋值给了Bootstrap实例的catalinaDaemon.

BootStrap#load

接下来我们再来看看org.apache.catalina.startup.Bootstrap#load方法,通过查看源代码,我们知道此方法通过反射调用了org.apache.catalina.startup.Catalina#load方法,那我们就来看看Catalina的load方法,Catalina#load方法代码如下:

org.apache.catalina.startup.Catalina#load

public void load() {// 1 Digester digester = createStartDigester();InputSource inputSource = null;InputStream inputStream = null;File file = null;try {file = configFile();inputStream = new FileInputStream(file);inputSource = new InputSource(file.toURI().toURL().toString());} catch (Exception e) {if (log.isDebugEnabled()) {log.debug(sm.getString("catalina.configFail", file), e);}}try {inputSource.setByteStream(inputStream);digester.push(this);digester.parse(inputSource);} catch (SAXParseException spe) {log.warn("Catalina.start using " + getConfigFile() + ": " +spe.getMessage());return;} catch (Exception e) {log.warn("Catalina.start using " + getConfigFile() + ": " , e);return;} finally {try {inputStream.close();} catch (IOException e) {// Ignore
        }}getServer().setCatalina(this);// Stream redirection
    initStreams();// Start the new servertry {// 2
        getServer().init();} catch (LifecycleException e) {if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {throw new java.lang.Error(e);} else {log.error("Catalina.start", e);}}}

上面的代码,我只保留了主流程核心的代码,下面我们重点逐一来分析一下上述代码中标注了数字的地方:

  1. 标注1的代码创建Digester实例解析”conf/server.xml”文件
  2. 标注2的代码最终调用了StandardServer的init方法。

大家可以自行查看下源代码,我们会发现如下的一个调用流程:

init call stack

org.apache.catalina.core.StandardServer#init
->org.apache.catalina.core.StandardService#init
-->org.apache.catalina.connector.Connector#init
-->org.apache.catalina.core.StandardEngine#init

因为StandardService,Connector,StandardEngine实现了LifeCycle接口,因此符合我们上文所获的生命周期的管理,最终都是通过他们自己实现的initInternal方法进行初始化

读到这里的时候,我想大家应该和我一样,以为StandardEngine#init方法会调用StandardHost#init方法,但是当我们查看StandardEngine#init方法的时候,发现并没有进行StandardHost的初始化,它到底做了什么呢?让我们来具体分析一下,我们首先拿StanderEngine的继承关系图来看下:通过上图以及前面说的LifeCyecle的模板方法模式,我们知道StandardEngine的初始化钩子方法initInternal方法最终调用了ContainerBase的initInternal方法,那我们拿ContainerBase#initInternal方法的代码看看:

org.apache.catalina.core.ContainerBase#initInternal

protected void initInternal() throws LifecycleException {BlockingQueue<Runnable> startStopQueue =new LinkedBlockingQueue<Runnable>();startStopExecutor = new ThreadPoolExecutor(getStartStopThreadsInternal(),getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,startStopQueue,new StartStopThreadFactory(getName() + "-startStop-"));startStopExecutor.allowCoreThreadTimeOut(true);super.initInternal();
}

我们可以看到StandardEngine的初始化仅仅是创建了一个ThreadPoolExecutor,当看到这里的时候,笔者当时也纳闷了,StandardEngine#init竟然没有调用StandardHost#init方法,那么StandardHost的init方法是什么时候被调用的呢?遇到这种不知道到底方法怎么调用的时候怎么办呢?笔者介绍个方法给大家。我们现在需要知道StandardHost#init方法何时被调用的,而我们知道init最终会调用钩子的initInternal方法,因此这个时候,我们可以在StandardHost中override initInternal方法,增加了实现方法以后,有两种方法可以用,一种就是设置个断点debug一下就可以看出线程调用栈了,另外一种就是在新增的方法中打印出调用栈。笔者这里采用第二种方法,我们增加如下的initInternal方法到StandardHost中:

org.apache.catalina.core.StandardHost#initInternal

protected void initInternal() throws LifecycleException {Throwable ex = new Throwable();StackTraceElement[] stackElements = ex.getStackTrace();if (stackElements != null) {for (int i = stackElements.length - 1; i >= 0; i--) {System.out.print(stackElements[i].getClassName() + "\t");System.out.print(stackElements[i].getMethodName() + "\t");System.out.print(stackElements[i].getFileName() + "\t");System.out.println(stackElements[i].getLineNumber());}}super.initInternal();
}

上面的代码将会打印出方法调用堆栈,对于调试非常有用,上面的方法运行以后在控制台打印出了如下的堆栈信息:

stack info

java.lang.Thread    run  Thread.java   680
java.util.concurrent.ThreadPoolExecutor$Worker run  ThreadPoolExecutor.java   918
java.util.concurrent.ThreadPoolExecutor$Worker runTask  ThreadPoolExecutor.java   895
java.util.concurrent.FutureTask   run  FutureTask.java   138
java.util.concurrent.FutureTask$Sync   innerRun FutureTask.java   303
org.apache.catalina.core.ContainerBase$StartChild   call ContainerBase.java    1549
org.apache.catalina.core.ContainerBase$StartChild   call ContainerBase.java    1559
org.apache.catalina.util.LifecycleBase start    LifecycleBase.java    139
org.apache.catalina.util.LifecycleBase init LifecycleBase.java    102
org.apache.catalina.core.StandardHost  initInternal StandardHost.java 794

通过控制台的信息,我们看到是StartChild#call方法调用的,而我们查看StartChild#call方法其实是在StandardEngine的startInternal方法中通过异步线程池去初始化子容器。因此到这里我们就理清楚了,StarndardHost的init方法是在调用start方法的时候被初始化。那么接下来我们就来看看,start方法的整体调用流程。

BootStrap#start

采用分析load方法一样的方法,经过对BootStrap#start的分析,我们最终可以得到得到如下的调用链:

org.apache.catalina.startup.Bootstrap#start call stack

org.apache.catalina.startup.Bootstrap#start
->org.apache.catalina.startup.Catalina#start 通过反射调用
-->org.apache.catalina.core.StandardServer#start
--->org.apache.catalina.core.StandardService#start
---->org.apache.catalina.core.StandardEngine#start
---->org.apache.catalina.Executor#start
---->org.apache.catalina.connector.Connector#start

综合上文的描述我们总体得到如下的调用链:

org.apache.catalina.startup.Bootstrap#main call stack

org.apache.catalina.startup.Bootstrap#main
->org.apache.catalina.startup.Bootstrap#init
->org.apache.catalina.startup.Bootstrap#load
-->org.apache.catalina.startup.Catalina#load
--->org.apache.catalina.core.StandardServer#init
---->org.apache.catalina.core.StandardService#init
----->org.apache.catalina.connector.Connector#init
----->org.apache.catalina.core.StandardEngine#init
->org.apache.catalina.startup.Bootstrap#start
-->org.apache.catalina.startup.Catalina#start 通过反射调用
--->org.apache.catalina.core.StandardServer#start
---->org.apache.catalina.core.StandardService#start
----->org.apache.catalina.core.StandardEngine#start
----->org.apache.catalina.Executor#start
----->org.apache.catalina.connector.Connector#start

通过上面的分析我们已经搞清楚了Tomcat启动的总体的过程,但是有一些关键的步骤,我们还需要进行进一步的深入探究。let’s do it.

Reference

Tomcat启动过程(Tomcat源代码阅读系列之三)

转载于:https://www.cnblogs.com/549294286/p/3717714.html

【转】Tomcat7启动的总过程 (有时间自己写下tomcat8的)相关推荐

  1. redis 启动加载mysql_Redis分析系列:启动加载过程

    从本篇文章开始(命名为Redis分析系列),将会通过分析Redis的源代码(以Redis 2.2.0 RC1为准),来对它的内部实现做一些探讨.本文主要介绍Redis启动加载过程,总体上可以分为如下几 ...

  2. jboss之启动加载过程详解(-)

    今天看了看jboss的boot.log和server.log日志,结合自己的理解和其他的资料,现对jboss的启动和加载过程做出如下总结: 本文以JBoss Application Server 4. ...

  3. 200plc,经典案例,两台水泵,一用一备 有一个总启动和总停止,控制这两路的启停

    200plc,经典案例,两台水泵,一用一备 水泵控制要求 1,有一个总启动和总停止,控制这两路的启停 2,两台水泵,水泵一备一用,系统上电后1#电机先工作,24小时后,2#电机工作,此后电机以此交替工 ...

  4. Android 7.0 ActivityManagerService(2) 启动Activity的过程:一

    从这一篇博客开始,我们将阅读AMS启动一个Activity的代码流程. 自己对Activity的启动过程也不是很了解,这里就初步做一个代码阅读笔记,为以后的迭代打下一个基础. 一.基础知识 在分析Ac ...

  5. Android系统在新进程中启动自定义服务过程(startService)的原理分析 (下)

    Step 10. ActivityManagerService.attachApplicationLocked 这个函数定义在frameworks/base/services/java/com/and ...

  6. libvirt 启动 qemu 的过程

    1 背景介绍 gdb 调试多任务程序时会有些麻烦: fork 之后没法同时跟踪父进程和子进程,如果在子进程里设置了一个 breakpoint,那么子进程将会收到一个 SIGTRAP 信号并退出.gdb ...

  7. ML:人工智能之机器学习ML解决实际应用问题的思路总过程(最全)

    ML:人工智能之机器学习ML解决实际应用问题的思路总过程(最全) 目录 详细思路结构 详细思路结构

  8. oracle ora 00283,【案例】Oracle报错ORA-16433非归档丢失redo无法启动的恢复过程

    天萃荷净 Oracle研究中心案例分析:运维DBA反映Oracle数据库处理非归档模式,redo文件损坏常规修复无法正常open数据库. 本站文章除注明转载外,均为本站原创: 转载自love wife ...

  9. Macbook Pro 启动Win7的过程中黑屏(black screen)

    如题所述,我的macbook pro启动Win7的过程中黑屏了,好像关机了一样,但是可以进入安全模式. 几番折腾,发现可能是Win7的最新的自动更新导致的(2018-4-10和2018-4-11两次更 ...

最新文章

  1. MySQL中定义fk语句_MySQL基础篇/第3篇:MySQL基本操作语句.md · qwqoo/MySQL-Review - Gitee.com...
  2. Windows2012使用笔记
  3. 从AndroidStudio同步上传项目代码到GitHub
  4. 计算机不能辨别汉字wifi,Win10系统连接不上被隐藏的中文Wifi的解决方法
  5. 金银岛(信息学奥赛一本通-T1225)
  6. 动态规划复习-HDU1081
  7. ps右键不显示编辑选项_0基础小白能上手的AE动态插画教程你还不知道么?
  8. 腾讯2020校园招聘后台开发面试编程题
  9. mcc mnc 运营商对应表_在南非怎么选手机的移动电话运营商?
  10. unity调用dll打开双目
  11. Luogu1880 石子合并
  12. 一个简单实用的boost升压电路
  13. 五色电阻在线计算机,色环电阻在线计算器
  14. 计算机专业群名有内涵,有内涵高大上的群名
  15. php语言有哪些特性,盘点PHP编程语言具有的特性
  16. [敛火成丹]Win11Dev-25236.1010专业工作站版-微调
  17. excel筛选时保留下面某些行不被筛选
  18. 【3DFR Python】convertDepth2Normal:从深度图计算法向图
  19. 搭建环境创建vue项目
  20. typora画流程图、时序图(顺序图)、甘特图

热门文章

  1. python解非线性规划问题讲析_python中线性规划中的单纯形法、scipy库与非线性规划求解问题...
  2. linux dns中文域名,Nginx 中文域名配置详解及实现
  3. python接入微信公众号_Python学习之微信公众号接入 一 验证
  4. springboot 获取application参数_LOOK ! SpringBoot的外部化配置最全解析
  5. 改善前端优化的有用技巧
  6. Matlab实用程序--图形应用-图形的叠加
  7. Python 项目依赖包 第三方库 生成requirements.txt的两种方法
  8. 草履虫纳米机器人_激光驱动的机器人大军!Nature:机器人尺寸小于 0.1 毫米,4 英寸晶圆可容纳 100 万个...
  9. 崩溃重启_semi-sync插件崩溃导致MySQL重启的故障分析-爱可生
  10. ntfs 格式在linux下挂载