天天看点

SpringBoot——嵌入式Servlet容器自动配置原理以及启动原理0 嵌入式Servlet容器自动配置原理以及启动原理的步骤1 嵌入式Servlet容器自动配置原理(以Tomcat为例)2 嵌入式Servlet容器启动原理(以Tomcat为例)

  • 0 嵌入式Servlet容器自动配置原理以及启动原理的步骤
  • 1 嵌入式Servlet容器自动配置原理(以Tomcat为例)
    • 1.1 简单介绍
  • 2 嵌入式Servlet容器启动原理(以Tomcat为例)
    • 2.1 SpringBoot启动时,启动Tomcat的原理
    • 2.2 嵌入式的Servlet容器工厂创建tomcat容器,初始化并启动容器
      • 2.2.1 嵌入式Servlet容器工厂的自动配置类
        • 1> 注册后置处理器(WebServerFactoryCustomizerBeanPostProcessor)
        • 2> 后置处理器(WebServerFactoryCustomizerBeanPostProcessor)
        • 3> web服务工厂定制器(WebServerFactoryCustomizer)
      • 2.2.2 嵌入式的Servlet容器工厂的配置类

版本说明:

  • Spring Boot 2.x 版本:嵌入式Servlet容器自动配置是通过

    WebServerFactoryCustomizer

    定制器 来定制的;
  • Spring Boot 1.x 版本:是通过

    EmbeddedServletContainerCustomizer

    嵌入式的Servlet容器定制器来定制的。

SpringBoot官方文档:

Embedded Web Servers、

Embedded Servlet Container Support

参考博客:https://cloud.tencent.com/developer/article/1551867

0 嵌入式Servlet容器自动配置原理以及启动原理的步骤

步骤:

  • SpringBoot 启动时,根据导入的依赖信息,自动创建对应的

    WebServerFactoryCustomizer

    (web服务工厂定制器);
  • WebServerFactoryCustomizerBeanPostProcessor

    (web服务工厂定制器组件的后置处理器)获取所有类型为web服务工厂定制器的组件(包含实现WebServerFactoryCustomizer接口,自定义的定制器组件),依次调用

    customize

    方法,定制Servlet容器配置;
  • 嵌入式的Servlet容器工厂创建Tomcat容器,初始化并启动容器。

1 嵌入式Servlet容器自动配置原理(以Tomcat为例)

1.1 简单介绍

SpringBoot默认使用

Tomcat

作为嵌入式的Servlet容器,

这里所谓的嵌入式服务器就是,SpringBoot使用 apache组织 提供的Tomcat的API来配置和定制Tomcat服务。

SpringBoot在启应用动时都会加载各个jar包下的

/META-INF/spring.factories

文件,读取其中的

org.springframework.boot.autoconfigure.EnableAutoConfiguration

类型的所有自动配置类,从而达到自动配置的效果。

而我们的servlet容器的自动配置也是从这个配置文件着手,在

spring-boot-autoconfigure-2.3.1.RELEASE.jar

包下的

/META-INF/spring.factories

配置文件中,有一个自动配置类,是用于Servlet容器的自动配置的。

// ServletWebServerFactoryAutoConfiguration 就是嵌入式servlet容器的自动配置类
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
           

ServletWebServerFactoryAutoConfiguration

是嵌入式Servlet容器的自动配置类,这个类的主要作用是

创建

TomcatServletWebServerFactory

工厂类,

创建

TomcatServletWebServerFactoryCustomizer

定制器类,

创建

FilterRegistrationBean

注册

ForwardedHeaderFilter

(ForwardedHeaderFilter会从Forwarded,X-Forwarded-Host,X-Forwarded-Port或者X-Forwarded-Proto中获取跳转信息),

同时很关键的一步是注册后置处理器

webServerFactoryCustomizerBeanPostProcessor

只要

Application

类一启动,就会执行

run

方法,

run经过一系列调用会通过

ServletWebServerApplicationContext

onRefresh

方法创建ioc容器,

然后通过

createWebServer

方法,createWebServer方法会去ioc容器里扫描是否有对应的

ServletWebServerFactory

工厂类(

TomcatServletWebServerFactory

是其中一种),

扫描得到,就会触发

webServerFactoryCustomizerBeanPostProcessor

后置处理器类,

这个处理器类会获取

TomcatServletWebServerFactoryCustomizer

定制器,并调用

customize

方法进行定制,

这时候工厂类起作用,调用

getWebServer

方法进行Tomcat属性配置和引擎设置等等,

再创建TomcatWebServer启动Tomcat容器。

2 嵌入式Servlet容器启动原理(以Tomcat为例)

2.1 SpringBoot启动时,启动Tomcat的原理

主要 是 围绕

ServletWebServerApplicationContext

类进行

(1)我们启动springboot应用时,都是直接运行主程序的

main

方法,然后调用里面的

SpringApplication

类的

run

方法。

(2)调用run方法时,会调用该类自己的

refreshContext

方法,这个方法的作用是创建IOC容器对象,并且初始化IOC容器,创建容器中的每一个组件。

(3)在该类的

reflesh

方法中,再调用了

ServletWebServerApplicationContext

类的

reflesh

方法,刷新刚才的IOC容器后(这个方法其实是继承

AbstractApplicationContext

类的),在该方法中又调用

onreflesh

方法(这个方法则是

ServletWebServerApplicationContext

类重写

AbstractApplicationContext

类的),最后在

onreflesh

中调用

createWebServer

方法,创建 WebServer对象。

(4)

createWebServer

方法,该方法最终能够获取到一个与当前应用(根据我们导入的依赖来获取)所导入的Servlet类型相匹配的web服务工厂

TomcatServletWebServerFactory

,通过该工厂就可以获取到相应的

WebServerFactoryCustomizer

(Web服务工厂定制器),在调用该工厂的

getWebServer

方法,创建->初始化->启动Tomcat。

注:

createWebServer

方法执行期间,我们其实会来到

EmbeddedWebServerFactoryCustomizerAutoConfiguration

,然后根据条件(配置的依赖)配置哪一个Web服务器。

* 在SpringBoot的run启动时,会判断当前所处环境。

* 如果是Web环境则通过创建一个`ServletWebServerApplicationContext`,执行构造函数的refresh方法,

* 在refresh方法内执行onRefresh方法,执行createWebServer方法,这个方法会根据当前应用是内置还是外置发布方式来决定以何种方式获取web服务器。

* 如果是内置方式则通过`TomcatServletWebServerFactory`工厂类来获取一个首选的web服务器,然后进行服务器的初始化配置,应用加载生效以及服务器启动的操作。
           

2.2 嵌入式的Servlet容器工厂创建tomcat容器,初始化并启动容器

2.2.1 嵌入式Servlet容器工厂的自动配置类

ServletWebServerFactoryAutoConfiguration

// 标注这是一个配置类
@Configuration(
    proxyBeanMethods = false
)
// 决定配置类的加载顺序的,当注解里的值越小越先加载
@AutoConfigureOrder(-2147483648)
// 表示当前必须有 servlet-api 依赖存在
@ConditionalOnClass({ServletRequest.class})
// 仅仅基于Servlet的web应用程序
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
// 向IOC容器中添加ServerProperties组件
@EnableConfigurationProperties({ServerProperties.class})
// 向IOC容器中注入一些组件
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,  
            EmbeddedTomcat.class, 
            EmbeddedJetty.class, 
            EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
    //略
}
           

@Import

注解可以看到,这个配置类向容器中批量添加组件,支持添加三种web服务器(Tomcat、Jetty、Undertow),而具体要配置哪个服务器,由

ServletWebServerFactoryConfiguration

决定,同时还定义了配置的顺序Tomcat>Jetty>Undertow,意思就是当环境中有Tomcat满足的依赖时就会优先使用Tomcat,依次往后推。

综合来看,

ServletWebServerFactoryConfiguration

自动配置类作了以下几件事:

  • 向IOC容器中添加了内部类

    BeanPostProcessorsRegistrar

    这个bean后置处理器注册器组件,这个组件也是向IOC容器中添加组件后置处理器,这里添加的是

    WebServerFactoryCustomizerBeanPostProcessor

    服务器工厂的定制器的后置处理器 ,对于

    xxxBeanPostProcessor

    来说,都是bean的后置处理器,即在bean创建完成后,在其初始化前后进行拦截处理。注册的后置处理器类

    WebServerFactoryCustomizerBeanPostProcessor

    ,在ioc容器启动的时候会调用。
  • 向IOC容器中添加了

    ServletWebServerFactoryConfiguration.EmbeddedTomcat

    等嵌入容器相关配置(这里以 tomcat 相关的配置为例)。
  • 向IOC容器中添加了

    ServletWebServerFactoryCustomizer

    TomcatServletWebServerFactoryCustomizer

    两个

    WebServerFactoryCustomizer

    类型的 bean。

1> 注册后置处理器(BeanPostProcessorsRegistrar)

部分代码

// ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
    // 略

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    if (this.beanFactory != null) {
            // 注册 webServerFactoryCustomizerBeanPostProcessor
            this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class);
            // 注册 errorPageRegistrarBeanPostProcessor
            this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);
        }
    }

    // 略
}
           

BeanPostProcessorsRegistar

注册了两个 bean,一个

WebServerFactoryCustomizerBeanPostProcessor

,一个

errorPageRegistrarBeanPostProcessor

;这两个都实现类

BeanPostProcessor

接口,属于 bean 的后置处理器,作用是在 bean 初始化前后加一些自己的逻辑处理。

  • WebServerFactoryCustomizerBeanPostProcessor

    :作用是在

    WebServerFactory

    初始化时调用上面自动配置类注入的那些

    WebServerFactoryCustomizer

    ,然后调用 WebServerFactoryCustomizer 中的

    customize

    方法来 处理 WebServerFactory。
  • errorPageRegistrarBeanPostProcessor:和上面的作用差不多,不过这个是处理 ErrorPageRegistrar 的。

2> 后置处理器(WebServerFactoryCustomizerBeanPostProcessor)

这个后置处理器,拿到所有WebServerFactoryCustomizer,分别执行对应的customize方法,定制Servlet容器,自定义的WebServerFactoryCustomizer是最后执行的,所以可以覆盖之前的默认的配置,最后 绑定到 工厂类(WebServerFactory)。

部分代码

// org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
    // 初始化 bean 之前调用
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 判断这个bean 是不是 继承自 WebServerFactory
        if (bean instanceof WebServerFactory) {
            // 是则执行
            this.postProcessBeforeInitialization((WebServerFactory)bean);
        }
        return bean;
    }
    
    private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
        // 遍历WebServerFactory 中所有 WebServerFactoryCustomizer 类,执行对应的customize方法,定制Servlet容器
        ((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
            customizer.customize(webServerFactory);
        });
    }

    // 初始化 bean 之后调用
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
           

3> web服务工厂定制器(WebServerFactoryCustomizer)

TomcatWebServerFactoryCustomizer

ServletWebServerFactoryCustomizer

为例

这两个 Customizer 实际上就是去处理一些配置值,然后绑定到 各自的工厂类中,将

ServerProperties

配置值绑定给

ConfigurableServletWebServerFactory

对象实例上

TomcatWebServerFactoryCustomizer

部分代码

public class TomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {
    // 略

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        // 拿到 tomcat 相关的配置
        ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
        // server.tomcat.additional-tld-skip-patterns
        if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {
            factory.getTldSkipPatterns()
                .addAll(tomcatProperties.getAdditionalTldSkipPatterns());
        }
        // server.redirectContextRoot
        if (tomcatProperties.getRedirectContextRoot() != null) {
            customizeRedirectContextRoot(factory,
                                        tomcatProperties.getRedirectContextRoot());
        }
        // server.useRelativeRedirects
        if (tomcatProperties.getUseRelativeRedirects() != null) {
            customizeUseRelativeRedirects(factory,
                                        tomcatProperties.getUseRelativeRedirects());
        }
        // 同上,略
    }
    // 略
}
           

ServletWebServerFactoryCustomizer

部分代码

public class ServletWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
    // 略

    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
        // 端口
        map.from(this.serverProperties::getPort).to(factory::setPort);
        // address
        map.from(this.serverProperties::getAddress).to(factory::setAddress);
        // contextPath
        map.from(this.serverProperties.getServlet()::getContextPath)
            .to(factory::setContextPath);
        // displayName
        map.from(this.serverProperties.getServlet()::getApplicationDisplayName)
            .to(factory::setDisplayName);
        // session 配置
        map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
        // ssl
        map.from(this.serverProperties::getSsl).to(factory::setSsl);
        // jsp
        map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
        // 压缩配置策略实现
        map.from(this.serverProperties::getCompression).to(factory::setCompression);
        // http2 
        map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
        // serverHeader
        map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
        // contextParameters
        map.from(this.serverProperties.getServlet()::getContextParameters)
            .to(factory::setInitParameters);
        // 同上,略
    }
    //略
}
           

2.2.2 嵌入式的Servlet容器工厂的配置类

ServletWebServerFactoryConfiguration

,分别对上述支持的三种servlet容器进行了配置。
@Configuration(
    proxyBeanMethods = false
)
class ServletWebServerFactoryConfiguration {
    // 略 三个内部类
}
           

可以发现这个类中有三个内部类,而这三个内部类也是具体的Servlet容器工厂配置类,分别通过

@ConditionalOnClass

@ConditionalOnMissingBean

限制配置类是否生效(判断是否存在自己手动添加的ServletWebServerFactory),由此可见,要想切换servlet容器,只需要添加相应容器的依赖,排除其他容器依赖即可。

以嵌入式的Tomcat 服务器为例,判断环境中是否引入了Tomcat所需的依赖

Servlet.class

,

Tomcat.class

,

UpgradeProtocol.class

,同时用户没有自己进行Web服务器配置(比如自己通过实现

ServletWebServerFactory

接口进行手动配置web服务器),这个工厂类内部就会对Tomcat进行一些初始化操作,那么这个Tomcat服务器就会生效。

// 嵌入式的Tomcat配置类
@Configuration(
    proxyBeanMethods = false
)
// 判断环境中是否引入了Tomcat所需的依赖`Servlet.class`, `Tomcat.class`, `UpgradeProtocol.class`
@ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
// 用户没有自己实现`ServletWebServerFactory`接口,配置Web服务,加入IOC容器
@ConditionalOnMissingBean(
    value = {ServletWebServerFactory.class},
    search = SearchStrategy.CURRENT
)
static class EmbeddedTomcat {
    EmbeddedTomcat() {
    }
    // 通过@Bean向容器中添加了TomcatServletWebServerFactory组件
    @Bean
    TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.getTomcatConnectorCustomizers().addAll((Collection)connectorCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatContextCustomizers().addAll((Collection)contextCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatProtocolHandlerCustomizers().addAll((Collection)protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
        return factory;
    }
}
           

可以知道这是 TomcatServletWebServer 容器工厂,

TomcatServletWebServerFactory

有一个

getWebServer

方法获得TomcatWebServer,对Tomcat进行的一些初始化操作,最重要的操作都在

getWebServer

方法内。

  • getWebServer

    方法:
    • 起初先创建一个tomcat,在设置一些配置,再通过

      getTomcatWebServer(tomcat)

      获得TomcatWebServer。
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    // 创建 Tomcat
    // 用的是最底层的tomcat实例进行配置(通过new Tomcat的方式,而这个Tomcat是tomcat源码包的一个实例类 package org.apache.catalina.startup)
    // 主要对端口,协议,tomcat组件对象等进行初始化并封装
    Tomcat tomcat = new Tomcat();
    // 以下是对 Tomcat 的一些设置
    File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    this.customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    this.configureEngine(tomcat.getEngine());
    Iterator var5 = this.additionalTomcatConnectors.iterator();

    while(var5.hasNext()) {
        Connector additionalConnector = (Connector)var5.next();
        tomcat.getService().addConnector(additionalConnector);
    }
    // 将要发布的Web应用信息Context初始化到tomcat中
    this.prepareContext(tomcat.getHost(), initializers);
    // 获得 TomcatWebServer
    return this.getTomcatWebServer(tomcat);
}
           

进入到

getTomcatWebServer(tomcat)

方法中

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    // 对 tomcat 的初始化,进行封装和启动
    // 最后将这个tomcat对象封装为一个TomcatWebServer对象供SpringBoot启动时调用
    return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
}
           

进入

TomcatWebServer

类中

// 对 tomcat 的初始化,进行封装和启动
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
    this.monitor = new Object();
    this.serviceConnectors = new HashMap();
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
    // 初始化 Tomcat
    this.initialize();
}

// 初始化 tomcat ,并启动
private void initialize() throws WebServerException {
    logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
    synchronized(this.monitor) {
        try {
            this.addInstanceIdToEngineName();
            Context context = this.findContext();
            context.addLifecycleListener((event) -> {
                if (context.equals(event.getSource()) && "start".equals(event.getType())) {
                    this.removeServiceConnectors();
                }

            });
            // 对初始化好的 tomcat 进行启动
            this.tomcat.start();
            this.rethrowDeferredStartupExceptions();

            try {
                ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
            } catch (NamingException var5) {
            }

            this.startDaemonAwaitThread();
        } catch (Exception var6) {
            this.stopSilently();
            this.destroySilently();
            throw new WebServerException("Unable to start embedded Tomcat", var6);
        }

    }
}
           

综上所述,Servlet容器的启动原理,实际上是SpringBoot通过创建原生Tomcat对象,对这个对象进行端口,协议,组件等初始化,并且将Web应用信息Context对象封装到这个 tomcat对象中,然后,在Web应用信息配置生命周期监听生效后,启动tomcat,最后将这个过程封装到一个WebServer对象中供SpringBoot启动时调用。