SpringBoot——嵌入式Servlet容器自动配置原理以及启动原理

导读:本篇文章讲解 SpringBoot——嵌入式Servlet容器自动配置原理以及启动原理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

版本说明:

  • 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经过一系列调用会通过ServletWebServerApplicationContextonRefresh方法创建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容器中添加了ServletWebServerFactoryCustomizerTomcatServletWebServerFactoryCustomizer 两个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)

TomcatWebServerFactoryCustomizerServletWebServerFactoryCustomizer为例

这两个 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启动时调用。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69769.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!