欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)

程序员文章站 2023-11-14 09:16:16
Spring Boot默认使用Tomcat作为嵌入式的Servlet容器,只要引入了spring-boot-start-web依赖,则默认是用Tomcat作为Servlet容器: org.springframework.boot

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中有什么

SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)

核心就是引入了tomcat和springmvc,我们先来看tomcat

spring boot默认支持tomcat,jetty,和undertow作为底层容器。如图:

SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)

而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是嵌入式servlet容器的自动配置类,该类在spring-boot-autoconfigure.jar中的web模块可以找到。

SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)

我们可以看到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

这里以大家熟悉的tomcat为例,首先spring boot会判断当前环境中是否引入了servlet和tomcat依赖,并且当前容器中没有自定义的embeddedservletcontainerfactory的情况下,则创建tomcat容器工厂。其他servlet容器工厂也是同样的道理。

embeddedservletcontainerfactory

  • 嵌入式servlet容器工厂
public interface embeddedservletcontainerfactory {

    embeddedservletcontainer getembeddedservletcontainer( servletcontextinitializer... initializers);
}

内部只有一个方法,用于获取嵌入式的servlet容器。

该工厂接口主要有三个实现类,分别对应三种嵌入式servlet容器的工厂类,如图所示:

SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)

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);
}
创建ioc容器,如果是web应用,则创建annotationconfigembeddedwebapplicationcontext的ioc容器;如果不是,则创建annotationconfigapplicationcontext的ioc容器;很明显我们创建的容器是annotationconfigembeddedwebapplicationcontext接着我们来看看第五步,刷新容器refreshcontext(context);
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实例的注册,我们留在下一篇具体讲