SpringBoot内置tomcat启动原理
前言
不得不说springboot的开发者是在为大众程序猿谋福利,把大家都惯成了懒汉,xml不配置了,连tomcat也懒的配置了,典型的一键启动系统,那么tomcat在springboot是怎么启动的呢?
内置tomcat
开发阶段对我们来说使用内置的tomcat是非常够用了,当然也可以使用jetty。
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> <version>2.1.6.release</version> </dependency>
@springbootapplication public class myspringboottomcatstarter{ public static void main(string[] args) { long time=system.currenttimemillis(); springapplication.run(myspringboottomcatstarter.class); system.out.println("===应用启动耗时:"+(system.currenttimemillis()-time)+"==="); } }
这里是main函数入口,两句代码最耀眼,分别是springbootapplication注解和springapplication.run()方法。
发布生产
发布的时候,目前大多数的做法还是排除内置的tomcat,打瓦包(war)然后部署在生产的tomcat中,好吧,那打包的时候应该怎么处理?
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> <!-- 移除嵌入式tomcat插件 --> <exclusions> <exclusion> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-tomcat</artifactid> </exclusion> </exclusions> </dependency> <!--添加servlet-api依赖---> <dependency> <groupid>javax.servlet</groupid> <artifactid>javax.servlet-api</artifactid> <version>3.1.0</version> <scope>provided</scope> </dependency>
更新main函数,主要是继承springbootservletinitializer,并重写configure()方法。
@springbootapplication public class myspringboottomcatstarter extends springbootservletinitializer { public static void main(string[] args) { long time=system.currenttimemillis(); springapplication.run(myspringboottomcatstarter.class); system.out.println("===应用启动耗时:"+(system.currenttimemillis()-time)+"==="); } @override protected springapplicationbuilder configure(springapplicationbuilder builder) { return builder.sources(this.getclass()); } }
从main函数说起
public static configurableapplicationcontext run(class<?> primarysource, string... args) { return run(new class[]{primarysource}, args); } --这里run方法返回的是configurableapplicationcontext public static configurableapplicationcontext run(class<?>[] primarysources, string[] args) { return (new springapplication(primarysources)).run(args); }
public configurableapplicationcontext run(string... args) { configurableapplicationcontext context = null; collection<springbootexceptionreporter> exceptionreporters = new arraylist(); this.configureheadlessproperty(); springapplicationrunlisteners listeners = this.getrunlisteners(args); listeners.starting(); collection exceptionreporters; try { applicationarguments applicationarguments = new defaultapplicationarguments(args); configurableenvironment environment = this.prepareenvironment(listeners, applicationarguments); this.configureignorebeaninfo(environment); //打印banner,这里你可以自己涂鸦一下,换成自己项目的logo banner printedbanner = this.printbanner(environment); //创建应用上下文 context = this.createapplicationcontext(); exceptionreporters = this.getspringfactoriesinstances(springbootexceptionreporter.class, new class[]{configurableapplicationcontext.class}, context); //预处理上下文 this.preparecontext(context, environment, listeners, applicationarguments, printedbanner); //刷新上下文 this.refreshcontext(context); //再刷新上下文 this.afterrefresh(context, applicationarguments); listeners.started(context); this.callrunners(context, applicationarguments); } catch (throwable var10) { } try { listeners.running(context); return context; } catch (throwable var9) { } }
既然我们想知道tomcat在springboot中是怎么启动的,那么run方法中,重点关注创建应用上下文(createapplicationcontext)和刷新上下文(refreshcontext)。
创建上下文
//创建上下文 protected configurableapplicationcontext createapplicationcontext() { class<?> contextclass = this.applicationcontextclass; if (contextclass == null) { try { switch(this.webapplicationtype) { case servlet: //创建annotationconfigservletwebserverapplicationcontext contextclass = class.forname("org.springframework.boot.web.servlet.context.annotationconfigservletwebserverapplicationcontext"); break; case reactive: contextclass = class.forname("org.springframework.boot.web.reactive.context.annotationconfigreactivewebserverapplicationcontext"); break; default: contextclass = class.forname("org.springframework.context.annotation.annotationconfigapplicationcontext"); } } catch (classnotfoundexception var3) { throw new illegalstateexception("unable create a default applicationcontext, please specify an applicationcontextclass", var3); } } return (configurableapplicationcontext)beanutils.instantiateclass(contextclass); }
这里会创建annotationconfigservletwebserverapplicationcontext类。
而annotationconfigservletwebserverapplicationcontext类继承了servletwebserverapplicationcontext,而这个类是最终集成了abstractapplicationcontext。
刷新上下文
//springapplication.java //刷新上下文 private void refreshcontext(configurableapplicationcontext context) { this.refresh(context); if (this.registershutdownhook) { try { context.registershutdownhook(); } catch (accesscontrolexception var3) { } } } //这里直接调用最终父类abstractapplicationcontext.refresh()方法 protected void refresh(applicationcontext applicationcontext) { ((abstractapplicationcontext)applicationcontext).refresh(); }
//abstractapplicationcontext.java public void refresh() throws beansexception, illegalstateexception { synchronized(this.startupshutdownmonitor) { this.preparerefresh(); configurablelistablebeanfactory beanfactory = this.obtainfreshbeanfactory(); this.preparebeanfactory(beanfactory); try { this.postprocessbeanfactory(beanfactory); this.invokebeanfactorypostprocessors(beanfactory); this.registerbeanpostprocessors(beanfactory); this.initmessagesource(); this.initapplicationeventmulticaster(); //调用各个子类的onrefresh()方法,也就说这里要回到子类:servletwebserverapplicationcontext,调用该类的onrefresh()方法 this.onrefresh(); this.registerlisteners(); this.finishbeanfactoryinitialization(beanfactory); this.finishrefresh(); } catch (beansexception var9) { this.destroybeans(); this.cancelrefresh(var9); throw var9; } finally { this.resetcommoncaches(); } } }
//servletwebserverapplicationcontext.java //在这个方法里看到了熟悉的面孔,this.createwebserver,神秘的面纱就要揭开了。 protected void onrefresh() { super.onrefresh(); try { this.createwebserver(); } catch (throwable var2) { } } //servletwebserverapplicationcontext.java //这里是创建webserver,但是还没有启动tomcat,这里是通过servletwebserverfactory创建,那么接着看下servletwebserverfactory private void createwebserver() { webserver webserver = this.webserver; servletcontext servletcontext = this.getservletcontext(); if (webserver == null && servletcontext == null) { servletwebserverfactory factory = this.getwebserverfactory(); this.webserver = factory.getwebserver(new servletcontextinitializer[]{this.getselfinitializer()}); } else if (servletcontext != null) { try { this.getselfinitializer().onstartup(servletcontext); } catch (servletexception var4) { } } this.initpropertysources(); } //接口 public interface servletwebserverfactory { webserver getwebserver(servletcontextinitializer... initializers); } //实现 abstractservletwebserverfactory jettyservletwebserverfactory tomcatservletwebserverfactory undertowservletwebserverfactory
这里servletwebserverfactory接口有4个实现类
而其中我们常用的有两个:tomcatservletwebserverfactory和jettyservletwebserverfactory。
//tomcatservletwebserverfactory.java //这里我们使用的tomcat,所以我们查看tomcatservletwebserverfactory。到这里总算是看到了tomcat的踪迹。 @override public webserver getwebserver(servletcontextinitializer... initializers) { tomcat tomcat = new tomcat(); file basedir = (this.basedirectory != null) ? this.basedirectory : createtempdir("tomcat"); tomcat.setbasedir(basedir.getabsolutepath()); //创建connector对象 connector connector = new connector(this.protocol); tomcat.getservice().addconnector(connector); customizeconnector(connector); tomcat.setconnector(connector); tomcat.gethost().setautodeploy(false); configureengine(tomcat.getengine()); for (connector additionalconnector : this.additionaltomcatconnectors) { tomcat.getservice().addconnector(additionalconnector); } preparecontext(tomcat.gethost(), initializers); return gettomcatwebserver(tomcat); } protected tomcatwebserver gettomcatwebserver(tomcat tomcat) { return new tomcatwebserver(tomcat, getport() >= 0); } //tomcat.java //返回engine容器,看到这里,如果熟悉tomcat源码的话,对engine不会感到陌生。 public engine getengine() { service service = getserver().findservices()[0]; if (service.getcontainer() != null) { return service.getcontainer(); } engine engine = new standardengine(); engine.setname( "tomcat" ); engine.setdefaulthost(hostname); engine.setrealm(createdefaultrealm()); service.setcontainer(engine); return engine; } //engine是*别容器,host是engine的子容器,context是host的子容器,wrapper是context的子容器
getwebserver这个方法创建了tomcat对象,并且做了两件重要的事情:把connector对象添加到tomcat中,configureengine(tomcat.getengine());
getwebserver方法返回的是tomcatwebserver。
//tomcatwebserver.java //这里调用构造函数实例化tomcatwebserver public tomcatwebserver(tomcat tomcat, boolean autostart) { assert.notnull(tomcat, "tomcat server must not be null"); this.tomcat = tomcat; this.autostart = autostart; initialize(); } private void initialize() throws webserverexception { //在控制台会看到这句日志 logger.info("tomcat initialized with port(s): " + getportsdescription(false)); synchronized (this.monitor) { try { addinstanceidtoenginename(); context context = findcontext(); context.addlifecyclelistener((event) -> { if (context.equals(event.getsource()) && lifecycle.start_event.equals(event.gettype())) { removeserviceconnectors(); } }); //===启动tomcat服务=== this.tomcat.start(); rethrowdeferredstartupexceptions(); try { contextbindings.bindclassloader(context, context.getnamingtoken(), getclass().getclassloader()); } catch (namingexception ex) { } //开启阻塞非守护进程 startdaemonawaitthread(); } catch (exception ex) { stopsilently(); destroysilently(); throw new webserverexception("unable to start embedded tomcat", ex); } } }
//tomcat.java public void start() throws lifecycleexception { getserver(); server.start(); } //这里server.start又会回到tomcatwebserver的 public void stop() throws lifecycleexception { getserver(); server.stop(); }
//tomcatwebserver.java //启动tomcat服务 @override public void start() throws webserverexception { synchronized (this.monitor) { if (this.started) { return; } try { addpreviouslyremovedconnectors(); connector connector = this.tomcat.getconnector(); if (connector != null && this.autostart) { performdeferredloadonstartup(); } checkthatconnectorshavestarted(); this.started = true; //在控制台打印这句日志,如果在yml设置了上下文,这里会打印 logger.info("tomcat started on port(s): " + getportsdescription(true) + " with context path '" + getcontextpath() + "'"); } catch (connectorstartfailedexception ex) { stopsilently(); throw ex; } catch (exception ex) { throw new webserverexception("unable to start embedded tomcat server", ex); } finally { context context = findcontext(); contextbindings.unbindclassloader(context, context.getnamingtoken(), getclass().getclassloader()); } } } //关闭tomcat服务 @override public void stop() throws webserverexception { synchronized (this.monitor) { boolean wasstarted = this.started; try { this.started = false; try { stoptomcat(); this.tomcat.destroy(); } catch (lifecycleexception ex) { } } catch (exception ex) { throw new webserverexception("unable to stop embedded tomcat", ex); } finally { if (wasstarted) { containercounter.decrementandget(); } } } }
附:tomcat顶层结构图
tomcat最顶层容器是server,代表着整个服务器,一个server包含多个service。从上图可以看除service主要包括多个connector和一个container。connector用来处理连接相关的事情,并提供socket到request和response相关转化。container用于封装和管理servlet,以及处理具体的request请求。那么上文提到的engine>host>context>wrapper容器又是怎么回事呢? 我们来看下图:
综上所述,一个tomcat只包含一个server,一个server可以包含多个service,一个service只有一个container,但有多个connector,这样一个服务可以处理多个连接。
多个connector和一个container就形成了一个service,有了service就可以对外提供服务了,但是service要提供服务又必须提供一个宿主环境,那就非server莫属了,所以整个tomcat的声明周期都由server控制。
总结
springboot的启动主要是通过实例化springapplication来启动的,启动过程主要做了以下几件事情:配置属性、获取监听器,发布应用开始启动事件初、始化输入参数、配置环境,输出banner、创建上下文、预处理上下文、刷新上下文、再刷新上下文、发布应用已经启动事件、发布应用启动完成事件。在springboot中启动tomcat的工作在刷新上下这一步。而tomcat的启动主要是实例化两个组件:connector、container,一个tomcat实例就是一个server,一个server包含多个service,也就是多个应用程序,每个service包含多个connector和一个container,而一个container下又包含多个子容器。
上一篇: 微信小程序开发过程中遇到的坑
推荐阅读
-
SpringBoot之旅第六篇-启动原理及自定义starter
-
使用tomcat启动SpringBoot项目
-
详解springboot-修改内置tomcat版本
-
深入springboot原理——一步步分析springboot启动机制(starter机制)
-
【SpringBoot】启动原理(三):run 方法解析
-
如何优雅的使用springboot项目内置tomcat
-
SpringBoot内置tomcat启动原理
-
SpringBoot内置tomcat启动原理、以及SpringBoot初始化Servlet的源码分析
-
SpringBoot(1)—启动原理之SpringApplication对象的创建
-
SpringBoot war包tomcat运行启动报错(Cannot determine embedded database driver class for