面试官:来说说Tomcat的启动过程是什么样子的
阿粉最近在疯狂的研究各种用的工具里面的源码实现,之前给大家都专门的去扣了一下 JDK 里面自带的exe程序,这次阿粉开始更加无聊,直接开始搞Tomcat。
tomcat分析">1.Tomcat分析
阿粉知道作为一个 Java 资深开发人员,对 Tomcat 那是再熟悉不过了,bin目录、conf目录、webapps目录,对这些目录熟悉的简直不能再熟悉了。一言不合就是一个shutdown.sh,或者来个shutdown.bat,但是你知道你的启动startup.bat,和startup.sh他们的启动过程是什么过程么?接下来我们就开始进入分析吧。
2.Tomcat的整体结构图
这个整体结构图可不是大家想的目录结构图,目录结构图阿粉就不给大家展示了,自己去打开你的 Tomcat,里面就有你想看到目录结构图,那么整体结构图是什么样子的呢?
给大家解释一下这个图的意思,
这个图就把里面的包含关系说的是明明白白了,为什么这么说呢?因为一个Server中可以存在多个服务,也就是多个Service,而一个Service中可以存在多个Connector,但是只能存在一个Container,是不是就非常的清晰了呢?
3.Tomcat的启动过程
接下来我们就来看看源码里面的启动过程吧,Bootstrap类中的启动过程。
这个类的位置是在tomcat的catalina的包里面,大家看一下主方法也就是所谓的main方法,
1 2 3 4 5 6 7 8 9 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 | public static void main(String[] args) {//对象初始化 if (daemon == null) { Bootstrap bootstrap = new Bootstrap(); try { bootstrap.init(); } catch (Throwable var3) { handleThrowable(var3); var3.printStackTrace(); return; } daemon = bootstrap; } else { 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")) { daemon.setAwait(true); //加载并且启动 daemon.load(args); 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 { log.warn("Bootstrap: command \"" + command + "\" does not exist."); } } catch (Throwable var4) { Throwable t = var4; if (var4 instanceof InvocationTargetException && var4.getCause() != null) { t = var4.getCause(); } handleThrowable(t); t.printStackTrace(); System.exit(1); } }
|
main方法里面的存在也很简单,先进行init的操作,然后再执行start,也就是说,启动过程中,首先要进行初始化,然后接下来再进行启动,最后阶段在来个stop,这样才算完整嘛。
在这里的start方法和stop方法调用的分别就是调用了Server内部的start和stop方法,而这三个方法都是按照图中的层级结构来的,先从Server的load,start,stop,然后Server的start再调用Service的start,而Service的start调用的就是Connector和Container的start方法了,这从外到内的启动,就可以把Tomcat完整的启动起来了。
我们在接下来就继续从外到内的启动开始分析一波。
3.1 Catalina启动过程
上面的启动入口我们已经成功找到了,那么我们就继续来,对象初始化完成后,执行了init的方法
1 2 3 4 5 6 | Bootstrap bootstrap = new Bootstrap();try { bootstrap.init(); } catch (Throwable var3) {
|
就是上面的这个,如果参数为空了,那么就开始调用start了,那么start方法是什么呢?
1 2 3 4 5 6 7 8 | public void start() throws Exception {if (this.catalinaDaemon == null) { this.init(); } Method method = this.catalinaDaemon.getClass().getMethod("start", (Class[])null); method.invoke(this.catalinaDaemon, (Object[])null); }
|
上面的start方法就是直接使用invoke的方法映射到了catalinaDaemon,也就是到了Catalina的start的方法上,
而这个Catalina的启动无非也就是调用了同样的方法,setAwait方法,load方法,start方法,
同样的setAwait方法比较少,阿粉就不给大家看了,无非就是个判断,而load方法一定得看,
1 2 3 4 5 6 7 8 9 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 | if (!this.loaded) {this.loaded = true; long t1 = System.nanoTime(); try { inputSource.setByteStream((InputStream)inputStream); digester.push(this); digester.parse(inputSource); break label242; } catch (SAXParseException var28) { log.warn("Catalina.start using " + this.getConfigFile() + ": " + var28.getMessage()); return; } catch (Exception var29) { log.warn("Catalina.start using " + this.getConfigFile() + ": ", var29); } } finally { if (inputStream != null) { try { ((InputStream)inputStream).close(); } catch (IOException var23) { ; } } } return; } try { //此处同样调用的Server的init方法, this.getServer().init(); } catch (LifecycleException var24) { if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new Error(var24); } log.error("Catalina.start", var24); } long t2 = System.nanoTime(); if (log.isInfoEnabled()) { log.info("Initialization processed in " + (t2 - t1) / 1000000L + " ms"); } }
|
而从这里就开始进入下一步了,Server的启动过程,因为从Catalina里面已经找到了getServer的初始化方法,接下来就是要进行Server的初始化,然后加载,然后启动的过程了。
3.2 Server的启动过程
Server是Tomcat里面的接口,而不是类,那么我们就只能去找实现它的子类来于是就找到了StandardServer extends LifecycleMBeanBase implements Server。
阿粉一看有继承,还有实现,那就先看看LifecycleMBeanBase这个被继承的类,于是再次去看了它,
1 | public abstract class LifecycleMBeanBase extends LifecycleBase implements JmxEnabled {
|
嗯?还有继承?继续往下扒拉,
1 | public abstract class LifecycleBase implements Lifecycle {
|
终于算是找到了,
阿粉一看这init方法和start方法又调用了initInternal()和startInternal(),找来找去又回去了,而阿粉也从这里知道了,模板方法,是有自己的子类具体实现
于是回到了StandardServer自己的init和start方法,
1 2 3 4 5 6 7 8 9 10 11 12 | protected void startInternal() throws LifecycleException {this.fireLifecycleEvent("configure_start", (Object)null); this.setState(LifecycleState.STARTING); this.globalNamingResources.start(); Object var1 = this.servicesLock; synchronized(this.servicesLock) { for(int i = 0; i < this.services.length; ++i) { this.services[i].start(); } } }
|
总得来说就是,StandardServer继承自LifecycleMBeanBase,而LifecycleMBeanBase继承自LifecycleBase,而LifecycleBase类中的模板方法,又让自己的子类去进行具体的实现,但是我们要知道他的Tomcat生命周期中存在这些内容才行。
图中都说了,Server里面有Service,那么一定就有,我们得去找找看,于是阿粉再次去找并且去看它到底是个什么意思,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public void addService(Service service) { service.setServer(this); Object var2 = this.servicesLock; synchronized(this.servicesLock) { Service[] results = new Service[this.services.length + 1]; System.arraycopy(this.services, 0, results, 0, this.services.length); results[this.services.length] = service; this.services = results; if (this.getState().isAvailable()) { try { service.start(); } catch (LifecycleException var6) { ; } } this.support.firePropertyChange("service", (Object)null, service); } }
|
位置是在Server的接口中出现了增加和删除Service的方法,Server的init方法和start方法循环去调用每个Service的init方法和start方法。
接下来我们看看Service的具体实现,找到StandardService:
1 2 3 4 5 6 7 8 9 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 | protected void initInternal() throws LifecycleException {super.initInternal(); if (this.engine != null) { this.engine.init(); } Executor[] arr$ = this.findExecutors(); int len$ = arr$.length; int len$; for(len$ = 0; len$ < len$; ++len$) { Executor executor = arr$[len$]; if (executor instanceof JmxEnabled) { ((JmxEnabled)executor).setDomain(this.getDomain()); } executor.init(); } this.mapperListener.init(); Object var11 = this.connectorsLock; synchronized(this.connectorsLock) { Connector[] arr$ = this.connectors; len$ = arr$.length; for(int i$ = 0; i$ < len$; ++i$) { Connector connector = arr$[i$]; try { connector.init(); } catch (Exception var9) { String message = sm.getString("standardService.connector.initFailed", new Object[]{connector}); log.error(message, var9); if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new LifecycleException(message); } } } } }
|
而在方法中主要调用Executor,mapperListener,executor的init方法。
connector之前已经有了,而这个mapperListener就是Mapper的监听器,用来坚挺container容器的变化。
文献参考
《深入剖析Tomcat》
《Tomcat架构解析》
《Servlet/JSP深入详解》
以上是 面试官:来说说Tomcat的启动过程是什么样子的 的全部内容,
来源链接:
utcz.com/a/130170.html