tomcat生命周期和初始化及启动
tomcat初始化及启动
前面有一篇提到过下面的截图
这里可以看到启动的类为 org.apache.catalina.startup.Bootstrap 调用的是start方法。
那我们下载下来tomcat源码之后直接运行这个方法来看下执行日志(源码可以去官网下载,如果需要可以私信或留下邮箱) 十月 18, 2021 3:32:15 下午 org.apache.catalina.startup.Bootstrap init 信息: Bootstrap--------init() 十月 18, 2021 3:32:15 下午 org.apache.catalina.startup.Bootstrap load 信息: Bootstrap--------load() 十月 18, 2021 3:32:15 下午 org.apache.catalina.startup.Catalina load 信息: Catalina--------load() 十月 18, 2021 3:32:15 下午 org.apache.catalina.startup.Catalina load 警告: Unable to load server configuration from [D:workspacemyProject omcat8confserver.xml] 十月 18, 2021 3:32:15 下午 org.apache.catalina.startup.Bootstrap start 信息: Bootstrap--------start() 十月 18, 2021 3:32:15 下午 org.apache.catalina.startup.Catalina start 信息: Catalina--------start() 十月 18, 2021 3:32:15 下午 org.apache.catalina.startup.Catalina load 信息: Catalina--------load() 十月 18, 2021 3:32:15 下午 org.apache.catalina.startup.Catalina load 警告: Unable to load server configuration from [D:workspacemyProject omcat8confserver.xml] 十月 18, 2021 3:32:15 下午 org.apache.catalina.startup.Catalina start
由上面的日志可以看出来一个大体的执行顺序
Bootstrap-init() => Bootstrap-load() => Catalina-load() => Bootstrap-start()=> Catalina-start()=> Catalina-load() Tomcat运行入口类/** * Tomcat运行入口类 */ public static void main(String args[]) { //初始化阶段 init() if (daemon == null) { // Don"t set daemon until init() has completed Bootstrap bootstrap = new Bootstrap(); try { bootstrap.init(); } catch (Throwable t) { handleThrowable(t); t.printStackTrace(); return; } daemon = bootstrap; } else { // When running as a service the call to stop will be on a new // thread so make sure the correct class loader is used to prevent // a range of class not found exceptions. Thread.currentThread().setContextClassLoader(daemon.catalinaLoader); } //运行阶段 start() 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")) { daemon.setAwait(true); daemon.load(args); daemon.start(); } 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 { 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); } }init阶段
从最开始进行跟进查看Bootstrap.init()
Bootstrap.init() public void init(String[] arguments) throws Exception { init(); load(arguments); }
init // Bootstrap init public void init() throws Exception { log.info("Bootstrap--------init()"); // 初始化 classLoader // commonLoader、catalinaLoader、sharedLoader, initClassLoaders(); Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); // Load our startup class and call its process() method if (log.isDebugEnabled()) log.debug("Loading startup class"); // 反射获取 Catalina 类对象 Class<?> startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); // Set the shared extensions class loader if (log.isDebugEnabled()) log.debug("Setting startup class properties"); String methodName = "setParentClassLoader"; Class<?> paramTypes[] = new Class[1]; paramTypes[0] = Class.forName("java.lang.ClassLoader"); Object paramValues[] = new Object[1]; paramValues[0] = sharedLoader; // 反射执行方法 setParentClassLoader 方法,设置其 parentClassLoader 为 sharedLoader Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); // 设置守护对象 catalinaDaemon = startupInstance; }
load private void load(String[] arguments) throws Exception { log.info("Bootstrap--------load()"); // Call the load() method String methodName = "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; } // 反射获取方法 load 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 public void load() { log.info("Catalina--------load()"); long t1 = System.nanoTime(); // 初始化一个dir java.io.tmpdir. 失败打日志,不影响后面执行 initDirs(); // Before digester - it may be needed initNaming(); // 获取带有配置的 Digester 对象 Digester digester = createStartDigester(); InputSource inputSource = null; InputStream inputStream = null; File file = null; try { try { // 读取 conf/server.xml file = configFile(); inputStream = new FileInputStream(file); inputSource = new InputSource(file.toURI().toURL().toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", file), e); } } if (inputStream == null) { try { inputStream = getClass().getClassLoader() .getResourceAsStream(getConfigFile()); inputSource = new InputSource (getClass().getClassLoader() .getResource(getConfigFile()).toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", getConfigFile()), e); } } } // This should be included in catalina.jar // Alternative: don"t bother with xml, just create it manually. if (inputStream == null) { try { inputStream = getClass().getClassLoader() .getResourceAsStream("server-embed.xml"); inputSource = new InputSource (getClass().getClassLoader() .getResource("server-embed.xml").toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", "server-embed.xml"), e); } } } if (inputStream == null || inputSource == null) { if (file == null) { log.warn(sm.getString("catalina.configFail", getConfigFile() + "] or [server-embed.xml]")); } else { log.warn(sm.getString("catalina.configFail", file.getAbsolutePath())); if (file.exists() && !file.canRead()) { log.warn("Permissions incorrect, read permission is not allowed on the file."); } } return; } try { inputSource.setByteStream(inputStream); digester.push(this); // 解析server.xml的配置,告诉Digester哪个xml标签应该解析成什么类,如果我们要改变server.xml的某个属性值(比如优化tomcat线程池), // 直接查看对应实现类的setXXX方法即可 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 } } } // 设置 Catalina 信息 getServer().setCatalina(this); // Catalina目录 getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); // 工作目录 getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Stream redirection initStreams(); // 最后调用server的init方法 try { 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"); } }
可以看到在Catalina.load 中调用了 调用server的init方法。这个server实例是哪个类呢? // 获取带有配置的 Digester 对象 Digester digester = createStartDigester();
查看createStartDigester这个方法: ... // Server信息 digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className"); digester.addSetProperties("Server"); digester.addSetNext("Server", "setServer", "org.apache.catalina.Server"); ...
所以我们接着看StandardServer的init方法 /** * Tomcat中Server的初始化方法 */ @Override protected void initInternal() throws LifecycleException { log.info("StandardServer--------init()"); super.initInternal(); onameStringCache = register(new StringCache(), "type=StringCache"); MBeanFactory factory = new MBeanFactory(); factory.setContainer(this); onameMBeanFactory = register(factory, "type=MBeanFactory"); globalNamingResources.init(); if (getCatalina() != null) { ClassLoader cl = getCatalina().getParentClassLoader(); while (cl != null && cl != ClassLoader.getSystemClassLoader()) { if (cl instanceof URLClassLoader) { URL[] urls = ((URLClassLoader) cl).getURLs(); for (URL url : urls) { if (url.getProtocol().equals("file")) { try { File f = new File (url.toURI()); if (f.isFile() && f.getName().endsWith(".jar")) { ExtensionValidator.addSystemResource(f); } } catch (URISyntaxException e) { // Ignore } catch (IOException e) { // Ignore } } } } cl = cl.getParent(); } } // 初始化我们定义的服务 for (int i = 0; i < services.length; i++) { services[i].init(); } }
上面可以看到 Server.init里面又调用了 service的init。沿着上面的思路进行跟踪初始化的源码(我下面就不展示了)。 start阶段
在运行入口类中我们就可以看到启动相关的代码 if (command.equals("startd")) { args[args.length - 1] = "start"; // 初始化 daemon.load(args); // 启动 这个daemnon在初始化的时候已经知道就是Catalina daemon.start(); }
Catalina.start public void start() { log.info("Catalina--------start()"); // 如果没有server就先调用一遍初始化 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方法, // 初始化的时候已经可以看到 server 是 standardServer 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"); } // Register shutdown hook if (useShutdownHook) { if (shutdownHook == null) { // shutdown线程 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); } } // 手写嵌入式toncat借鉴 if (await) { await(); stop(); } }
嗯,后面的过程其实和初始化一样的。
下面是一个时序图,有兴趣的可以自己画一下。
tomcat生命周期
在上面看init阶段的时候其实有一个疑问就是明明是调用的是init方法,但是实际上在对应的类(例如:StandardServer)中的源码却是initInternal方法。拿StandardServer来举例 // Catalina.load 方法 ... // 最后调用server的init方法 try { 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); } } ... // 这里返回的是server接口 public Server getServer() { return server; }
StandardServer对应的类关系图谱:
LifecycleBase中有如下代码,这样就可以看到在init里面调用的 initInternal()。 // @Override public final synchronized void init() throws LifecycleException { if (!state.equals(LifecycleState.NEW)) { invalidTransition(Lifecycle.BEFORE_INIT_EVENT); } try { setStateInternal(LifecycleState.INITIALIZING, null, false); initInternal(); setStateInternal(LifecycleState.INITIALIZED, null, false); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); setStateInternal(LifecycleState.FAILED, null, false); throw new LifecycleException( sm.getString("lifecycleBase.initFail",toString()), t); } }
在来看下下面类架构图:可以看到整个过程中的大部分类最终都会集成或实现Lifecycle
这个是一个典型的模板模式
整体生命周期如下:
当然Tomcat在整体过程中也涉及到状态的管理,使用的就是state属性。下面截图是状态检查的例子之一
如何选择监听喇叭阜新声艺视听当你花了数个小时在房间或工作室仔细调整混音后,它听起来很不错。但当你到其他录音室,车上,或是手机上,甚至在酒吧的PA系统声音却听起来完全不一样低音很轰,高音刺耳,而人声和乐器相对于
可油可电,超级混动唐dmi周末景区自驾游周末跟家人一起去景区自驾游,太不容易等到周末应该去山里避避暑!这个天气一路上并不是那么凉快呀,空调一直打开,座椅通风也必须开着,不得不赞赏一下这个座椅通风的功能。智能语音说一句你好
关于比亚迪唐使用后的真实感受作为唐车车主,个人观点如下1车是好车,国产中的精品,目前还是比较满意,动力绝对充沛,外观也很不错,内饰做工什么都不差。2做工不差,但是在很多细节方面稍微还是有点瑕疵的,但是作为实质
万达轻资产转型,有远见!王健林为何总是能抓住机遇?市场竞争无时无刻不在,任何人都不能放松警惕,那些原本的优势,只要稍有懈怠就会被削弱。如今我国的市场经济体制改革不断深入,开放程度也进一步扩大,因此各地都高招频出,采取措施招商引资。
改造一个简易直流电源对于收音机爱好者来说,维修调试或者日常使用,如果有一个万能稳压电源就会很方便。虽然我也有专业的维修电源,但是还是没有小变压器来的方便。对于这个电源的要求主要有几点要是线性稳压,纹波
5部超级好看的港片,可惜好多人不知道曾经的港片黄金时代经典实在太多,总有我们错过的冷门佳作,比如今天这五部,强烈安利!01柔道龙虎榜导演杜琪峰豆瓣7。6主演古天乐郭富城应采儿豆瓣评选21世纪国产片TOP20,这部柔道
人工智能时代图像传感器的新方向随着人工智能时代的来临,相应的芯片产品和行业也产生了相应的新方向。在人工智能的各个分支中,机器视觉无疑是应用最广泛的方向,它支撑着诸如人脸检测工业异常检测手势识别等诸多重要的应用。
NXPConnects2021线上峰会强势来袭!高峰对话,大咖云集,精彩盛宴不容错过NXPCONNECTS2021在行业大变局的背景下,探寻行业发展动力,洞察发展趋势,是半导体人不断思考与讨论的问题。NXPConnects2021线上峰会将于11月重磅推出,全球三
2020年4月4日清明节上午,长鸣的汽笛,回荡在城市上空视频加载中生命对于人只有一次。向在抗击新冠肺炎疫情战斗中牺牲的烈士致敬!为逝世同胞致哀!活着的我们,生活还要继续。让汽笛声警示我们,不要忘记自然或社会中无处不在的风险,不要忘记敌对
电器产业该如何做?2021中国电器新消费报告解读电器消费趋势新一代智能技术与电器产业的深度融合,给传统电器产业的转型升级注入了强劲驱动力,也给消费者带来了更好的用户体验。11月4日,京东电器京东消费及产业发展研究院联合新华网大数据中心发布了
商品价格这么定,利润不翻倍都难不知道大家有没有注意到,有些电影院售高端瓶装饮用水,价格不等在2040元之间一瓶。为什么要在这个场所卖这么高价位的水呢?是为了营造这个电影院十分的高级吗?是为了吸引更多的人群来这看
模拟数播各领风骚,JR唱盘大丹悉数上阵乐燊贸易SIAV2021回顾SIAV2021于9月2426日在上海新锦江大酒店和锦江饭店锦楠楼举行。香港乐燊贸易公司今年于新锦江百合厅A厅B厅,展出旗下代理的JRTransrotor唱盘Koetsu唱头Ree
奕浩电器HECO,感受不一样的德国声苏州奕浩电器有限公司于锦楠楼590591房展示旗下代理的HECO音箱,包括旗舰Direkt和CelanRevolution系列。首先亮相的,是旗舰Direkt(见题图),2路2单元
南方房产中介,别老东北怎么怎么了东北房子便宜,也没收割普通老百姓。随便买套房,随便跌,都没南方中介的中介费高,咋地,你们房子金子做的啊?说说杭州深圳,你们的杠杆到第几到了?现在已经开始跌了,跑得快比赛是不是开始了
东北经济还需要熬多久?南方是不是真的四季常春了?第一,北方重工业南方重轻工业和科技。第二,南方对外贸易发达,烫平了全球市场,也全军覆灭了北方,电子商务独步全球。第三,房地产嫁接在大厂华为大小老板,南方房价高。以上三条是事实,不单
黑龙江大庆房价走势如何?看这里在过去的3月份,黑龙江3个城市同比下降,分别是哈尔滨(同比下降2。66)大庆(同比下降1。88)七台河(同比下降5。71)4个城市环比下降,分别是鹤岗(环比下降7。17)鸡西(环比
石油之后,大庆出路在哪?石油大学与沃尔沃联手建新能源之都建议大庆的东北石油学发力新能源,建成新能源大学。大庆依托沃尔沃,打造新能源全产业链,建设成新能源之都。大庆是中国石油之都,当年的大庆油田凭一己之力给共和国输入澎湃动力,为共和国立下
回顾卓丽飞翔系列品鉴会圆满结束2021年10月16日,卓丽飞翔系列主题品鉴会在广州锦融音响圆满举行!这个品鉴会可以说是疫情过后首个发烧友大型交流会,几十名音响爱好者来到现场品鉴。让我们一起来回顾一下品鉴会的现场
Roksan新Attessa系列4款新机即将发布Roksan于近日宣布,将发布4款新机型,均为其入门系列Attessa中的产品,包括AttessaTurntable黑胶唱盘AttessaIntegratedAmplifier合并
BowersampampampWilkins800钻石系列荣耀问世追求卓越,精益求精如何让世界上最成功的经典高端扬声器系列再上一个台阶?过去6年里,这是Steyning研发中心(SRE)全球知名音响品牌BowersWilkins(宝华韦健)工程设计团队的所在地优秀
力必拓智慧公厕随着我国城镇化建设步伐的不断加快,城市公共设施的智能化,已是智慧城市建设的重要标志。公共厕所是城市公共服务设施不可或缺的组成部分,是展示城市文明形象和公共服务水平的窗口。公厕做为现
4G无线路由智能洗车T2801。行业背景当前,自助洗车的应用场景非常广泛。不过,随着运营规模的不断扩大,维护不便成本增加等现状,也成为摆在运营商面前的难题,主要表现在l室外大规模布线或WiFi联网不便l人工巡