Tomcat源码解析-启动过程分析之主干流程
找到catalina.sh脚本路径,将启动参数传递给catalina.sh执行。
catalina.sh start 最终会执行org.apache.catalina.startup.Bootstrap中的main方法,并把start参数传入。
以后分析Tomcat关闭的时候,也是一个套路,最终都会调用到org.apache.catalina.startup.Bootstrap的main方法,并把stop参数传入。
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
/** * 通过提供的脚本启动Tomcat时的主方法和入口点。 * * @param args 要处理的命令行参数 */ public static void main(String args[]) { //main使用的守护进程对象。 synchronized (daemonLock) { //daemon是volatile修饰的Bootstrap对象 if (daemon == null) { //在init()完成之前不要设置守护进程 Bootstrap bootstrap = new Bootstrap(); try { //初始化守护进程。 bootstrap.init(); } catch (Throwable t) { handleThrowable(t); t.printStackTrace(); return; } daemon = bootstrap; } else { // W当作为服务运行时,要停止的调用将位于新线程上, // 因此请确保使用了正确的类加载器,以防止出现一系列类未找到异常。直到init()完成 Thread.currentThread().setContextClassLoader(daemon.catalinaLoader); } } try { String command = "start"; if (args.length > 0) { command = args[args.length - 1]; } //命令解析与执行 if (command.equals("startd")) { args[args.length - 1] = "start"; daemon.load(args); daemon.start(); } else if (command.equals("stopd")) { args[args.length - 1] = "stop"; daemon.stop(); } else if (command.equals("start")) { //启动操作 //通过反射调用守护进程引用的org.apache.catalina.startup.Catalina实例的setAwait方法 daemon.setAwait(true); //调用Catalina实例的load方法 daemon.load(args); //start方法 daemon.start(); //反射调用Catalina实例的getServer方法,返回的对象为空时,终止当前运行的Java虚拟机。 if (null == daemon.getServer()) { System.exit(1); } } else if (command.equals("stop")) { //通过反射调用Catalina的stopServer方法。 daemon.stopServer(args); } else if (command.equals("configtest")) { daemon.load(args); if (null == daemon.getServer()) { System.exit(1); } System.exit(0); } else { log.warn("Bootstrap: command \"" + command + "\" does not exist."); } } catch (Throwable t) { // Unwrap the Exception for clearer error reporting if (t instanceof InvocationTargetException && t.getCause() != null) { t = t.getCause(); } handleThrowable(t); t.printStackTrace(); System.exit(1); } } |
1、初始化守护进程,获取类加载器和反射实例化org.apache.catalina.startup.Catalina。
2、根据启动参数start,通过反射,依次执行Catalina实例的setAwait、load、start方法。
1 setAwait
|
1
2
3
4
5
6
7
8
|
/** * Use await. */ protected boolean await = false; public void setAwait(boolean b) { await = b; } |
2 load
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
/** * 启动一个新的服务器实例。 */ public void load() { //防止重复加载。 if (loaded) { return; } loaded = true; long t1 = System.nanoTime(); //创建java.io.tmpdir文件夹 initDirs(); // Before digester - it may be needed //初始化jmx的环境变量 initNaming(); // Create and execute our Digester //创建和配置将用于启动的Digester。 //配置解析server.xml中各个标签的解析类 Digester digester = createStartDigester(); InputSource inputSource = null; InputStream inputStream = null; File file = null; try { //下面一大段都是为了加载conf/server.xml配置文件,失败就加载server-embed.xml ... try { inputSource.setByteStream(inputStream); //把Catalina作为一个顶级容器 digester.push(this); //解析过程会实例化各个组件,比如Server、Container、Connector等 digester.parse(inputSource); } catch (SAXParseException spe) { log.warn("Catalina.start using " + getConfigFile() + ": " + spe.getMessage()); return; } catch (Exception e) { log.warn("Catalina.start using " + getConfigFile() + ": ", e); return; } } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { // Ignore } } } //这里的server在解析xml之后就有值了,这是Server的Catalina信息 getServer().setCatalina(this); getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Stream redirection initStreams(); // Start the new server try { //生命周期init方法 getServer().init(); } catch (LifecycleException e) { if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new java.lang.Error(e); } else { log.error("Catalina.start", e); } } long t2 = System.nanoTime(); if (log.isInfoEnabled()) { log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms"); } } |
- 初始化JMX环境变量
- 创建和配置Digester
- 读取配置文件conf/server.xml配置文件,失败就加载server-embed.xml
- 通过Digester解析配置文件,并将当前Catalina作为最顶层容器,解析过程会实例化各种组件
- 设置Server组件的catalina信息
- 调用Server组件的生命周期init方法
下面看start方法:
3 start
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
/** * 启动一个新的服务器实例。 */ public void start() { //如果Server组件不存在,则重新执行load方法 if (getServer() == null) { load(); } //依然不存在就返回 if (getServer() == null) { log.fatal("Cannot start server. Server instance is not configured."); return; } long t1 = System.nanoTime(); // Start the new server try { //调用Server的start方法 getServer().start(); } catch (LifecycleException e) { log.fatal(sm.getString("catalina.serverStartFail"), e); try { //异常的时候调用Server的destroy方法 getServer().destroy(); } catch (LifecycleException e1) { log.debug("destroy() failed for failed Server ", e1); } return; } long t2 = System.nanoTime(); if (log.isInfoEnabled()) { log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms"); } // Register shutdown hook //注册关闭钩子 if (useShutdownHook) { if (shutdownHook == null) { // Catalina.this.stop(); shutdownHook = new CatalinaShutdownHook(); } Runtime.getRuntime().addShutdownHook(shutdownHook); // If JULI is being used, disable JULI's shutdown hook since // shutdown hooks run in parallel and log messages may be lost // if JULI's hook completes before the CatalinaShutdownHook() LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).setUseShutdownHook( false); } } // Bootstrap中会设置await为true,其目的在于让tomcat在shutdown端口阻塞监听关闭命令 if (await) { //等待收到正确的关机命令,然后返回。 await(); //停止现有的服务器实例。 stop(); } } |
- 调用Server组件的start方法,开启一个新的服务。
- 注册关闭钩子
- 让Tomcat在shutdown端口阻塞监听关闭命令
其中的load方法中的解析配置文件与注册组件、执行生命周期方法init;
start方法中的开启服务、注册关闭钩子、阻塞监听关闭指令等详细细节,将在后期慢慢分析。
Tomcat源码解析-启动过程分析之主干流程的更多相关文章
- 渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(下)
关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/08/12/es-code03/ 前提 上篇文章写完了 ES 流程启动的一部分,main 方法都入 ...
- 渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(上)
关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/08/11/es-code02/ 前提 上篇文章写了 ElasticSearch 源码解析 -- ...
- Tomcat源码分析——启动与停止服务
前言 熟悉Tomcat的工程师们,肯定都知道Tomcat是如何启动与停止的.对于startup.sh.startup.bat.shutdown.sh.shutdown.bat等脚本或者批处理命令,大家 ...
- Tomcat源码解析-整体流程介绍
一.架构 下面谈谈我对Tomcat架构的理解 总体架构: 1.面向组件架构 2.基于JMX 3.事件侦听 1)面向组件架构 tomcat代码看似很庞大,但从结构上看却很清晰和简单,它主要由一堆组件组成 ...
- Okhttp3源码解析(3)-Call分析(整体流程)
### 前言 前面我们讲了 [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析]( ...
- TOMCAT源码分析(启动框架)
建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握TOMCAT的框架的. 所以得实践.实践.再实践. 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动 ...
- 分布式事务_02_2PC框架raincat源码解析-启动过程
一.前言 上一节已经将raincat demo工程运行起来了,这一节来分析下raincat启动过程的源码 主要包括: 事务协调者启动过程 事务参与者启动过程 二.协调者启动过程 主要就是在启动类中通过 ...
- 【Android】Android 4.0 Launcher2源码分析——启动过程分析
Android的应用程序的入口定义在AndroidManifest.xml文件中可以找出:[html] <manifest xmlns:android="http://schemas. ...
- 【转】Android 4.0 Launcher2源码分析——启动过程分析
Android的应用程序的入口定义在AndroidManifest.xml文件中可以找出:[html] <manifest xmlns:android="http://schemas. ...
随机推荐
- 6.react 基础 - 关于 react 开发 的原则
1. 声明式开发 通过绑定元素 在数据变更时 对元素进行动态渲染 2. 可以与其他框架并存 不在React的绑定元素内, 可以使用其他框架 如 ( vue jQuery 等 ) 进行元素操作 3. 组 ...
- 17.3.12---urlparse模块的URL下载
1---urlparse模块是一个解析与泛解析Web网址URL字符串的一个工具 urlparse模块会将一个普通的url解析为6个部分,返回的数据类型都是元祖,同时,他还可以将已经分解后的url在组合 ...
- Gym102361E Escape
Link 首先我们可以推出一些有用的结论: 1.任意两个机器人之间的路线不能重合,但是可以垂直交叉. 2.如果一个格子没有转向器,那么最多允许两个机器人以相互垂直的方向通过. 3.如果一个格子有转向器 ...
- UML-类图-如何画引用类和集合?
1.画引用类 引用到了类时,需要画关联线,否则其他基本类型(int.string.date等)不画. 2.画集合 当然,方法1中可加入箭头.对应java代码: public class Sale { ...
- Reservoir Computing论文学习
目录 背景: RC优势: 储备池计算主要理论组成: ESNS数学模型 结构表示 状态方程和输出方程 计算过程 储备池的优化 GA:使用进化算法对参数进行优化: 基于随机梯度下降法的储备池参数优化 参考 ...
- java中 Spring 定时器定时任务Quartz的正确使用方法集配置
定时任务我想大家都不默认,现在流行的框架spring就带了定时任何 我的个人网站(http://www.yzcopen.com)上用户上传的文件都是用这套定时任务执行定时清除 第一步:在applica ...
- memset为int型数组初始化问题
头文件:#include <string.h>memset() 函数用来将指定内存的前n个字节设置为特定的值,其原型为: void * memset( void * ptr, int ...
- linux 下删除乱码的文件夹
[keke.zhaokk@gw2.mpi2.cm10 /home/keke.zhaokk] $ls -i 85082119 dataMining 85082939 ????֦???-???idޢ??? ...
- Debian8.8为普通用户添加sudo权限
1.进入root用户,su root 输入密码,我们首先修改 /etc/sudoers 文件的属性为可写权限# chmod +w /etc/sudoers2.编辑 vim /etc/sudoers,添 ...
- vs code打开文件显示的中文乱码
这种情况下,一般是编码格式导致的,操作办法: 鼠标点击之后,上面会弹出这个界面,双击选中 然后从UTF-8换到GB2312,或者自己根据情况,更改编码格式