网站首页 > 技术文章 正文
image.png
Tomcat的启动核心流程
??前面给大家介绍了Tomcat中的生命周期的设计,掌握了这块对于我们分析Tomcat的核心流程是非常有帮助的,也就是我们需要创建相关的核心组件,比如Server,Service肯定都绕不开生命周期的方法。
image.png
1.启动的入口
??你可以通过脚本来启动Tomcat服务(startup.bat),但如果你看过脚本的命令,你会发现最终调用的还是Bootstrap中的main方法,所以我们需要从main方法来开始
image.png
??然后我们去看main方法中的代码,我们需要重点关注的方法有三个
- bootstrap.init()方法
- load()方法
- start()方法
??也就是在这三个方法中会完成Tomcat的核心操作。
2.init方法
??我们来看下init方法中的代码,非核心的我们直接去掉
public void init() throws Exception {
// 创建相关的类加载器
initClassLoaders();
// 省略部分代码...
// 通过反射创建了 Catalina 类对象
Class<?> startupClass = catalinaLoader
.loadClass("org.apache.catalina.startup.Catalina");
// 创建了 Catalina 实例
Object startupInstance = startupClass.getConstructor().newInstance();
// 省略部分代码...
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
// 把 sharedLoader 设置为了 commonLoader的父加载器
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
// Catalina 实例 赋值给了 catalinaDaemon
catalinaDaemon = startupInstance;
}
- 首先是调用了initClassLoaders()方法,这个方法会完成对应的ClassLoader的创建,这个比较重要,后面专门写一篇文章来介绍。
- 通过反射的方式创建了Catalina的类对象,并通过反射创建了Catalina的实例
- 设置了类加载器的父子关系
- 用过成员变量catalinaDaemon记录了我们创建的Catalina实例
??这个是通过bootstrap.init()方法我们可以获取到的有用的信息。然后我们继续往下面看。
3.load方法
??然后我们来看下load方法做了什么事情,代码如下:
private void load(String[] arguments) throws Exception {
// Call the load() method
String methodName = "load"; // 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;
}
// catalinaDaemon 就是在 init中创建的 Catalina 对象
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled()) {
log.debug("Calling startup class " + method);
}
// 会执行 Catalina的load方法
method.invoke(catalinaDaemon, param);
}
??上面的代码非常简单,通过注释我们也可以看出该方法的作用是调用 Catalina的load方法。所以我们还需要加入到Catalina的load方法中来查看,代码同样比较长,只留下关键代码
public void load() {
if (loaded) {
return; // 只能被加载一次
}
loaded = true;
initDirs(); // 废弃的方法
// Before digester - it may be needed
initNaming(); // 和JNDI 相关的内容 忽略
// Create and execute our Digester
// 创建并且执行我们的 Digester 对象 Server.xml
Digester digester = createStartDigester();
// 省略掉了 Digester文件处理的代码
getServer().setCatalina(this); // Server对象绑定 Catalina对象
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection
initStreams();
// 省略掉了部分代码...
getServer().init(); // 完成 Server Service Engine Connector等组件的init操作
}
把上面的代码简化后我们发现这个Load方法其实也是蛮简单的,就做了两件事。
- 通过Apache下的Digester组件完成了Server.xml文件的解析
- 通过getServer().init() 方法完成了Server,Service,Engin,Connector等核心组件的初始化操作,这块和前面的LifecycleBase呼应起来了。
image.png
??如果生命周期的内容不清楚,请看上一篇文章的介绍。
4.start方法
??最后我们来看下start方法的代码。
public void start() throws Exception {
if (catalinaDaemon == null) {
init(); // 如果 catalinaDaemon 为空 初始化操作
}
// 获取的是 Catalina 中的 start方法
Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
// 执行 Catalina 的start方法
method.invoke(catalinaDaemon, (Object [])null);
}
??上面的代码逻辑也很清楚,就是通过反射的方式调用了Catalina对象的start方法。所以进入Catalina的start方法中查看。
public void start() {
if (getServer() == null) {
load(); // 如果Server 为空 重新 init 相关的组件
}
if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
// Start the new server 关键方法--->启动Server
try {
getServer().start();
} catch (LifecycleException e) {
// 省略...
}
// 省略...
// Register shutdown hook 注册关闭的钩子
if (useShutdownHook) {
// 省略...
}
if (await) {
await();
stop();
}
}
??通过上面的代码我们可以发现核心的代码还是getServer.start()方法,也就是通过Server对象来嵌套的调用相关注解的start方法。
image.png
5.核心流程的总结
我们可以通过下图来总结下Tomcat启动的核心流程
image.png
??从图中我们可以看到Bootstrap其实没有做什么核心的事情,主要还是Catalina来完成的。
??本文只是从Tomcat的启动核心流程来讲解,还有些具体的实现细节没有介绍到,会在后续的文章中给大家细细到来,欢迎关注收藏哦!
猜你喜欢
- 2024-09-20 Java创建对象的6种方式(java什么是面向对象)
- 2024-09-20 java与数据库结合学习(java如何与数据库建立连接)
- 2024-09-20 「java设计模式」——代理模式(案例解析)
- 2024-09-20 反射魔镜:Java编程中的神秘力量,你真的会用了吗?
- 2024-09-20 Java反射详解(java反射的用法)
- 2024-09-20 JAVA基础(java基础题库及答案)
- 2024-09-20 java反射机制(Java反射机制主要提供了以下哪些功能)
- 2024-09-20 Java面试基础(java面试基础题及答案整理)
- 2024-09-20 同事跳槽到百度,总结的30道Java面试必问题目,限时领取
- 2024-09-20 Java反射(java下一页)
- 1514℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 562℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 507℃MySQL service启动脚本浅析(r12笔记第59天)
- 486℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 484℃启用MySQL查询缓存(mysql8.0查询缓存)
- 464℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 444℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 441℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)