Tomcat-启动流程

article/2025/10/29 0:03:54

文章目录

  • 叨叨
  • 程序入口
  • 加载load
    • 流程简述
      • Bootstrap.load()
      • Catalina.load(params)
      • init()模板方法
      • init流程总结
  • start()模板方法
    • 流程简述
    • start()模板方法
    • Host.start()特别说明

叨叨

讲真,最开始源码调试几遍下来还是很懵了,尤其是组件初始化过程中不断去调用其他组件的init()方法,中间使用模板方法的设计模式,最终抽象方法交给哪个子类去实现有点绕。这个搞明白之后,start()方法的流程就和init()类似了。过程中涉及的组件经过初步梳理,再结合server.xml中的标签,基本可以猜个七七八八,最后结合Tomcat的架构设计图进行梳理修正。
这里先记录下启动流程的学习。Tomcat源码的下载及相关配置就不赘述了,网上有很多教程。

程序入口

java程序的执行入口是main()方法,根据官网的启动流程描述或者启动脚本中的内容,可以定位到一个类:
org.apache.catalina.startup.Bootstrap

在这里插入图片描述

进到这个类中直接搜索main()方法,看下main()方法主要分为两大块:

  1. 初始化bootstrap
    进入init()方法可以看到是在做类加载器的初始化与设置
  2. 加载与启动bootstrap
    条件判断进入load()和start()的执行,按照猜想,在设置完类加载器后,框架启动的尿性就是读取并加载配置,初始化核心组件对象,然后启动框架。下面着重看下load和start两个方法。

在这里插入图片描述

加载load

流程简述

加载什么呢?无非就是:

  • 配置信息的加载(毕竟我们还有个配置文件server.xml)
  • 核心对象的加载

看下源码究竟做了什么:

Bootstrap.load()

通过反射调用Catalina.load()

Method method =catalinaDaemon.getClass().getMethod("load", paramTypes);
if (log.isDebugEnabled())log.debug("Calling startup class " + method);
System.out.println("******************* >>>   反射调用Catalina.load()");
method.invoke(catalinaDaemon, param);

Catalina.load(params)

首次看到load方法,直观感受:

  • 有几个init开头的方法,初始化可能会用到的资源
  • 创建了一个Digester对象,并解析输入源 digester.parse(inputSource)
  • 执行getServer().init()
// inputSource来自于一个文件
digester.parse(inputSource);
// 文件正是 configFile = "conf/server.xml";
file = configFile();

所以digester对象是用来解析server.xml的。
getServer()返回一个Server对象,server何时被set进Catalina对象中的?
最后调用server.init()开始初始化流程。

init()模板方法

init()方法是接口Lifecycle中的方法,可以看到init()方法在LifecycleBase类中实现,该类是一个抽象类,并且在init()方法的执行逻辑中调用抽象方法initInternal(),这不是妥妥的模板方法设计模式么。
所以这个initInternal应该是在某个server实现类A中执行的,且类A需要继承LifecycleBase或其子类。server只有一个实现类 StandardServer 且满足上述条件。

LifecycleBase中init模板方法:
在这里插入图片描述
StandardServer中的实现:

// The set of Services associated with this Server.
private Service services[] = new Service[0];protected void initInternal() throws LifecycleException {// 调用父类super.initInternal();//...// Initialize our defined Servicesfor (int i = 0; i < services.length; i++) {services[i].init();}//...
}

super.initInternal()方法:
在这里插入图片描述

最开始看到这里时还是有点乱的,梳理之后就发现这样的层次逻辑很清晰。通常我们使用模板方法> 有两层:

第一层:抽象模板类
第二层:继承了抽象类的具体子类

Tomcat这里你可以理解成有三层:

第一层:抽象模板类
第二层:继承了抽象类的通用基础类LifecycleMBeanBase
第三层:继承了基础类的具体子类StandardXXX类

模板类中定义了模板方法init和抽象方法initInternal;基础类中实现initInternal时做了公共逻辑的抽> 取(即注册组件),最后在具体组件类中进行具体组件的初始化逻辑。

看一下类的继承关系:
在这里插入图片描述
再看一下在service组件的initInternal方法中,调用了engine,executor,mapperListener,connector四个组件的init()方法:

在具体组件类的初始化过程initInternal()中,都是通过super.initInternal()调用通用的组件注册逻
辑,最后再执行组件自身的初始化逻辑。

service中四个组件的init概览:

engine.init();1. Host组件的初始化并没有在此处执行,而是在start阶段进行的。(具体见start过程)2. engine的初始化主要做了如下操作:3. realm对象的处理4. 完成engine组件的注册executor.init();完成executor组件的注册mapperListener.init();完成listener组件的注册connector.init();1. 创建coyote适配器2. protocolHandler.init() --> abstractProtocol.init() --> endpoint.init()

init流程总结

两load:
bootstrap.load:反射调用Catalina.load();
catalina.load:创建xml解析器解析server.xml配置,调用server.init()开始初始化;
Lifecycle实现类与模板方法模式:
通过定义Lifecycle接口以及使用模板方法,规范并统一了组件类的初始化过程。
需要注意:
Host等组件的初始化是在start过程中进行的。(见下说明)

start()模板方法

流程简述

组件的start()方法与init的流程类似,也是通过Lifecycle接口配合使用模板方法的模式完成组件的启动。
boostrap.start

通过反射调用Catalina.start()

Catalina.start

  • 检查server实例是否存在
  • getServer().start()启动server

start()模板方法

LifecycleBase中start模板方法:

public final synchronized void start() throws LifecycleException {System.out.println("******************* >>>   "+this.getClass().getName()+" 调用LifecycleBase.start");if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||LifecycleState.STARTED.equals(state)) {if (log.isDebugEnabled()) {Exception e = new LifecycleException();log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);} else if (log.isInfoEnabled()) {log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));}return;}// 检查组件状态,是否需要初始化。Host等主键的初始化就是在这里开始的if (state.equals(LifecycleState.NEW)) {init();} else if (state.equals(LifecycleState.FAILED)) {stop();} else if (!state.equals(LifecycleState.INITIALIZED) &&!state.equals(LifecycleState.STOPPED)) {invalidTransition(Lifecycle.BEFORE_START_EVENT);}try {setStateInternal(LifecycleState.STARTING_PREP, null, false);// 调用抽象模板的具体子类startInternal();if (state.equals(LifecycleState.FAILED)) {// This is a 'controlled' failure. The component put itself into the// FAILED state so call stop() to complete the clean-up.stop();} else if (!state.equals(LifecycleState.STARTING)) {// Shouldn't be necessary but acts as a check that sub-classes are// doing what they are supposed to.invalidTransition(Lifecycle.AFTER_START_EVENT);} else {setStateInternal(LifecycleState.STARTED, null, false);}} catch (Throwable t) {// This is an 'uncontrolled' failure so put the component into the// FAILED state and throw an exception.ExceptionUtils.handleThrowable(t);setStateInternal(LifecycleState.FAILED, null, false);throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);}
}protected abstract void startInternal() throws LifecycleException;

StandardServer中的实现:

protected void startInternal() throws LifecycleException {fireLifecycleEvent(CONFIGURE_START_EVENT, null);setState(LifecycleState.STARTING);globalNamingResources.start();// Start our defined Servicessynchronized (servicesLock) {for (int i = 0; i < services.length; i++) {services[i].start();}}
}

service的start():

与init过程类似,在start过程中,同样也是同时对多个组件进行start()方法调用。

StandardEngine.start

直接调用父类ContainerBase.startInternal()方法,启动子组件和子容器;
子容器组件的启动通过线程池
在这里插入图片描述
StandardContext.startInternal
在这里插入图片描述

Host.start()特别说明

按照上面几个组件的加载机制以及server.xml中标签的层次结构,engine初始化时应该调用host的初始化方法,网上很多教程帖子的启动时序图也都是那样画的。这跟源码中的init流程是不符的

在这里插入图片描述
在这里插入图片描述

于是我开始在load阶段逐行阅读,始终找不到哪里调用了Host的init方法,最后在日志中找到答案:

在这里插入图片描述

启动流程之后细说,这里只需要知道:

  1. engine会将子标签(server.xml标签层级)对象遍历出来构建成可运行的线程丢到线程
    在这里插入图片描述
  2. 这些子标签组件会调用自身的start()方法启动
    在这里插入图片描述
  3. 组件启动时会检查组件状态,如果为NEW则会执行组件init方法
    在这里插入图片描述

http://chatgpt.dhexx.cn/article/SMPTUpPw.shtml

相关文章

java教程(三)初识J2ee十三个规范

&#xfeff;&#xfeff; 我目前是个java未入门的新手&#xff0c;这边看到高手的一些文章&#xff0c;转载过来。---haogting J2EE是一套全然不同于传统应用开发的技术架构&#xff0c;主要可简化且规范应用系统的开发与部署&#xff0c;进而提高可移植性、安全与复用价值。 …

J2EE【开发环境搭建】

EasyUI【DataGrid数据查询】_云彩123的博客-CSDN博客_easyui 获取datagrid数据如需温习上一节内容&#xff0c;请点击下方链接进行跳转&#xff1a;EasyUI(Tree后端工作)_云彩123的博客-CSDN博客用户信息表&#xff08;T_User&#xff09;列名数据类型长度允许空默认值说明idva…

J2EE(环境搭建)

目录 一、jdk安装&环境配置 A、拿到exe文件 B、默认安装位置 C、配置环境变量 D、注意事项 二、Eclipse安装&设置默认编码集 A、解压 B、选择工作区间 C、设置默认编码集 Ca、设置jsp默认编码 Cb、设置workspace编码方式 三、配置Tomcat及JDK A、配置Tomcat …

java quartz 教程_Quartz 教程

一、关于 Quartz Quartz logo Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。 Quartz 可以与 J2EE 与 J2SE 应用程序相结合也可以单独使用。 Quartz 允许程序开发人员根据时间的间隔来调度作业。 Quartz 实现了作…

J2EE入门:环境搭建

前言&#xff1a; JDK的安装以及JDK的环境配置 tomcat的安装 Eclipce的环境配置 MySQL的安装 JDK安装以及环境配置 直接点击JDK安装包安装 JDK默认安装位置&#xff1a;C:\Program Files\Java 然后双击jdk144文件夹 进来之后选中地址栏复制地址 然后退出至桌面,右键桌面…

J2EE之旅(入门)

从这次开始我们就要开始学习j2ee&#xff0c;我们先来介绍一下我们将要使用到的 1.测试工具&#xff1a;ApiPost_Setup_5.4.2_x64.exe 2.开发工具:vs 3.数据库&#xff1a;考虑了多方面&#xff0c;数据库我们就选用mysql 4.数据库可视化工具&#xff1a; 由于这些软件安装大部…

J2EE入门教程

2005-12-11 20:20:21 J2EE入门教程本教程讲述Sun J2EE 1.3 SDK的安装、配置和创建并部署第一个简单的EJB。我们的目标平台是Windows XP SP2&#xff0c;JDK 1.4.2和J2EE SDK 1.3.1的Windows版本。我们在下面的实践中主要使用命令行模式&#xff0c;便于深入J2EE的内部机制。首先…

如何拥有个人的Github技术博客

题图&#xff1a;87testing.com 前不久用wordpress搭建了一个博客&#xff0c;用起来感觉不是太好用&#xff0c;并且数据库经常会发生异常挂掉&#xff0c;没找到解决方案。 看到有不少人使用的hexogithub方式搭建属于自己的博客&#xff0c; 于是&#xff0c;我也做了一些尝试…

github博客+hexo

这里基于ubuntu18&#xff0c;win10其实一样的&#xff0c;把sudo去掉就行了 安装 安装git 配置github的ssh 安装Node.js 版本需不低于 10.13&#xff0c;建议使用 Node.js 12.0 及以上版本 建议换个源 npm install express --registryhttps://registry.npm.taobao.org在gi…

【hexo+gitee/github】博客搭建

文章目录 参考资料1. 使用gitee&#xff08;github&#xff09;与hexo好处2. 安装2.1 安装Nodejs2.2 安装git2.3 安装Hexo 3. 建站4. 启动5. 配置5.1 主题配置 6. 部署到gitee pages6.1 创建一个gitee仓库6.2 配置路径6.3 开启GiteePages服务6.4 github pages部署6.4. 博客源代…

hexo+github部署个人博客

文章目录 hexogithub部署个人博客前期准备hexo搭建个人博客新建GitHub仓库将个人博客部署到Git pages写文章和发布文章Hexo优化修改博客目录下的_config.yml配置 matery主题美化 hexogithub部署个人博客 前期准备 安装git git的下载地址安装node.js node.js注册github账号&am…

利用github进行自定义博客地址

写在前面&#xff1a; 大家好&#xff0c;我是 花狗Fdog &#xff0c;来自内蒙古的一个小城市&#xff0c;目前在泰州读书。 很感谢能有这样一个平台让我能够在这里分享所学所感。 我喜欢编程&#xff0c;喜欢代码&#xff0c;喜欢去做一个程序员。 努力学习&#xff0c;争取多…

如何搭建github.io博客

零、宣传我的Github.io博客 https://ruixiangjiang.github.io/ 如果我的电脑没开就不能访问。。。正在调试中。。。 一、注册一个账号 进入www.github.com &#xff0c;然后注册。首页右上角头像那里有一个“”号&#xff0c;点击后选择“New repositor”。创建完之后点击“…

使用Github.io打造个人博客网站

Github.io上可以方便地打造自己的博客网站&#xff0c;相对于CSDN等社区网站来说既没有广告又能凸显个人调性。 建站步骤 建站步骤非常简单 在个人github上建一个repository&#xff0c;名字必须是username.github.io 推送一个静态html上去 # clone the prj git clone htt…

搭建hexo+Github博客

摘要 本文主要向大家介绍了如何搭建hexo-Github博客的详细过程&#xff0c;有需要的朋友们可以尝试阅读&#xff0c;希望对大家有帮助。 简要吐槽 搭博客其实没那么麻烦&#xff0c;但是在网上各类教程的“指导下”&#xff0c;我不知道放弃了多少次又重新试了多少次&#xff…

GitHub: 个人博客搭建

GitHub: 个人博客搭建 食材&#xff1a;jekyll 工具&#xff1a;GitHub&#xff0c;visual studio code&#xff0c;chrome。 第一步&#xff1a;安装Jekyll&#xff0c;具体参考官方链接 https://www.jekyll.com.cn/docs/step-by-step/01-setup/ 操作指南 安装一个完整的…

hexo+github搭建博客(超级详细版,精细入微)

转载此文章前&#xff0c;请先联系作者&#xff0c;经作者同意后再转载&#xff0c;并请注明原文链接和作者&#xff0c;整理这些不容易&#xff0c;最终版权归作者所有&#xff0c;谢谢合作&#xff01;移步我的个人博客&#xff1a;https://yafine-blog.cn&#xff0c;阅读体…

用github搭建个人(博客网站

&#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f64f;作者水平很有限&#xff0c;如果发现错误&#xff0c;请留言轰炸哦&#xff01;万分感谢&a…

个人博客构建——github个人博客

摘要 为什么需要构建自己的博客呢&#xff1f;第一&#xff0c;是为解决平台的可能随意删除自己博文&#xff0c;需要有一个不被其他所左右的分享平台&#xff0c;第二&#xff0c;需要一个载体来记录的自己的学习内容&#xff0c;第三&#xff0c;也是为自己自媒体提前做一个…

Hexo+GitHub免费搭建个人博客(保姆级教程)

文章目录 HexoGitHub免费搭建个人博客(保姆级教程)安装依赖环境Git的下载与安装Node.js下载与安装:red_circle:***注意一***:red_circle:***注意二*** Git和Node.js工具工作状态检验:red_circle:**注意三** 安装Hexo:ng:小插曲1:ng:小插曲3&#xff1a; 生成博客文件博客效果预…