SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
spring boot默认使用tomcat作为嵌入式的servlet容器,只要引入了spring-boot-start-web依赖,则默认是用tomcat作为servlet容器:
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency>
servlet容器的使用
默认servlet容器
我们看看spring-boot-starter-web这个starter中有什么
核心就是引入了tomcat和springmvc,我们先来看tomcat
spring boot默认支持tomcat,jetty,和undertow作为底层容器。如图:
而spring boot默认使用tomcat,一旦引入spring-boot-starter-web模块,就默认使用tomcat容器。
切换servlet容器
那如果我么想切换其他servlet容器呢,只需如下两步:
- 将tomcat依赖移除掉
- 引入其他servlet容器依赖
引入jetty:
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> <exclusions> <exclusion> <!--移除spring-boot-starter-web中的tomcat--> <artifactid>spring-boot-starter-tomcat</artifactid> <groupid>org.springframework.boot</groupid> </exclusion> </exclusions> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <!--引入jetty--> <artifactid>spring-boot-starter-jetty</artifactid> </dependency>
servlet容器自动配置原理
embeddedservletcontainerautoconfiguration
我们可以看到embeddedservletcontainerautoconfiguration被配置在spring.factories中,看过我前面文章的朋友应该知道springboot自动配置的原理,这里将embeddedservletcontainerautoconfiguration配置类加入到ioc容器中,接着我们来具体看看这个配置类:
@autoconfigureorder(ordered.highest_precedence) @configuration @conditionalonwebapplication// 在web环境下才会起作用 @import(beanpostprocessorsregistrar.class)// 会import一个内部类beanpostprocessorsregistrar public class embeddedservletcontainerautoconfiguration { @configuration // tomcat类和servlet类必须在classloader中存在 // 文章开头我们已经导入了web的starter,其中包含tomcat和springmvc // 那么classpath下会存在tomcat.class和servlet.class @conditionalonclass({ servlet.class, tomcat.class }) // 当前spring容器中不存在embeddedservletcontainerfactory类型的实例 @conditionalonmissingbean(value = embeddedservletcontainerfactory.class, search = searchstrategy.current) public static class embeddedtomcat { @bean public tomcatembeddedservletcontainerfactory tomcatembeddedservletcontainerfactory() { // 上述条件注解成立的话就会构造tomcatembeddedservletcontainerfactory这个embeddedservletcontainerfactory return new tomcatembeddedservletcontainerfactory(); } } @configuration @conditionalonclass({ servlet.class, server.class, loader.class, webappcontext.class }) @conditionalonmissingbean(value = embeddedservletcontainerfactory.class, search = searchstrategy.current) public static class embeddedjetty { @bean public jettyembeddedservletcontainerfactory jettyembeddedservletcontainerfactory() { return new jettyembeddedservletcontainerfactory(); } } @configuration @conditionalonclass({ servlet.class, undertow.class, sslclientauthmode.class }) @conditionalonmissingbean(value = embeddedservletcontainerfactory.class, search = searchstrategy.current) public static class embeddedundertow { @bean public undertowembeddedservletcontainerfactory undertowembeddedservletcontainerfactory() { return new undertowembeddedservletcontainerfactory(); } } //other code... }
在这个自动配置类中配置了三个容器工厂的bean,分别是:
-
tomcatembeddedservletcontainerfactory
-
jettyembeddedservletcontainerfactory
-
undertowembeddedservletcontainerfactory
embeddedservletcontainerfactory
- 嵌入式servlet容器工厂
public interface embeddedservletcontainerfactory { embeddedservletcontainer getembeddedservletcontainer( servletcontextinitializer... initializers); }
内部只有一个方法,用于获取嵌入式的servlet容器。
该工厂接口主要有三个实现类,分别对应三种嵌入式servlet容器的工厂类,如图所示:
tomcatembeddedservletcontainerfactory
以tomcat容器工厂tomcatembeddedservletcontainerfactory类为例:
public class tomcatembeddedservletcontainerfactory extends abstractembeddedservletcontainerfactory implements resourceloaderaware { //other code... @override public embeddedservletcontainer getembeddedservletcontainer( servletcontextinitializer... initializers) { //创建一个tomcat tomcat tomcat = new tomcat(); //配置tomcat的基本环节 file basedir = (this.basedirectory != null ? this.basedirectory: createtempdir("tomcat")); tomcat.setbasedir(basedir.getabsolutepath()); 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); //包装tomcat对象,返回一个嵌入式tomcat容器,内部会启动该tomcat容器 return gettomcatembeddedservletcontainer(tomcat); } }
首先会创建一个tomcat的对象,并设置一些属性配置,最后调用gettomcatembeddedservletcontainer(tomcat)方法,内部会启动tomcat,我们来看看:
protected tomcatembeddedservletcontainer gettomcatembeddedservletcontainer( tomcat tomcat) { return new tomcatembeddedservletcontainer(tomcat, getport() >= 0); }
该函数很简单,就是来创建tomcat容器并返回。看看tomcatembeddedservletcontainer类:
public class tomcatembeddedservletcontainer implements embeddedservletcontainer { public tomcatembeddedservletcontainer(tomcat tomcat, boolean autostart) { assert.notnull(tomcat, "tomcat server must not be null"); this.tomcat = tomcat; this.autostart = autostart; //初始化嵌入式tomcat容器,并启动tomcat initialize(); } private void initialize() throws embeddedservletcontainerexception { tomcatembeddedservletcontainer.logger .info("tomcat initialized with port(s): " + getportsdescription(false)); synchronized (this.monitor) { try { addinstanceidtoenginename(); try { final context context = findcontext(); context.addlifecyclelistener(new lifecyclelistener() { @override public void lifecycleevent(lifecycleevent event) { if (context.equals(event.getsource()) && lifecycle.start_event.equals(event.gettype())) { // remove service connectors so that protocol // binding doesn't happen when the service is // started. removeserviceconnectors(); } } }); // start the server to trigger initialization listeners //启动tomcat this.tomcat.start(); // we can re-throw failure exception directly in the main thread rethrowdeferredstartupexceptions(); try { contextbindings.bindclassloader(context, getnamingtoken(context), getclass().getclassloader()); } catch (namingexception ex) { // naming is not enabled. continue } // unlike jetty, all tomcat threads are daemon threads. we create a // blocking non-daemon to stop immediate shutdown startdaemonawaitthread(); } catch (exception ex) { containercounter.decrementandget(); throw ex; } } catch (exception ex) { stopsilently(); throw new embeddedservletcontainerexception( "unable to start embedded tomcat", ex); } } } }
到这里就启动了嵌入式的servlet容器,其他容器类似。
servlet容器启动原理
springboot启动过程
我们回顾一下前面讲解的springboot启动过程,也就是run方法:
public configurableapplicationcontext run(string... args) { // 计时工具 stopwatch stopwatch = new stopwatch(); stopwatch.start(); configurableapplicationcontext context = null; collection<springbootexceptionreporter> exceptionreporters = new arraylist<>(); configureheadlessproperty(); // 第一步:获取并启动监听器 springapplicationrunlisteners listeners = getrunlisteners(args); listeners.starting(); try { applicationarguments applicationarguments = new defaultapplicationarguments(args); // 第二步:根据springapplicationrunlisteners以及参数来准备环境 configurableenvironment environment = prepareenvironment(listeners,applicationarguments); configureignorebeaninfo(environment); // 准备banner打印器 - 就是启动spring boot的时候打印在console上的ascii艺术字体 banner printedbanner = printbanner(environment); // 第三步:创建spring容器 context = createapplicationcontext(); exceptionreporters = getspringfactoriesinstances( springbootexceptionreporter.class, new class[] { configurableapplicationcontext.class }, context); // 第四步:spring容器前置处理 preparecontext(context, environment, listeners, applicationarguments,printedbanner); // 第五步:刷新容器 refreshcontext(context); // 第六步:spring容器后置处理 afterrefresh(context, applicationarguments); // 第七步:发出结束执行的事件 listeners.started(context); // 第八步:执行runners this.callrunners(context, applicationarguments); stopwatch.stop(); // 返回容器 return context; } catch (throwable ex) { handlerunfailure(context, listeners, exceptionreporters, ex); throw new illegalstateexception(ex); } }
我们回顾一下第三步:创建spring容器
public static final string default_context_class = "org.springframework.context." + "annotation.annotationconfigapplicationcontext"; public static final string default_web_context_class = "org.springframework." + "boot.context.embedded.annotationconfigembeddedwebapplicationcontext"; protected configurableapplicationcontext createapplicationcontext() { class<?> contextclass = this.applicationcontextclass; if (contextclass == null) { try { //根据应用环境,创建不同的ioc容器 contextclass = class.forname(this.webenvironment ? default_web_context_class : default_context_class); } } return (configurableapplicationcontext) beanutils.instantiate(contextclass); }
private void refreshcontext(configurableapplicationcontext context) { refresh(context); } protected void refresh(applicationcontext applicationcontext) { assert.isinstanceof(abstractapplicationcontext.class, applicationcontext); //调用容器的refresh()方法刷新容器 ((abstractapplicationcontext) applicationcontext).refresh(); }
容器刷新过程
调用抽象父类abstractapplicationcontext的refresh()方法;
abstractapplicationcontext
1 public void refresh() throws beansexception, illegalstateexception { 2 synchronized (this.startupshutdownmonitor) { 3 /** 4 * 刷新上下文环境 5 */ 6 preparerefresh(); 7 8 /** 9 * 初始化beanfactory,解析xml,相当于之前的xmlbeanfactory的操作, 10 */ 11 configurablelistablebeanfactory beanfactory = obtainfreshbeanfactory(); 12 13 /** 14 * 为上下文准备beanfactory,即对beanfactory的各种功能进行填充,如常用的注解@autowired @qualifier等 15 * 添加applicationcontextawareprocessor处理器 16 * 在依赖注入忽略实现*aware的接口,如environmentaware、applicationeventpublisheraware等 17 * 注册依赖,如一个bean的属性中含有applicationeventpublisher(beanfactory),则会将beanfactory的实例注入进去 18 */ 19 preparebeanfactory(beanfactory); 20 21 try { 22 /** 23 * 提供子类覆盖的额外处理,即子类处理自定义的beanfactorypostprocess 24 */ 25 postprocessbeanfactory(beanfactory); 26 27 /** 28 * 激活各种beanfactory处理器,包括beandefinitionregistrybeanfactorypostprocessor和普通的beanfactorypostprocessor 29 * 执行对应的postprocessbeandefinitionregistry方法 和 postprocessbeanfactory方法 30 */ 31 invokebeanfactorypostprocessors(beanfactory); 32 33 /** 34 * 注册拦截bean创建的bean处理器,即注册beanpostprocessor,不是beanfactorypostprocessor,注意两者的区别 35 * 注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法 36 */ 37 registerbeanpostprocessors(beanfactory); 38 39 /** 40 * 初始化上下文中的资源文件,如国际化文件的处理等 41 */ 42 initmessagesource(); 43 44 /** 45 * 初始化上下文事件广播器,并放入applicatioeventmulticaster,如applicationeventpublisher 46 */ 47 initapplicationeventmulticaster(); 48 49 /** 50 * 给子类扩展初始化其他bean 51 */ 52 onrefresh(); 53 54 /** 55 * 在所有bean中查找listener bean,然后注册到广播器中 56 */ 57 registerlisteners(); 58 59 /** 60 * 设置转换器 61 * 注册一个默认的属性值解析器 62 * 冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理 63 * 初始化剩余的非惰性的bean,即初始化非延迟加载的bean 64 */ 65 finishbeanfactoryinitialization(beanfactory); 66 67 /** 68 * 通过spring的事件发布机制发布contextrefreshedevent事件,以保证对应的监听器做进一步的处理 69 * 即对那种在spring启动后需要处理的一些类,这些类实现了applicationlistener<contextrefreshedevent>, 70 * 这里就是要触发这些类的执行(执行onapplicationevent方法) 71 * spring的内置event有contextclosedevent、contextrefreshedevent、contextstartedevent、contextstoppedevent、requesthandleevent 72 * 完成初始化,通知生命周期处理器lifecycleprocessor刷新过程,同时发出contextrefreshevent通知其他人 73 */ 74 finishrefresh(); 75 } 76 77 finally { 78 79 resetcommoncaches(); 80 } 81 } 82 }
我们看第52行的方法:
protected void onrefresh() throws beansexception { }
很明显抽象父类abstractapplicationcontext中的onrefresh是一个空方法,并且使用protected修饰,也就是其子类可以重写onrefresh方法,那我们看看其子类annotationconfigembeddedwebapplicationcontext中的onrefresh方法是如何重写的,annotationconfigembeddedwebapplicationcontext又继承embeddedwebapplicationcontext,如下:
public class annotationconfigembeddedwebapplicationcontext extends embeddedwebapplicationcontext {
那我们看看其父类embeddedwebapplicationcontext 是如何重写onrefresh方法的:
embeddedwebapplicationcontext
@override protected void onrefresh() { super.onrefresh(); try { //核心方法:会获取嵌入式的servlet容器工厂,并通过工厂来获取servlet容器 createembeddedservletcontainer(); } catch (throwable ex) { throw new applicationcontextexception("unable to start embedded container", ex); } }
在createembeddedservletcontainer方法中会获取嵌入式的servlet容器工厂,并通过工厂来获取servlet容器:
1 private void createembeddedservletcontainer() { 2 embeddedservletcontainer localcontainer = this.embeddedservletcontainer; 3 servletcontext localservletcontext = getservletcontext(); 4 if (localcontainer == null && localservletcontext == null) { 5 //先获取嵌入式servlet容器工厂 6 embeddedservletcontainerfactory containerfactory = getembeddedservletcontainerfactory(); 7 //根据容器工厂来获取对应的嵌入式servlet容器 8 this.embeddedservletcontainer = containerfactory.getembeddedservletcontainer(getselfinitializer()); 9 } 10 else if (localservletcontext != null) { 11 try { 12 getselfinitializer().onstartup(localservletcontext); 13 } 14 catch (servletexception ex) { 15 throw new applicationcontextexception("cannot initialize servlet context",ex); 16 } 17 } 18 initpropertysources(); 19 }
关键代码在第6和第8行,先获取servlet容器工厂,然后根据容器工厂来获取对应的嵌入式servlet容器
获取servlet容器工厂
protected embeddedservletcontainerfactory getembeddedservletcontainerfactory() { //从spring的ioc容器中获取embeddedservletcontainerfactory.class类型的bean string[] beannames = getbeanfactory().getbeannamesfortype(embeddedservletcontainerfactory.class); //调用getbean实例化embeddedservletcontainerfactory.class return getbeanfactory().getbean(beannames[0], embeddedservletcontainerfactory.class); }
我们看到先从spring的ioc容器中获取embeddedservletcontainerfactory.class类型的bean,然后调用getbean实例化embeddedservletcontainerfactory.class,大家还记得我们第一节servlet容器自动配置类embeddedservletcontainerautoconfiguration中注入spring容器的对象是什么吗?当我们引入spring-boot-starter-web这个启动器后,会注入tomcatembeddedservletcontainerfactory这个对象到spring容器中,所以这里获取到的servlet容器工厂是tomcatembeddedservletcontainerfactory,然后调用
tomcatembeddedservletcontainerfactory的getembeddedservletcontainer方法获取servlet容器,并且启动tomcat,大家可以看看文章开头的getembeddedservletcontainer方法。
大家看一下第8行代码获取servlet容器方法的参数getselfinitializer(),这是个啥?我们点进去看看
private servletcontextinitializer getselfinitializer() { //创建一个servletcontextinitializer对象,并重写onstartup方法,很明显是一个回调方法 return new servletcontextinitializer() { public void onstartup(servletcontext servletcontext) throws servletexception { embeddedwebapplicationcontext.this.selfinitialize(servletcontext); } }; }
创建一个servletcontextinitializer对象,并重写onstartup方法,很明显是一个回调方法,这里给大家留一点疑问:
- servletcontextinitializer对象创建过程是怎样的?
- onstartup是何时调用的?
- onstartup方法的作用是什么?
servletcontextinitializer
是 servlet 容器初始化的时候,提供的初始化接口。这里涉及到servlet、filter实例的注册,我们留在下一篇具体讲
推荐阅读
-
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
SpringBoot 源码解析 (九)----- Spring Boot的核心能力 - 整合Mybatis
-
SpringBoot 源码解析 (五)----- Spring Boot的核心能力 - 自动配置源码解析
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
-
SpringBoot 源码解析 (五)----- Spring Boot的核心能力 - 自动配置源码解析
-
SpringBoot 源码解析 (九)----- Spring Boot的核心能力 - 整合Mybatis