最新要闻
- 世界微速讯:理想汽车2022年亏损额创新高达20.32亿元
- 天天热议:腾势D9首创双枪充电 赵长江:比高压充电更节省社会成本
- 当前视讯!DXO排名全球第一!京东方认领荣耀Magic5/Pro屏幕
- 【全球播资讯】史诗级更新!雷军:小米12S Ultra 2倍变焦快捷按键正式上线
- 每日消息!iOS封闭垄断 俄罗斯怒罚苹果9.06亿卢布:后者服软已悄悄支付
- 李想:比亚迪汽车毛利率20%左右 跟理想汽车很接近 良心
- 世界最新:梅西第七次当选世界足球先生 FIFA年度最佳球员!网友泪目:实至名归
- 全球消息!游戏第一神U上新!AMD锐龙9 7950X3D超频后更强:性能完秒13900KS
- 天天热点!40吨重 我国长征九号重型火箭未来将发射大型天文望远镜:探测地外生命
- 湖人官方:詹姆斯明日因右脚伤势缺战灰熊
- 全球热消息:世界第二大水电站16台机组全部通过验收:自主研制 登顶世界水电
- 全球观速讯丨增程是落后技术?李想:理想汽车单车型研发投入中国行业最高
- 环球今热点:10多天还没到 市民吐槽快递延误 客服:你投诉去吧
- 天天速递!承诺员工1人1套房!董明珠被传与王自如恋爱 格力愤怒已报案
- 事与愿违下一句啥意思_事与愿违下一句是什么
- 印尼16月大男婴重达27公斤:比8岁男童还重
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
【世界热闻】tomcat源码分析(一)如何启动服务
(资料图片)
从startup.sh入手
os400=falsecase "`uname`" inOS400*) os400=true;;esacPRG="$0"while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : ".*-> \(.*\)$"` if expr "$link" : "/.*" > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fidonePRGDIR=`dirname "$PRG"`EXECUTABLE=catalina.shif $os400; then evalelse if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then echo "Cannot find $PRGDIR/$EXECUTABLE" echo "The file is absent or does not have execute permission" echo "This file is needed to run this program" exit 1 fifiexec "$PRGDIR"/"$EXECUTABLE" start "$@"
整个脚本核心就是最后一句代码, EXECUTABLE变量是catalina.sh, 代表执行catalina.sh, 参数是start, 再去对比了shutdown.sh, 两个脚本的核心都是调用catalina.sh传递的变量不同。
浏览catalina.sh脚本
整个脚本很长,我这里之截图了我们关心的脚本内容。 这段代码里, 除了能看到参数传递start, 最后会输出Tomcat started外,能看到调用了org.apache.catalina.startup.Bootstrap, 也就是说找到我们的程序入口,或者说找到了我们的程序的main函数。
shift eval $_NOHUP "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \ -D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \ -classpath "\"$CLASSPATH\"" \ -Djava.security.manager \ -Djava.security.policy=="\"$CATALINA_BASE/conf/catalina.policy\"" \ -Dcatalina.base="\"$CATALINA_BASE\"" \ -Dcatalina.home="\"$CATALINA_HOME\"" \ -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \ org.apache.catalina.startup.Bootstrap "$@" start \ >> "$CATALINA_OUT" 2>&1 "&" else eval $_NOHUP "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \ -D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \ -classpath "\"$CLASSPATH\"" \ -Dcatalina.base="\"$CATALINA_BASE\"" \ -Dcatalina.home="\"$CATALINA_HOME\"" \ -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \ org.apache.catalina.startup.Bootstrap "$@" start \ >> "$CATALINA_OUT" 2>&1 "&" fi if [ ! -z "$CATALINA_PID" ]; then echo $! > "$CATALINA_PID" fi echo "Tomcat started."
看到这里我们做个小小的总结:Tomcat本质上也是一个java程序,因此startup.sh会启动一个jvm来运行tomcat的启动类Bootstrap.java。
Bootstrap类核心功能
- 静态构造器部分, 主要初始化了CATALINA_HOME和CATALINA_BASE两个变量内容
- main函数方法部分一,创建和初始化daemon, 创建三个类加载器
- main函数方法部分二,控制tomcat的启动和停止
从Bootstrap.main方法开始
开始main方法之前,首先看两个关键属性
/*************守护进程对象**********/private static volatile Bootstrap daemon = null;/***守护程序用的catalina对象***/private Object catalinaDaemon = null;
Bootstrap#main
public static void main(String args[]) {synchronized (daemonLock) {if (daemon == null) {//初始化完成之前,不要对daemon赋值Bootstrap bootstrap = new Bootstrap();try { //调用初始化方法, 完成加载器的配置和初始化器的准备bootstrap.init();} catch (Throwable t) {handleThrowable(t);t.printStackTrace();return;}daemon = bootstrap;} else {//当作为服务正在运行时,如果调用停止方法,这将在一个新线程上进行,以确保使用正确的类加载器,防止出现未找到类的异常Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);}}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")) {daemon.setAwait(true); //Bootstrap加载daemon.load(args); //Bootstrap启动daemon.start();if (null == daemon.getServer()) {System.exit(1);}} else if (command.equals("stop")) {daemon.stopServer(args);} else if (command.equals("configtest")) {daemon.load(args);if (null == daemon.getServer()) {System.exit(1);}System.exit(0);} else {} }
public void init() throws Exception { //初始化类的三个加载器initClassLoaders();//设置线程类加载器, 将容器的加载器传入Thread.currentThread().setContextClassLoader(catalinaLoader);//加载安全类加载器SecurityClassLoad.securityClassLoad(catalinaLoader);//通过反射加载catalinaClass> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.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; //共享加载器Method method = startupInstance.getClass().getMethod(methodName, paramTypes); //对类加载器进行初始化赋值//调用catalina类内部的setParentClassLoader方法对catalina类内部的类加载赋值method.invoke(startupInstance, paramValues);//将创建好的startupInstance对象赋值给catalinaDaemoncatalinaDaemon = startupInstance;}
Catalina#load
Catalina类的load方法核心就解析config/server.xml并创建Server组件实例, 也就是我们在tomcat整体架构章节里了解的一个tomcat只有一个Server实例。 这部分代码块,我删掉了注释代码,try...catch, 只留下了核心业务代码。
public void load() {loaded = true;long t1 = System.nanoTime();initDirs();initNaming(); //利用digester类解析server.xml,得到容器的配置Digester digester = createStartDigester();InputSource inputSource = null;InputStream inputStream = null;File file = null;file = configFile();inputStream = new FileInputStream(file);inputSource = new InputSource(file.toURI().toURL().toString());if (inputStream == null) {inputStream = getClass().getClassLoader().getResourceAsStream(getConfigFile());inputSource = new InputSource(getClass().getClassLoader().getResource(getConfigFile()).toString());}if (inputStream == null) {inputStream = getClass().getClassLoader().getResourceAsStream("server-embed.xml");inputSource = new InputSource(getClass().getClassLoader().getResource("server-embed.xml").toString());}if (inputStream == null || inputSource == null) {return;}try {inputSource.setByteStream(inputStream);digester.push(this);digester.parse(inputSource);} catch (SAXParseException spe) {return;} catch (Exception e) {return;}getServer().setCatalina(this);getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());initStreams(); //服务器执行初始化 开始调用的Server的初始化方法注意Server是一个接口getServer().init();}
Catalina#start
public void start() {if (getServer() == null) {load();}if (getServer() == null) {return;}long t1 = System.nanoTime();//开始一个Server实例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 = System.nanoTime();if(log.isInfoEnabled()) {log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");}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) {await();stop();}}
从Bootstrap#createStartDigester方法中可以看到Server接口的实现类是StandardServer
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
Server接口继承了Lifecycle接口StandardServer类继承了抽象类LifecycleMBeanBase,同时实现了Server接口LifecycleMBeanBase抽象类又继承了抽象类LifecycleBase, 而LifecycleBase抽象类又实现了Lifecycle接口通过前面的调用链看出来Catalina.start会调用Server接口的start方法,而StandardServer实现类的start方法就追溯到了LifeCycleBase抽象的start方法, 这个类里定义了抽象方法startInternal让子类去实现。 在start方法中也调用了startInternal方法。
StandardServer类图
StandardServer#startInternal
protected void startInternal() throws LifecycleException {fireLifecycleEvent(CONFIGURE_START_EVENT, null);setState(LifecycleState.STARTING);globalNamingResources.start();synchronized (servicesLock) { //这里启动定义的多个servicefor (Service service : services) {service.start();}}}
根据Server的实现类StandardServer类,我顺便查看了其所在包, 看到了整个tomcat用到的核心组件的实现类都在这里了,比如StandardEngine, StandardService,StandardHost, 可以查看其他的实现类结构。
总结
结合上面的两张图片,以及上述的源码分析,我们就能总结出来整个startup.sh过程中完成的任务
- Tomcat本质上是一个java程序,因此startup脚本会启动一个jvm来运行tomcat的启动类Bootstrap.
- Bootstrap的主要任务就是初始化tomcat的类加载器,并且创建Catalina.
- Catalina是一个启动类,通过解析Server.xml创建相应组件,通过调用Server接口实现类去启动Server.
- StandardServer通过调用父类LifecycleBase的start方法,并且重写startInternal方法来启动Server和启动Service.
- Service组件的职责就是管理连接器和顶层容器,他会调用连接器和顶层容器的start方法.
- 容器组件负责启动管理子容器,并且调用Host的start方法, 将各层容器启动起来。
参考资料
https://juejin.cn/post/7155750621864263716https://2i3i.com/tomcat-code-3.htmlhttps://juejin.cn/post/7082681444182523934https://time.geekbang.org/column/article/97603https://zhuanlan.zhihu.com/p/344635709
-
【世界热闻】tomcat源码分析(一)如何启动服务
从startup sh入手os400=falsecase"`uname`"inOS400*)os400=true;;esacPRG="$0"w
来源: -
彻底理解 HashMap 及 LinkedHashMap,面试官请随便问!
来源:https: blog csdn net fuzhongmin05 article details 104355841下面基于JDK1 8的源码来学习HashMap及LinkedHashM
来源: -
当前焦点!轻量级CI/CD发布部署环境搭建及使用_02_docker安装jenkins
轻量级CI CD发布部署环境搭建及使用_02_docker安装jenkins授人以鱼不如授人以渔,如果说的别人都没明白...
来源: 【世界热闻】tomcat源码分析(一)如何启动服务
协同办公领域未来三大趋势(协同办公带来的机遇)
彻底理解 HashMap 及 LinkedHashMap,面试官请随便问!
当前焦点!轻量级CI/CD发布部署环境搭建及使用_02_docker安装jenkins
世界微速讯:理想汽车2022年亏损额创新高达20.32亿元
天天热议:腾势D9首创双枪充电 赵长江:比高压充电更节省社会成本
当前视讯!DXO排名全球第一!京东方认领荣耀Magic5/Pro屏幕
【全球播资讯】史诗级更新!雷军:小米12S Ultra 2倍变焦快捷按键正式上线
环球时讯:巧妙利用“慧言”机器人在安全场景中实践
每日消息!iOS封闭垄断 俄罗斯怒罚苹果9.06亿卢布:后者服软已悄悄支付
李想:比亚迪汽车毛利率20%左右 跟理想汽车很接近 良心
世界最新:梅西第七次当选世界足球先生 FIFA年度最佳球员!网友泪目:实至名归
全球消息!游戏第一神U上新!AMD锐龙9 7950X3D超频后更强:性能完秒13900KS
天天热点!40吨重 我国长征九号重型火箭未来将发射大型天文望远镜:探测地外生命
湖人官方:詹姆斯明日因右脚伤势缺战灰熊
全球热消息:世界第二大水电站16台机组全部通过验收:自主研制 登顶世界水电
全球观速讯丨增程是落后技术?李想:理想汽车单车型研发投入中国行业最高
环球今热点:10多天还没到 市民吐槽快递延误 客服:你投诉去吧
读Java性能权威指南(第2版)笔记04_ Java SE API技巧下
天天速递!承诺员工1人1套房!董明珠被传与王自如恋爱 格力愤怒已报案
事与愿违下一句啥意思_事与愿违下一句是什么
天天观热点:一元二次方程
全球报道:k8s中的PV和PVC
观察:Canvas画布
印尼16月大男婴重达27公斤:比8岁男童还重
焦点日报:最后一天赶紧换!苹果iPhone等设备全系电池明天涨价:最贵519元
关注:茶颜悦色回应满杯奶茶放久了变半杯:奶沫放久了会自己消掉
每日看点!小米Wi-Fi 7路由器BE7000亮相:4个2.5G口 定价千元买吗?
当前视点!3名《炉石传说》电竞运动员注册成功:然而暴雪禁止中国玩家参加炉石赛事
环球聚焦:东川区开展校园周边环境专项集中整治行动
资讯推荐:冒险岛辅助2023_冒险岛多趣辅助
记录写了6年代码的心得
今亮点!Django uwsgi问题解析
全球新消息丨不坑盒子:word/wps最强辅助工具2023最新版
天天热议:轻松超越i9-13900KS!AMD锐龙9 7950X3D首发评测:一半功耗、价格更低
【天天报资讯】电池容量最大的旗舰手机!荣耀Magic5 Pro图赏
天天速递!女生奶茶中喝出3个标签后细菌感染:商家赔付1000元及检查费用
天天视讯!做一个右键菜单 Vue 组件
天天热文:设计模式(十二)----结构型模式之代理模式和装饰者模式的区别
每日动态!闻着臭、吃着香?游客热衷打包柳州螺蛳粉 机场火车站回应
天天热点评!番茄涨价太狠 英国餐馆推出白披萨:网友吐槽太苦了
每日热文:荣耀Magic5 Pro全球首发硅碳负极电池:5450mAh超大容量
焦点观察:IDEA2022.3永久激活破解 最新激活 一键激活 亲测可用!
浅谈: sizeof()与指针
今亮点!0x07_自制操作系统My-OS实现键盘输出字符到屏幕
当前热议!Python中logging模块用法
世界滚动:C语言程序设计
天天最资讯丨有信免费电话使用攻略_有信怎么使用 免费
七夕 我们分手
微资讯!自带散热泵!一加推出45W液冷散热背夹:最高可降20℃
倒计时两分钟!SpaceX载人火箭发射最后关头被紧急叫停
世界资讯:七彩虹战戟CN700 1TB SSD评测:国产方案7.1GB/s读取、499元最香
GTX 750 Ti挑战大热单机:画面不忍直视
【热闻】“狗”气越来越足!第二代哈弗大狗正式上市:13.58万元起
【天天新视野】关于Ajax
路飞项目封装logger、response以及全局异常
最新消息:不怕被坑了!工信部出手:自动续费前5日应以显著方式提醒
1999元 极米Play 2投影仪上架:120寸大屏、Type-C供电
【世界时快讯】3D大颗粒 杰士邦特惠装30支:19.9元秒杀
异形方向盘有隐患吗?汽车博主:既不方便也不安全
从银行数字化转型来聊一聊,火山引擎 VeDI 旗下 ByteHouse 的应用场景
今日热讯:第123篇: JS函数属性与方法
Prometheus配置Alertmanager(钉钉告警)
全球微头条丨照坏了手机的激光雷达:会照瞎你的眼吗?
全球第一大癌!39岁女儿与67岁父亲先后确诊乳腺癌
世界微速讯:电动自行车电池容量正常但里程不足?5招教你解决
环球讯息:延迟骤降50%:魅族20系列首发游戏模式6.0
热文:秒独显的3GHz高频缩水 AMD锐龙7000HS上市前又挨一刀
房贷60万20年和30年利息差多少?两种还款方式利息对比
当前视点!WHAT IS PPM Encoder ?
焦点速递!记录--关于无感刷新Token,我是这样子做的
热点!如何使用appuploader制作apple证书
世界热资讯!JMeter的基础认识
【环球热闻】3月1日广东男篮主场见 威姆斯晒登机照:回家
【天天报资讯】Redis 缓存过期策略和内存淘汰策略
环球热头条丨【复盘】搭建日志平台的复盘与思考
天天微动态丨sed命令的使用(替换)
环球实时:软件工程阅读笔记
Android ADB 备忘清单_开发速查表分享
焦点资讯:美国高中地下发现加密货币矿场 7个月耗电费12万元
直屏手机为何越来越多了?原因有三个
新资讯:近250家公司用ChatGPT取代员工:过半考核优秀 能省10万美元
环球滚动:诺基亚60年首次更换Logo后:手机品牌继续沿用经典蓝底Logo
环球快报:比老司机还稳!特斯拉主动安全压线避让变道前车:车主惊叹
天天头条:三睾拟双身虫
Prometheus插件安装(mysql_exporter)
消息!河南农信社数字化转型实践方案
今日讯!剑指 Offer 55 - I. 二叉树的深度(java解题)
世界新动态:八代将军吉宗下载_八代将军吉宗
女子驾奇瑞小蚂蚁 高速上油门到底车速为0!4S店:退款退车
世界速看:暴雪联合时尚品牌推出《暗黑4》主题女装:这画风太诡异了
焦点播报:董明珠:格力员工一人一套房承诺不变
个人私生活被恶搞辣眼?董明珠:有些自媒体为了赚钱不择手段 低级下流无聊
一文看懂小米无线AR眼镜探索版:自研微手势 隔空就能刷抖音
数据结构刷题2023.02.27小记
天天即时看!Ubuntu-深度学习环境搭建(yolov3)
焦点滚动:学习swoole之前,你需要知道的几件事
世界看热讯:流氓APP再见!26条新规严管移动应用:不得欺骗误导用户
Win11 22H2“Moment 2”更新快了:将在未来几周推送
每日讯息!男子醉驾被查也不让新手妻子开车:为了不要出现更大的事故