环境准备

  • IntelliJ IDEA 2024.1.4 (需要安装Ant插件)
  • JDK 1.8
  • Ant 1.10.13

源码仓库: https://github.com/apache/tomcat | 源码Tag: 8.5.38

前言

Tomcat是Apache基金会开源的Servlet型Web服务器, 在Spring Boot系列框架中作为默认Web服务器组件。

Tomcat是一个历史悠久的Web服务组件, 它的项目管理工具使用的是Ant, 而不是Maven; 它所支持的JDK版本比较低, 在本Tag中可观察到为JDK1.7。

解决编译问题

打开Ant插件, 可以看到几十个Task可以执行。

打开build.xml文件, 首先要注意的应该是build.properties的文件名,刚好项目目录内有个build.properties.default的文件, 复制一份改个build.properties的名试下叭。

在这个文件中, 我们需要修改base.path, 这项配置是tomcat编译时依赖的Jar包的存放位置。默认配置如下:

1
base.path=${user.home}/tomcat-build-libs # 即 ~/tomcat-build-libs

然后找到Ant插件内显示的两个Task, deploy和embed, 分别是用来构建Tomcat独立服务和嵌入式Jar包(就是spring-boot-starter-tomcat用的依赖)。

执行完成后, 会出现output目录。

寻找程序入口

阅读源码, 一种方式是首先找到我们最常用的启动方式, 并找到程序入口。

首先, 我们先将Tomcat作为独立服务来看待,在output/build/bin 中找到startup.sh, 可以看到最后一行如下:

1
exec "$PRGDIR"/"$EXECUTABLE" start "$@"

那么我们搜索 EXECUTABLE 可以看到就是调用了catalina.sh

打开以后翻到底部, 发现好长的一连串 if-else, 实际上这些都不重要, 只是在做一些启动参数解析及预处理。我们知道启动Java程序肯定会有启动类的路径, 所以直接搜索org.apache , 肯定能发现很多都试用了下面这个Class:

1
org.apache.catalina.startup.Bootstrap

找到这个文件, 在里面果然发现了main方法, 所以程序入口就找到了。

实际上, 笔者并不是直接搜索的包路径, 而是粗略的看了一下那些if-else, 从下往上看,发现大部分是exit 1, 而剩下的就是正常启动命令咯, 然后就顺理成章的发现了Bootstrap, 找到了main方法。

探究如何启动

查看Bootstrap类的main方法, 主要就是三行代码

  • bootstrap.init(): 初始化, 这里主要就是处理 ClassLoader
  • daemon.load(args): 这里args是我们执行命令行中传递的参数
  • daemon.start(): 这就是正式启动的方法

这里daemon 和 bootstrap 其实是一个对象, 只是调用时机不同, 引用的名称不同

分析一下, 这三个方法依次点击进去看一下, 实际上看似在操作bootstrap, 实则是在操作Catalina, 我们把代码跟下来, 会发现init() 是在设置 catalinaDaemon 这个属性, 而load和start都是在使用反射执行Catalina的对应方法

Catalina的Server属性, 即为Web服务器的核心业务, 而start方法也最终也会执行到getServer().start()方法

另一种启动方式

如果你看过springboot的Servlet类型Web服务启动代码, 会发现它是使用Tomcat这个类的start()方法启动的, 没有任何Catalina相关的代码. 那我们再来看下Tomcat和Calalina有什么区别呢?

在Tomcat中也存在start方法, 只是不再需要Bootstrap.init和 load()方法了,而是直接使用Tomcat.init方法进行初始化. 也就是直接通过代码调用的方式启动, 不需要使用命令行方式进行启动和终止了.

总结

由此可见, Calalina就是命令行方式的核心启动类; 而Tomcat就是嵌入式的核心启动类.

__END__