《看透SpringMVC源码分析与实践》
Tomcat源码分析—-初始化与启动

tomcat环境搭建

源码下载
由于项目使用的tomcat版本时7.47,从apache svn check代码,svn地址是:
http://svn.apache.org/repos/asf/tomcat/tc7.0.x/tags/TOMCAT_7_0_47/。

idea环境搭建
idea搭建tomcat7开发环境,参照基于IntelliJ IDEA 15.0.2的Tomcat7.0.69源码运行环境搭建

  • 将tomcat_7_0_47转换为Maven工程,添加 pom.xml 文件。
  • 在tomcat_7_0_47的源码根目录下,新建 catalina-base目录,作为Tomcat的工作目录
  • 将 conf、logs(新建目录)、webapps、work (新建目录)文件夹,移入 catalina-base目录
  • idea引入import Project –>pom.xml
  • 删除无用的文件、文件夹(非强迫症患者,此步可跳过)。删除bin/,modules/,res/,test/,build.properties等文件,最终的项目结构如下图:
  • 运行org.apache.catalina.startup.Bootstrap.main(),jvm参数为:-Dcatalina.base="你的工程目录\catalina-base",log配置为output to file:你的工程目录\catalina-base\logs

可能会报错:
java.lang.ClassNotFoundException: websocket.drawboard.DrawboardContextListener 只需将/webapps/example 文件夹删除删除即可。


tomcat启动

tomcat的启动入口是org.apache.catalina.startup.Bootstrap.main()

Bootstrap.main()

//org.apache.catalina.startup.Bootstrap
public static void main(String args[]) {//创建一个Bootstrap,当bootstrap 初始化完成之后,再赋值给daemonif (daemon == null) {//try-catch 省略....Bootstrap bootstrap = new Bootstrap();//调用init()方法,初始化ClassLoader,并用ClassLoader穿件Catalina实例,赋给catalinaDaemon变量。bootstrap.init();daemon = bootstrap;} else {Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);}try {//默认command为startString command = "start";if (args.length > 0) {//取args最后一个参数,为commandcommand = args[args.length - 1];}if (command.equals("startd")) {args[args.length - 1] = "start";daemon.load(args);daemon.start();} else if (command.equals("start")) {//start比 startd多了一行代码: daemon.setAwait(true);daemon.setAwait(true);daemon.load(args);//调用start方法...daemon.start();}//省略,stopd,stopelse if (command.equals("configtest")) {daemon.load(args);if (null==daemon.getServer()) {System.exit(1);}System.exit(0);}} catch (Throwable t) {//省略log....System.exit(1);}}

main方法可传入的参数有
startd、stopd、start、stop,startd和start的区别是设置了一个await的boolean变量为true

main方法内容分为三部分:

  • 首先创建Bootstrap,并执行init()方法初始化,
  • 然后调用load()
  • 最后调用start()方法。

Bootstrap.init()
调用Boostrap的init方法主要完成:

  • 初始化路径:CATALINA_HOME,CATALINA_BASE
  • 初始化类加载器:commonLoader、catalinaLoader、sharedLoader.
  • 初始化Boostrap的Catalina对象:通过反射生成Catalina对象,并通过反射调用setParentClassLoader方法设置其父 ClassLoader为sharedLoader
//org.apache.catalina.startup.Bootstrap
public void init() throws Exception {//设置安装目录:CATALINA_HOMEsetCatalinaHome();//设置工作目录CATALINA_BASEsetCatalinaBase();//初始化classLoadersinitClassLoaders();//主线程的classLoader设置为catalinaLoaderThread.currentThread().setContextClassLoader(catalinaLoader);//安全管理的classLoad设置为catalineLoaderSecurityClassLoad.securityClassLoad(catalinaLoader);//省略log....//load启动类CatalinaClass<?> startupClass =catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");Object startupInstance = startupClass.newInstance();/* * 反射实现:catalina的ParentClassLoader设置为sharedLoader* catalina.setParentClassLoader(sharedLoader);*/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);method.invoke(startupInstance, paramValues);catalinaDaemon = startupInstance;}

CATALINA_HOME和CATALINA_BASE区别?

CATALINA_HOME是Tomcat的安装目录,指向公用信息的位置,就是bin和lib的父目录。。
CATALINA_BASE是Tomcat的工作目录指向每个Tomcat目录私有信息的位置,就是conf、logs、temp、webapps和work的父目录。

如果我们想要运行Tomcat 的 多个实例,但是不想安装多个Tomcat软件副本。那么我们可以配置多个工作 目录,每个运行实例独占一个工作目录,但是共享同一个安装目录
tomcat多实例配置

Bootstrap.initClassLoaders()
tomcat的classloader通过conf/catalina.properties配置,配置内容为:

package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.##classloader 相关
common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
shared.loader=//省略....

initClassLoaders()方法逻辑
初始化commonLoader ,catalinaLoader ,sharedLoader

//org.apache.catalina.startup.Bootstrap
private void initClassLoaders() {try {//commonLoader 设置父类加载器为null,则为jdk中的commonLoader = createClassLoader("common", null);if( commonLoader == null ) {//若没有配置文件配置,则取SystemClassLoader。commonLoader=this.getClass().getClassLoader();}//catalinaLoader , sharedLoader 设置父类加载器为 commonLoadercatalinaLoader = createClassLoader("server", commonLoader);sharedLoader = createClassLoader("shared", commonLoader);} catch (Throwable t) {handleThrowable(t);log.error("Class loader creation threw exception", t);System.exit(1);}}

SecurityClassLoad.securityClassLoad()方法逻辑
使用catalinaLoader加载tomcat容器独享的一些calss,

//org.apache.catalina.security.SecurityClassLoad
public static void securityClassLoad(ClassLoader loader)throws Exception {if( System.getSecurityManager() == null ){return;}loadCorePackage(loader);//加载org.apache.catalina.core.下的classloadCoyotePackage(loader);//加载org.apache.coyote下的classloadLoaderPackage(loader);loadRealmPackage(loader);loadSessionPackage(loader);loadUtilPackage(loader);loadValvesPackage(loader);loadJavaxPackage(loader);loadConnectorPackage(loader);loadTomcatPackage(loader);}

Bootstrap.load()
Bootstrap.load最终调用的是Catalina的load方法.,主要完成:

  • 设置系统变量
  • 初始化命名系统
  • Digester类,默认将conf/server.xml解析成相应的对象,这个解析很重要,因为是他完成了Connector和Container的初始化工作。
  • Server调用init初始化声明周期,其父类LifecycleMBeanBase实现
//org.apache.catalina.startup.Catalina
private void load() {//当前面代码没有初始化CATALINA_BASE和CATALINA_HOME时,重新设置二者值initDirs();//初始化命名系统,即向System.Properties设置"java.naming.factory.initial"为"org.apache.naming.java.javaURLContextFactory"initNaming();//创建Digester,主要用于处理xml配置文件,创建Server,初始化Connector和ContainerDigester digester = createStartDigester();InputSource inputSource = null;InputStream inputStream = null;File file = null;try {//获取配置文件:默认值conf/server.xml,可由command参数-config指定file = configFile();inputStream = new FileInputStream(file);inputSource = new InputSource(file.toURI().toURL().toString());} //省略log,catch...//省略log,try-catch,部分代码.....try{inputSource.setByteStream(inputStream);digester.push(this);//解析xmldigester.parse(inputSource);} //省略log,catch...getServer().setCatalina(this);//System.out , System.errorinitStreams();//调用LifecycleBase.init();最终调用自身的initInternal()方法,见后续`StandardServer`getServer().init();
}

Digester解析server.xml解析过程见: Tomcat—-Container源码分析

StandardServer.init()初始化流程见: Tomcat—-生命周期管理


Boostrap.start()
最终调用的是Catalina.start(),它主要完成:

  • Server加载:Server才是正真的tomcat服务执行者。调用load方法,初始化Connector和Container
  • 调用Server的start方法,最终调用的是StandardServer的startInternal方法,调用自己所有的Service的start方法,启动connector和container、excutor的start
  • 注册钩子
//org.apache.catalina.startup.Catalina
public void start() {if (getServer() == null) {//Server加载load();}try {//服务启动,//调用LifecycleBase.start();最终调用自身的startInternal()方法getServer().start();} //省略 catch,log.....// 注册shutdown钩子if (useShutdownHook) {if (shutdownHook == null) {shutdownHook = new CatalinaShutdownHook();}Runtime.getRuntime().addShutdownHook(shutdownHook);LogManager logManager = LogManager.getLogManager();if (logManager instanceof ClassLoaderLogManager) {((ClassLoaderLogManager) logManager).setUseShutdownHook(false);}}//进入等待状态if (await) {//调用Server.await()方法await(); stop(); }}

至此,tomat就启动完成了。

Tomat启动-源码跟踪相关推荐

  1. spring-boot启动源码学习-1

    2019独角兽企业重金招聘Python工程师标准>>> spring-boot启动源码分析-启动初始化 主要对spring-boot的启动流程中的启动初始化进行学习,学习spring ...

  2. 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )

    文章目录 前言 一.热启动与冷启动选择 二.AMS 进程中执行的相关操作 三.通过 Binder 机制转到 ActivityThread 中执行的操作 总结 前言 上一篇博客 [Android 启动过 ...

  3. Android 9(P)之init进程启动源码分析指南之一

         Android 9 之init进程启动源码分析指南之一 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...

  4. Myth源码解析系列之五- 服务启动源码解析

    通过前面几篇文章,我们搭建了环境,也进行了分布式事务服务的体验,相信大家对myth也有了一个大体直观的了解,接下来我们将正式步入源码解析之旅~~ order服务启动源码解析(myth-demo-spr ...

  5. spring-boot-2.0.3启动源码篇一 - SpringApplication构造方法

    前言 spring-boot-2.0.3应用篇 - shiro集成,实现了spring-boot与shiro的整合,效果大家也看到了,工程确实集成了shiro的认证与授权功能.如果大家能正确搭建起来, ...

  6. 【Linux 内核 内存管理】Linux 内核内存布局 ④ ( ARM64 架构体系内存分布 | 内核启动源码 start_kernel | 内存初始化 mm_init | mem_init )

    文章目录 一.ARM64 架构体系内存分布 二.Linux 内核启动源码 start_kernel 三.内存初始化源码 mm_init 四.内存初始化源码 mem_init 一.ARM64 架构体系内 ...

  7. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 二 )

    文章目录 前言 一.ActivityManagerService.attachApplicationLocked 二.ActivityStackSupervisor.attachApplication ...

  8. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread -> Activity、主线程阶段 二 )

    文章目录 前言 一.ActivityThread 类 handleLaunchActivity -> performLaunchActivity 方法 二.Instrumentation.new ...

  9. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread -> Activity、主线程阶段 一 )

    文章目录 前言 一.ClientTransactionHandler.scheduleTransaction 二.ActivityThread.H 处理 EXECUTE_TRANSACTION 消息 ...

最新文章

  1. java 类的执行顺序_java类加载的顺序
  2. Elasticearch 安装 基础介绍 (一)
  3. 山东旅游学院2021高考成绩查询,2021年山东旅游职业学院录取查询入口,高考录取结果查询网址登录...
  4. 酒店管理系统c语言带注释,酒店管理系统--C语言版.pdf
  5. 如何使用IMPORT关键字读取ABAP cluster表里的数据到ABAP内表
  6. angularjs directive 实例 详解
  7. Redis作者antirez:开源维护者的挣扎
  8. 如何添加自动更新Play Framework 2.X项目的版本号
  9. 死锁Waiting--DeadLockDemo
  10. python数据挖掘分析案例python_Python 数据挖掘实例 决策树分析
  11. 深入理解MySQL8.0直方图
  12. iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载+使用输出流代替文件句柄...
  13. python简明教程_04
  14. 自动跑程序vbs脚本
  15. 魔方口诀(个人备忘)
  16. 自定义xml解析框架
  17. 运动轨迹 php,两种JS实现小球抛物线轨迹运动的方法
  18. [问题已处理]-centos7 history命令没有任何记录
  19. 计算机本科生论文都抽查什么,本科生毕业论文抽查,抄袭马上会被撤销学位,学生最好别违规...
  20. dns 劫持什么意思、dns 劫持原理及几种解决方法

热门文章

  1. Python 中 if not 的用法
  2. 如何搭建属于自己的Web服务器
  3. 如何css设置div页面100%高度, body页面全高
  4. VirtualBox 立即卸载此应用,因为它与Windows 10 不兼容
  5. Spark优化篇:RBO/CBO
  6. C# 文件的保存与读取
  7. 设计文档的内容(概要设计,需求分析,详细设计)
  8. 网页特效应用二期(电脑新建一个文本文档复制粘贴改后缀名为.html就能用)
  9. 海康威视硬件工程师校招一面面经
  10. 什么是强引用,软引用,弱引用,虚引用