当前位置: 首页 > 科技观察

面试官:给大家说说Tomcat的启动流程是什么样的

时间:2023-03-20 20:10:49 科技观察

本文转载自微信公众号《Java极客技术》,作者鸭血范。转载本文请联系Java极客技术公众号。阿芬最近在疯狂研究各种工具中的源码实现。之前给大家专门介绍一下JDK自带的exe程序。这一次,阿芬更加无聊了,开始着手做Tomcat。一、Tomcat分析阿芬知道,作为一名资深的Java开发人员,对Tomcat是非常熟悉的。熟悉bin目录、conf目录、webapps目录的人,对这些目录几乎都不熟悉。一个不同意是一个shutdown.sh,还是一个shutdown.bat,但是你知道你的启动startup.bat和startup.sh的启动过程是什么吗?接下来,我们开始分析。2、Tomcat的整体结构图这个整体结构图并不是大家认为的目录结构图。我不会给你看目录结构图。自己打开你的Tomcat,里面有一张你想看的目录结构图。那么整体结构图是什么样子的呢?我来解释一下这张图的意思,Server:整个服务器。服务:专项服务。Connector:提供Socket与请求、响应的连接。容器:用于封装和管理Servlet,以及具体的处理请求。这张图很清楚的展示了里面的包含关系。你为什么这么说?因为一个Server里面可以有多个Service,也就是多个Service,一个Service里面可以有多个Connector,但是只能有一个Container,是不是很清楚?3、Tomcat的启动过程接下来我们看一下源码中的启动过程,Bootstrap类中的启动过程。这个类的位置在tomcat的catalina包中。我们看一下main方法,也就是所谓的main方法,publicstaticvoidmain(String[]args){//对象初始化if(daemon==null){Bootstrapbootstrap=newBootstrap();try{bootstrap.init();}catch(Throwablevar3){handleThrowable(var3);var3.printStackTrace();return;}daemon=bootstrap;}else{Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);}try{Stringcommand="开始";if(args.length>0){command=args[args.length-1];}if(command.equals("startd")){args[args.length-1]="start";//Loaddaemon.load(args);//启动daemon.start();}elseif(command.equals("stopd")){args[args.length-1]="stop";//停止daemon.stop();}elseif(command.equals("start")){daemon.setAwait(true);//加载并启动daemon.load(args);daemon.start();if(null==daemon.getServer()){System.exit(1);}}elseif(command.equals("stop")){daemon.stopServer(args);}elseif(command.equals("configtest")){daemon.load(args);if(null==daemon.getServer()){System.exit(1);}System.exit(0);}else{log.warn("Bootstrap:command\""+command+"\"doesnotexist.");}}catch(Throwablevar4){Throwablet=var4;if(var4instanceofInvocationTargetException&&var4.getCause()!=null){t=var4.getCause();}handleThrowable(t);t.printStackTrace();System.exit(1);}}main方法中的存在也很简单,先执行init操作,然后Executestart,也就是说在启动过程中,要先初始化,然后再启动。最后一个阶段是停止,这样它就完成了。load方法:其实load方法就是根据server.xml文件创建Server,调用Server的init方法进行初始化。start方法:start方法很直接,启动服务器。停止方法:停止方法相同,停止服务器。这里的start方法和stop方法分别调用了Server内部的start和stop方法,而这三个方法按照图中的层级结构,首先从Server的加载、启动、停止,然后是Server的启动Service的启动调用Service的启动,Service的启动调用Connector和Container的启动方法。这样从外到内启动就可以完全启动tomcat了。我们将继续从外到内分析波浪。3.1我们已经成功找到了Catalina启动过程上面的启动入口,下面我们继续。对象初始化完成后,init方法Bootstrapbootstrap=newBootstrap();try{bootstrap.init();}catch(Throwablevar3){就是上面那个。如果参数为空,则调用start,那么start方法是什么?publicvoidstart()throwsException{if(this.catalinaDaemon==null){this.init();}Methodmethod=this.catalinaDaemon.getClass().getMethod("开始",(Class[])null);方法。invoke(this.catalinaDaemon,(Object[])null);}上面的start方法直接映射到使用invokecatalinaDaemon的方法,即Catalina的start方法,这个Catalina的start无非就是调用同方法,setAwait方法,load方法,start方法,setAwait方法:用于设置服务器启动完成时是否进入等待,如果为true则等待,如果不为false则不进入等待。load方法:创建并初始化服务器,start方法:启动服务器,同样的setAwait方法比较少见,我就不给大家展示了,无非就是判断,load方法一定要看,if(!this.loaded){this.loaded=true;longt1=System.nanoTime();try{inputSource.setByteStream((InputStream)inputStream);digester.push(this);digester.parse(inputSource);breaklabel242;}catch(SAXParseExceptionvar28){log.warn("Catalina.startusing"+this.getConfigFile()+":"+var28.getMessage());return;}catch(Exceptionvar29){log.warn("Catalina.startusing"+this.getConfigFile()+":",var29);}}finally{if(inputStream!=null){try{((InputStream)inputStream).close();}catch(IOExceptionvar23){;}}}返回;}try{//thisServer的init方法,在this.getServer().init();}catch(LifecycleExceptionvar24){if(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")){thrownewError(var24);}log.error("Catalina.start",var24);}longt2=System.nanoTime();if(log.isInfoEnabled()){log.info("Initializationprocessedin"+(t2-t1)/1000000L+"ms");}}从这里开始,我们将进入下一步,服务器启动过程,因为getServer的初始化方法已经从Catalina中找到了,接下来就是初始化服务器,然后加载,然后startitProcess3.2Server启动流程Server在Tomcat中是一个接口,不是一个类,那么我们只能找实现它的子类,所以我们找到了StandardServerextendsLifecycleMBeanBaseimplementsServer。阿芬看到有继承也有实现,先看继承类LifecycleMBeanBase,于是又看了看,publicabstractclassLifecycleMBeanBaseextendsLifecycleBaseimplementsJmxEnabled{嗯?有传承吗?继续往下挖,publicabstractclassLifecycleBaseimplementsLifecycle{终于找到了,阿芬看到init方法和start方法又调用了initInternal()和startInternal(),来来回回,阿芬从这里也知道模板方法有自己的子类来具体实现。回到StandardServer自己的init和start方法,protectedvoidstartInternal()throwsLifecycleException{this.fireLifecycleEvent("configure_start",(Object)null);this.setState(LifecycleState.STARTING);this.globalNamingResources.start();Objectvar1=this。servicesLock;synchronized(this.servicesLock){for(inti=0;i

猜你喜欢