Tomcat源码解析(二): Bootstrap和Catalina
Tomcat源码解析(二): Bootstrap和Catalina
Tomcat源码系列文章Tomcat源码解析(一): Tomcat整体架构Tomcat源码解析(二): Bootstrap和Catalina前言在tomcat的bin目录下有两个启动tomcat的文件 一个是startup.bat,它用于windows环境下启动tomcat另一个是startup.sh,它用于linux环境下启动
Tomcat源码解析(二): Bootstrap和Catalina
Tomcat源码系列文章
Tomcat源码解析(一): Tomcat整体架构
Tomcat源码解析(二): Bootstrap和Catalina
- 在tomcat的bin目录下有两个启动tomcat的文件
- 一个是
startup.bat
,它用于windows
环境下启动tomcat - 另一个是
startup.sh
,它用于linux
环境下启动tomcat
- 一个是
- 两个文件中的逻辑是一样的, 我们只分析其中的startup.bat
- 而startup.bat文件实际上就做了一件事情:启动catalina.bat
- catalina.bat中下面这段指定了tomcat的启动类为
Bootstrap
这个类,catalina.bat最终执行了Bootstrap类中的main
方法来启动tomcat
set _EXECJAVA=%_RUJAVA%
set MAICLASS=org.startup.Bootstrap
set ACTIO=start
- 首先来看下整个启动过程,我们可以看到
Bootstrap
作为启动入口首先进行了初始化方法init
然后load
方法加载了Catalina
1、main
- Bootstrap的main方法首先会创建一个
Bootstrap
对象,调用它的init
方法初始化 - 然后根据启动参数,调用Bootstrap对象的不同方法,默认模式为start,该模式下将会先后调用
load
与start
方法
private static final Object daemonLock = new Object();
private static volatile Bootstrap daemon = null;
// Bootstrap类的main方法
public static void main(String args[]) {
// 创建一个 Bootstrap 对象
synchronized (daemonLock) {
if (daemon == null) {
Bootstrap bootstrap = new Bootstrap();
try {
// 调用init方法初始化
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
().setContextClassLoader();
}
}
// 根据启动参数,分别调用 Bootstrap 对象的不同方法
try {
// 默认参数为start
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (("startd")) {
...
} else if (("stopd")) {
...
} else if (("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
(1);
}
} else if (("stop")) {
...
} else if (("configtest")) {
...
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
(1);
}
}
2、init
- 本文对类加载器内容不做分析,后续看情况单独讲
- 简单来说init就是
反射实例化Catalina
对象
public void init() throws Exception {
// 初始化类加载器相关内容
initClassLoaders();
().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
// 通过catalinaLoader加载Catalina,反射实例化Catalina对象
Class<?> startupClass = catalinaLoader.loadClass("org.startup.Catalina");
Object startupInstance = startupClass.getCtructor().newInstance();
// Set the shared extensi class loader
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
String methodame = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forame("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
// 反射将sharedLoader设置为catalinaLoader的父类加载器,本文不做分析
Method method =
startupInstance.getClass().getMethod(methodame, paramTypes);
method.invoke(startupInstance, paramValues);
// 将catalina实例引用赋值
catalinaDaemon = startupInstance;
}
、load与start
- load与start都是通过上一步获取到的catalinaDaemon对象反射调用catalina类的
load
与start
方法 - 这两个过程我们会在下面的Catalina内容中介绍
load方法:
代码语言:javascript代码运行次数:0运行复制private void load(String[] arguments) throws Exception {
String methodame = "load";
Object param[];
Class<?> paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new Object[1];
param[0] = arguments;
}
Method method =
catalinaDaemon.getClass().getMethod(methodame, paramTypes);
// 反射调用catalina的load方法,参数为null
method.invoke(catalinaDaemon, param);
}
start方法:
代码语言:javascript代码运行次数:0运行复制 public void start()
throws Exception {
if( catalinaDaemon==null ) init();
// 反射调用catalina的start方法,参数为null
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null);
}
- 上文中Bootstrap类的load与start方法实质上就是反射调用catalina类的load与start方法
1、load
- 创建Digester对象,解析
conf/server.xml
文件 - 核心内容调用Server实现类
StandardServer
的init
方法来初始化组件(下篇文章单独讲)
public void load() {
// 如果已经加载则退出,默认false,下面会置为true
if (loaded) {
return;
}
loaded = true;
initDirs();
initaming();
// 创建Digester对象,用来解析server.xml文件
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
// 加载conf目录下的server.xml文件
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(().toURL().toString());
} catch (Exception e) {
...
}
try {
inputSource.setByteStream(inputStream);
digester.push(this);
// 开始解析conf/server.xml文件
digester.parse(inputSource);
} catch (SAXParseException spe) {
...
}
// server和catalina之间建立关联
// Server接口实现类StandardServer是在解析server.xml文件时候创建
// 当时StandardServer对象set到Catalina
// 此时又将Catalinaset到StandardServer对象中
// 形成:你中有我,我中有你
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
...
// 初始化server,后面另开一篇单独讲
try {
getServer().init();
} catch (LifecycleException e) {
...
}
}
Digester对象解析server.xml文件
- 解析server.xml文件,根据<Server>和<Service>标签内容,创建
Server
和Service
是实现类StandardServer
和StandardService
2、start
- 再来看下整个启动过程
- Catalina的load方法最后一步getServer().init(),就是Server、Service、Engine等一系列
组件的初始化
- 核心内容调用server实现类
StandardServer
的start
方法来启动服务器(下篇文章单独讲)
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
// 无法启动服务器。未配置服务器实例
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = ();
// 调用server的start方法来启动服务器
try {
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
long t2 = ();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
// 注册关闭钩子
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
// 向addShutdownHook方法传入的Thread,其run方法即为自定义的shutdown时清理逻辑
Runtime.getRuntime().addShutdownHook(shutdownHook);
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
// 进入等待状态
// 启动类Bootstrap默认调start方法设置await=true
if (await) {
// main线程等待,等待接收shutdown命令,接受到则跳出阻塞
await();
// 跳出阻塞,执行Server.stop();
stop();
}
}
2.1、注册shutdown钩子
- 向addShutdownHook方法传入的
Thread线程任务
,其run方法即为自定义的shutdown时清理逻辑
- JDK内部,是通过一个Map来保存所有添加的多个ShutdownHook,在被触发时执行
- 那这个shutdownHook一般是在什么时候会被调用呢?
程序正常退出
,这发生在最后的非守护线程退出时,或者在调用exit(等同于)方法时
- 为响应用户中断而终止虚拟机,如键入
^C
或发生系统事件,比如用户注销或系统关闭
- 在Java虚拟机退出的时候,这些设置的shutdownHook会被并行的调用
- 相当于此线程等待着虚拟机正常退出,就会执行Catalina#stop方法
- 整体逻辑下方Socket监听SHUTDOW响应然后调用Catalina#stop方法一样
ps:对于非正常方式退出Java虚拟机,例如杀进程,系统断电等,这些情况下,shutdownHook不会被执行
protected class CatalinaShutdownHook extends Thread {
@Override
public void run() {
try {
if (getServer() != null) {
stop();
}
} catch (Throwable ex) {
...
}
}
}
2.2、阻塞tomcat主线程
server.xml开头内容
- Catalina的await方法实际是调用Server实现类StandardServer的await方法
public void await() {
getServer().await();
}
- 阻塞tomcat主线程,只要stopAwait不为true, tomcat主线程在此无限循环
- 监听到客户端发起
SHUTDOW
命令后,退出阻塞,往下执行stop方法
// StandardServer类方法
private int port = 8005;
private String shutdown = "SHUTDOW";
private volatile ServerSocket awaitSocket = null;
@Override
public void await() {
// shutdown端口配置为-2,启动完Server直接再终止Server
if( port == -2 ) {
return;
}
// 配置为-1,则不再监听shutdown端口
if( port==-1 ) {
try {
awaitThread = ();
while(!stopAwait) {
try {
Thread.sleep( 10000 );
} catch( InterruptedException ex ) {
// continue and check the flag
}
}
} finally {
awaitThread = null;
}
return;
}
// 开启socket监听server.xml中的shutdown端口
// 创建socket服务端
try {
awaitSocket = new ServerSocket(port, 1,
InetAddress.getByame(address));
} catch (IOException e) {
return;
}
// 默认false,进入while循环
while (!stopAwait) {
ServerSocket serverSocket = awaitSocket;
if (serverSocket == null) {
break;
}
// Wait for the next connection
Socket socket = null;
StringBuilder command = new StringBuilder();
InputStream stream;
try {
// accept阻塞监听端口
socket = serverSocket.accept();
// 设置阻塞超时时间10秒,如果超时抛异常,catch捕捉到重新进入while循环
socket.setSoTimeout(10 * 1000);
stream = socket.getInputStream();
} catch (SocketTimeoutException ste) {
continue;
}
// 从流中读取字符串
...
// 如果读取到字符串命令是"SHUTDOW"则,跳出循环,开始终止服务器
// shutdown变量是取server.xml中Server的shutdown属性
boolean match = ().equals(shutdown);
if (match) {
log.info(sm.getString("standardServer.shutdownViaPort"));
break;
} else
log.warn("StandardServer.await: Invalid command '"
+ () + "' received");
}
}
2.、停止Tomcat
- 最终调用Server的stop和destroy方法(下篇文章单独讲)
public void stop() {
try {
if (useShutdownHook) {
// 移除shutdown钩子,这个stop方法会停止server,不需要钩子再次执行
Runtime.getRuntime().removeShutdownHook(shutdownHook);
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
true);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
// 调用Server的stop和destroy方法
try {
Server s = getServer();
LifecycleState state = s.getState();
if (LifecycleState.STOPPIG_PREPpareTo(state) <= 0
&& LifecycleState.DESTROYEDpareTo(state) >= 0) {
// othing to do. stop() was already called
} else {
s.stop();
s.destroy();
}
} catch (LifecycleException e) {
("Catalina.stop", e);
}
}
- Bootstrap是一个启动引导类,本身没有太多启动关闭细节的实现
- 而是通过加载Catalina,对Catalina发号施令,调用start、stop等方法
#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格
上传时间: 2025-07-20 16:36:13
推荐阅读
留言与评论(共有 13 条评论) |
本站网友 济荷高速 | 25分钟前 发表 |
e1); } return; } long t2 = (); if(log.isInfoEnabled()) { log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms"); } // 注册关闭钩子 if (useShutdownHook) { if (shutdownHook == null) { shutdownHook = new CatalinaShutdownHook(); } // 向addShutdownHook方法传入的Thread | |
本站网友 无锡二手房 | 26分钟前 发表 |
后面另开一篇单独讲 try { getServer().init(); } catch (LifecycleException e) { ... } } Digester对象解析server.xml文件 解析server.xml文件 | |
本站网友 wakao | 19分钟前 发表 |
例如杀进程 | |
本站网友 miku5 | 14分钟前 发表 |
load与startload与start都是通过上一步获取到的catalinaDaemon对象反射调用catalina类的load与start方法这两个过程我们会在下面的Catalina内容中介绍load方法:代码语言:javascript代码运行次数:0运行复制private void load(String[] arguments) throws Exception { String methodame = "load"; Object param[]; Class<?> paramTypes[]; if (arguments==null || arguments.length==0) { paramTypes = null; param = null; } else { paramTypes = new Class[1]; paramTypes[0] = arguments.getClass(); param = new Object[1]; param[0] = arguments; } Method method = catalinaDaemon.getClass().getMethod(methodame | |
本站网友 李爱民 | 27分钟前 发表 |
Service | |
本站网友 弃之可惜的上一句 | 7分钟前 发表 |
解析conf/server.xml文件核心内容调用Server实现类StandardServer的init方法来初始化组件(下篇文章单独讲)代码语言:javascript代码运行次数:0运行复制public void load() { // 如果已经加载则退出 | |
本站网友 殡仪馆 | 24分钟前 发表 |
这发生在最后的非守护线程退出时 | |
本站网友 小儿腹泻贴 | 23分钟前 发表 |
其run方法即为自定义的shutdown时清理逻辑 Runtime.getRuntime().addShutdownHook(shutdownHook); LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).setUseShutdownHook( false); } } // 进入等待状态 // 启动类Bootstrap默认调start方法设置await=true if (await) { // main线程等待 | |
本站网友 中国劲酒能壮阳吗 | 7分钟前 发表 |
Invalid command '" + () + "' received"); } }2. | |
本站网友 张江高科苑 | 21分钟前 发表 |
停止Tomcat最终调用Server的stop和destroy方法(下篇文章单独讲)代码语言:javascript代码运行次数:0运行复制public void stop() { try { if (useShutdownHook) { // 移除shutdown钩子 | |
本站网友 北京医疗器械公司 | 17分钟前 发表 |
执行Server.stop(); stop(); } }2.1 | |
本站网友 人造脂肪 | 14分钟前 发表 |
反射实例化Catalina对象 Class<?> startupClass = catalinaLoader.loadClass("org.startup.Catalina"); Object startupInstance = startupClass.getCtructor().newInstance(); // Set the shared extensi class loader if (log.isDebugEnabled()) log.debug("Setting startup class properties"); String methodame = "setParentClassLoader"; Class<?> paramTypes[] = new Class[1]; paramTypes[0] = Class.forame("java.lang.ClassLoader"); Object paramValues[] = new Object[1]; paramValues[0] = sharedLoader; // 反射将sharedLoader设置为catalinaLoader的父类加载器 |