SpringBoot 嵌入式web容器的启动原理详解
springboot应用启动run方法
springapplication.java 中执行的代码
@springbootapplication @enableasync //使用异步注解@async 需要在这里加上@enableasync @mapperscan("springboot.dao") //不可或缺作用是扫描dao包下面的所有mapper装配 public class helloapplication { public static void main(string[] args) { springapplication.run(helloapplication.class,args); } }
public static configurableapplicationcontext run(class<?> primarysource, string... args) { return run(new class[]{primarysource}, args); }
public static configurableapplicationcontext run(class<?>[] primarysources, string[] args) { return (new springapplication(primarysources)).run(args); }
private void refreshcontext(configurableapplicationcontext context) { this.refresh(context); if (this.registershutdownhook) { try { context.registershutdownhook(); } catch (accesscontrolexception var3) { } }
protected void refresh(applicationcontext applicationcontext) { assert.isinstanceof(abstractapplicationcontext.class, applicationcontext); ((abstractapplicationcontext)applicationcontext).refresh(); }
servletwebserverapplicationcontext.java执行的方法
public final void refresh() throws beansexception, illegalstateexception { try { super.refresh(); } catch (runtimeexception var2) { this.stopandreleasewebserver(); throw var2; } }
protected void onrefresh() { super.onrefresh(); try { this.createwebserver(); } catch (throwable var2) { throw new applicationcontextexception("unable to start web server", var2); } }
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) { throw new applicationcontextexception("cannot initialize servlet context", var4); } } this.initpropertysources(); }
protected servletwebserverfactory getwebserverfactory() { string[] beannames = this.getbeanfactory().getbeannamesfortype(servletwebserverfactory.class); if (beannames.length == 0) { throw new applicationcontextexception("unable to start servletwebserverapplicationcontext due to missing servletwebserverfactory bean."); } else if (beannames.length > 1) { throw new applicationcontextexception("unable to start servletwebserverapplicationcontext due to multiple servletwebserverfactory beans : " + stringutils.arraytocommadelimitedstring(beannames)); } else { return (servletwebserverfactory)this.getbeanfactory().getbean(beannames[0], servletwebserverfactory.class); } }
//配置嵌入式的servlet容器 @bean public webserverfactorycustomizer<configurablewebserverfactory> mycustomizer(){ return new webserverfactorycustomizer<configurablewebserverfactory>() { @override public void customize(configurablewebserverfactory factory) { factory.setport(8081); } }; }
springboot 2.x 版本
嵌入式servlet容器自动配置原理以及启动原理
一、版本说明
spring boot 2.x 版本的嵌入式servlet容器自动配置是通过 webserverfactorycustomizer定制器 来定制的,而在spring boot 1.x 版本中我们是通过 embeddedservletcontainercustomizer 嵌入式的servlet容器定制器来定制的。由于之前看的资料都是1.x的版本,但是我使用的是2.x,所以在这里记录一下2.x版本的嵌入式servlet容器自动配置原理以及启动原理。
二、总结
嵌入式servlet容器自动配置原理以及启动原理有三大步:
步骤:
- springboot 根据导入的依赖信息,自动创建对应的 webserverfactorycustomizer(web服务工厂定制器);
- webserverfactorycustomizerbeanpostprocessor(web服务工厂定制器组件的后置处理器)获取所有类型为web服务工厂定制器的组件(包含实现webserverfactorycustomizer接口,自定义的定制器组件),依次调用customize()定制接口,定制servlet容器配置;
- 嵌入式的servlet容器工厂创建tomcat容器,初始化并启动容器。
三、嵌入式servlet容器自动配置原理(以tomcat为例)
1、首先找到 embeddedwebserverfactorycustomizerautoconfiguration ,在里面我们可以看到springboot支持的 servlet容器
//sprinboot支持的servlet容器有三个tomcat、jetty、undertow,但是默认配置的是tomcat //嵌入式的undertow @configuration( proxybeanmethods = false ) @conditionalonclass({undertow.class, sslclientauthmode.class}) public static class undertowwebserverfactorycustomizerconfiguration { public undertowwebserverfactorycustomizerconfiguration() { } @bean public undertowwebserverfactorycustomizer undertowwebserverfactorycustomizer(environment environment, serverproperties serverproperties) { return new undertowwebserverfactorycustomizer(environment, serverproperties); } } //嵌入式的jetty @configuration( proxybeanmethods = false ) @conditionalonclass({server.class, loader.class, webappcontext.class}) public static class jettywebserverfactorycustomizerconfiguration { public jettywebserverfactorycustomizerconfiguration() { } @bean public jettywebserverfactorycustomizer jettywebserverfactorycustomizer(environment environment, serverproperties serverproperties) { return new jettywebserverfactorycustomizer(environment, serverproperties); } } //嵌入式的tomcat @configuration( proxybeanmethods = false ) @conditionalonclass({tomcat.class, upgradeprotocol.class}) public static class tomcatwebserverfactorycustomizerconfiguration { public tomcatwebserverfactorycustomizerconfiguration() { } @bean public tomcatwebserverfactorycustomizer tomcatwebserverfactorycustomizer(environment environment, serverproperties serverproperties) { return new tomcatwebserverfactorycustomizer(environment, serverproperties); } }
2、准备环节
1)在以下位置打一个断点
2)、点进 tomcatwebserverfactorycustomizer 也就是上图 return 的,然后在里面的如下位置打一个断点
3)、然后在里面debug程序,我们在控制台就可以看到如下信息
3、按照上图从下往上分析。我们启动springboot应用时,都是直接运行主程序的main方法,然后调用里面的run方法,如下图
4、调用完run方法,回来到 refreshcontext 方法,这个方法是帮我们创建ioc容器对象,并且初始化容器,创建容器中的每一个组件
5、在调用了 reflesh 方法刷新刚才的ioc容器后,来到 onreflesh 方法,调用createwebserver()方法,创建webserver
6、来到createwebserver()方法,该方法最终能够获取到一个与当前应用(也就是总结里说的第一步,根据我们导入的依赖来获取)所导入的servlet类型相匹配的web服务工厂,通过工厂就可以获取到相应的 webserverfactorycustomizer (web服务工厂定制器)
注:createwebserver()执行后,我们其实来到了 embeddedwebserverfactorycustomizerautoconfiguration,然后根据条件(配置的依赖)配置哪一个web服务器
我们通过查看 servletwebserverfactory 的子类,可以看到其中三个就是tomcat、jetty和undertow,根据我们的配置,所以这里获取到的是 tomcatwebserverfactorycustomizer
至此,tomcatwebserverfactorycustomizer组件创建完成,对应的服务配置类也已添加到ioc容器。
7、因为容器中某个组件要创建对象就会惊动后置处理器 然后就到 webserverfactorycustomizerbeanpostprocessor(web服务工厂定制器组件的后置处理器),该类负责在bean组件初始化之前执行初始化工作。它先从ioc容器中获取所有类型为webserverfactorycustomizerbeans(web服务工厂定制器的组件)
通过后置处理器获取到的tomcatwebserverfactorycustomizer调用customize()定制方法,获取到servlet容器相关配置类serverproperties,进行自动配置
至此,嵌入式servlet容器的自动配置完成。
注:从源码分析可以得出配置嵌入式servlet容器的两种解决方案:
1、在全局配置文件中,通过server.xxx来修改和server有关的配置:
server.port=8081 server.tomcat.xxx...
2、实现webserverfactorycustomizer接口,重写它的customize()方法,对容器进行定制配置:
@functionalinterface public interface webserverfactorycustomizer<t extends webserverfactory> { void customize(t factory); }
四、嵌入式servlet容器启动原理(以tomcat为例)
1、应用启动后,根据导入的依赖信息,创建了相应的servlet容器工厂,创建了tomcatservletwebserverfactory,调用getwebserver()方法创建tomcat容器:(其实就是重写了servletwebserverfactory里面的getwebserver方法)
找到下面的gettomcatwebserver方法
2、然后点进去分析tomcatwebserver的有参构造器,执行 initialize() 方法
3、点进去就可以发现,里面通过调用start方法来启动tomcat
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。