Spring Boot中的Web服务器

Spring Boot提供了对Web服务器的内置支持,会自动配置和创建Web服务器。本文分析最常见的Servlet服务器Tomcat和基于Netty的WebFlux服务器的配置与创建过程。


上下文refresh

Spring Boot运行流程一文中,提到Spring Boot会刷新应用上下文。而该动作是由Spring Framework提供的AbstractApplicationContext中的refresh方法来实现的。

java
spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        /*
         * 通过new DefaultListableBeanFactory()的方式创建容器,
         * 这一步会加载bean的定义
         */
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            // 会注册web作用域
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // Invoke factory processors registered as beans in the context.
            /*
             * 执行BeanFactoryPostProcessor
             */
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            /*
             * 往beanPostProcessors属性中add后置处理器并整理好顺序,便于后续执行
             */
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            // Initialize message source for this context.
            /*
             * 这一步较简单,向容器里注册一个一个事件源的单例Bean:MessageSource
             */
            initMessageSource();

            // Initialize event multicaster for this context.
            /*
             * 初始化Spring的事件多播器:ApplicationEventMulticaster
             */
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            /*
             * 模板方法,子类会实现
             * 比如在Spring Boot中,会在该方法中创建web服务器
             */
            onRefresh();

            // Check for listener beans and register them.
            /*
             * 注册事件监听器
             */
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            /*
             * ---- 极其重要步骤 ----
             * 会实例化单例bean
             */
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            // 容器刷新收尾工作
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}

在该方法中会调用onRefresh方法,该方法默认是一个空方法,具体的扩展需要由子类来实现。同样地在Spring Boot运行流程一文中,根据不同的web应用类型创建了不同类型的应用上下文对象。

  • 对于Servet创建的是AnnotationConfigServletWebServerApplicationContext,在其直接父类ServletWebServerApplicationContext中重写了onRefresh方法。
  • 对于Reactive创建的是AnnotationConfigReactiveWebServerApplicationContext,在其直接父类ReactiveWebServerApplicationContext中重写了onRefresh方法。
v2.7.x
Servlet
Reactive
<
>
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java
@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        // 创建web容器
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java
@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        // 创建HttpServer
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start reactive web server", ex);
    }
}

下面分别对Servlet和Reactive两种类型的Web容器创建过程进行分析。

Servlet

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java
private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    // 如果webServer为null,则新建一个。一般都是这种情况。
    if (webServer == null && servletContext == null) {
        StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
        // 获取WebServer工厂,这里关注TomcatServletWebServerFactory就是了
        ServletWebServerFactory factory = getWebServerFactory();
        createWebServer.tag("factory", factory.getClass().toString());
        // 创建WebServer实例,并传入ServletContainerInitializer
        this.webServer = factory.getWebServer(getSelfInitializer());
        createWebServer.end();
        // 向容器中注入两个bean
        getBeanFactory().registerSingleton("webServerGracefulShutdown",
                new WebServerGracefulShutdownLifecycle(this.webServer)); // 负责设置running标志位和停止WebServer
        getBeanFactory().registerSingleton("webServerStartStop",
                new WebServerStartStopLifecycle(this, this.webServer)); // 负责实际启动和停止WebServer
    }
    else if (servletContext != null) { // 如果servletContext对象存在
        try {
            // 则通过SCI来初始化
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}

getWebServerFactory方法

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java
protected ServletWebServerFactory getWebServerFactory() {
    // Use bean names so that we don't consider the hierarchy
    // 获取容器中所有ServletWebServerFactory类型的bean名称
    String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    // 如果一个也没有,则抛出异常
    if (beanNames.length == 0) {
        throw new MissingWebServerFactoryBeanException(getClass(), ServletWebServerFactory.class,
                WebApplicationType.SERVLET);
    }
    // 如果大于1个,也会抛出异常
    if (beanNames.length > 1) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
                + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
    // 获取WebServerFactory实例
    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

该方法中获取了bean工厂中类型为ServletWebServerFactory的bean实例。而这个bean实例是怎么添加到工厂中的呢?实际上Spring Boot提供了ServletWebServerFactoryAutoConfiguration这个自动配置类,会通过@Import注解引入一系列的Web服务器工厂类。

v2.7.x
java
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })

可以看到这里引入了三种工厂类,分别针对Tomcat、Jetty和Undertow,本文以Tomcat为例。

v2.7.x
java
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration.java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {

    // 注入TomcatServletWebServerFactory实例
    @Bean
    TomcatServletWebServerFactory tomcatServletWebServerFactory(
            ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
            ObjectProvider<TomcatContextCustomizer> contextCustomizers,
            ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
        // 创建工厂类对象
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.getTomcatConnectorCustomizers()
                .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatContextCustomizers()
                .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatProtocolHandlerCustomizers()
                .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
        return factory;
    }

}

注意该类上面的两个conditional相关注解,必须满足这两个条件,该配置类才会被加载,对于其他两种工厂类的是一样的。

那如果在项目中导入了多种Web服务器的依赖会怎么样?比如同时导入Tomcat和Jetty的依赖,两种工厂类的@ConditionalOnClass注解都满足了要求,但是由于受到@ConditionalOnMissingBean注解的限制,最多只能有一个生效。此时在上面自动配置类上的@Import注解中的类顺序就非常重要,排在前面的会被先加载,对于上面的情况,Tomcat的工厂类在Jetty的工厂类前面,所以同时引入了Tomcat和Jetty的依赖,只有Tomcat会生效,而不会创建Jetty的Web服务器。

好,回到主线,此时getWebServerFactory返回了TomcatServletWebServerFactory类型的对象。

getWebServer方法

接下来调用了Web工厂类的getWebServer方法来获取Web服务器,并传入了一个初始化器。先来看这个初始化器是什么。

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java
// 返回一个SCI对象
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    return this::selfInitialize;
}
// 该方法负责初始化servletContext对象
private void selfInitialize(ServletContext servletContext) throws ServletException {
    // 设置上下文attr属性
    prepareWebApplicationContext(servletContext);
    // 向spring容器中注入ServletContextScope
    registerApplicationScope(servletContext);
    // 将servletContext对象注入到容器中
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    // 遍历容器中设置的其他Servlet上下文初始化器对象
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        // 执行Servlet上下文初始化器
        beans.onStartup(servletContext);
    }
}

可以看到,这个初始化器就是对servlet上下文做了相关配置,最重要的一点是执行了Servlet的上下文初始化器,这里就不继续深入了,后续针对具体的初始化器做具体的分析。

上面将该初始化器作为参数传递给了TomcatServletWebServerFactorygetWebServer方法。

v2.7.x
getWebServer
createTempDir
<
>
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 禁用JMX注册
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    // 创建Tomcat实例
    Tomcat tomcat = new Tomcat();
    // 获取或者创建Tomcat的工作目录
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    // 设置工作目录
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    for (LifecycleListener listener : this.serverLifecycleListeners) {
        tomcat.getServer().addLifecycleListener(listener);
    }
    // 创建连接器组件
    Connector connector = new Connector(this.protocol);
    // 设置在失败时抛出异常
    connector.setThrowOnFailure(true);
    // 获取service组件,不存在则会创建,然后添加连接器组件
    tomcat.getService().addConnector(connector);
    // 定制化连接器,会设置监听端口
    customizeConnector(connector);
    // 为tomcat实例设置连接器
    tomcat.setConnector(connector);
    // 获取Host组件,内部会先获取Engine组件,如果不存在则新建;然后设置主机自动部署任务为false
    tomcat.getHost().setAutoDeploy(false);
    // 配置Engine组件
    configureEngine(tomcat.getEngine());
    // 添加额外的连接器组件
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    // 创建Tomcat中的Context组件,并设置其属性,然后将其添加到Host组件下
    prepareContext(tomcat.getHost(), initializers);
    // 根据Tomcat实例,创建TomcatWebServer实例对象
    return getTomcatWebServer(tomcat);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/AbstractConfigurableWebServerFactory.java
protected final File createTempDir(String prefix) {
    try {
        /*
         * 在操作系统的临时目录下创建tomcat的工作目录,
         * 在Linux中的例子为:/tmp/tomcat.8080.11102105182777561955,结尾的数字是底层JDK创建临时文件时生成的随机数
         */
        File tempDir = Files.createTempDirectory(prefix + "." + getPort() + ".").toFile();
        tempDir.deleteOnExit();
        return tempDir;
    }
    catch (IOException ex) {
        throw new WebServerException(
                "Unable to create tempDir. java.io.tmpdir is set to " + System.getProperty("java.io.tmpdir"), ex);
    }
}

getWebServer方法中创建了的Tomcat对象并做了相关的配置,随后将其作为参数调用getTomcatWebServer来创建TomatWebServer对象。

调用customizeConnector方法会定制化连接器,会设置Tomcat所要监听的端口。端口及其他的信息是通过ServletWebServerFactoryCustomizercustomize方法来设置的。而该方法是在WebServerFactoryCustomizerBeanPostProcessor这个后置处理器的后置处理逻辑中调用到的。 ServletWebServerFactoryCustomizercustomize方法是设置到AbstractConfigurableWebServerFactory类中定义的属性上的,而该类是具体的Web服务器工厂类的父类。

注意Tomcat类是Tomcat提供的,不是Spring Boot中的,而TomcatWebServer是Spring Boot提供的类,是对Tomcat的封装。

创建并配置Tomcat的Context组件

v2.7.x
prepareContext
configureContext
<
>
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
    File documentRoot = getValidDocumentRoot();
    // 创建tomcat中的context组件,该类是Spring Boot中实现的,继承tomcat中的StandardContext
    TomcatEmbeddedContext context = new TomcatEmbeddedContext();
    if (documentRoot != null) {
        context.setResources(new LoaderHidingResourceRoot(context));
    }
    // 为context组件设置一些属性
    context.setName(getContextPath());
    context.setDisplayName(getDisplayName());
    context.setPath(getContextPath());
    File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
    context.setDocBase(docBase.getAbsolutePath());
    context.addLifecycleListener(new FixContextListener());
    context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
            : ClassUtils.getDefaultClassLoader());
    resetDefaultLocaleMapping(context);
    addLocaleMappings(context);
    try {
        context.setCreateUploadTargets(true);
    }
    catch (NoSuchMethodError ex) {
        // Tomcat is < 8.5.39. Continue.
    }
    configureTldPatterns(context);
    WebappLoader loader = new WebappLoader();
    // 设置类加载器名称
    loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
    loader.setDelegate(true);
    // 设置web应用加载器
    context.setLoader(loader);
    // 是否注册默认servlet,该方法默认返回false
    if (isRegisterDefaultServlet()) {
        // 注册默认的servlet
        addDefaultServlet(context);
    }
    // JSP相关
    if (shouldRegisterJspServlet()) {
        addJspServlet(context);
        addJasperInitializer(context);
    }
    // 添加声明周期监听器,用于配置静态资源
    context.addLifecycleListener(new StaticResourceConfigurer(context));
    // 添加一些默认的初始化器,并和上层传递过来的合并在一起
    ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
    // 为host添加context子组件
    host.addChild(context);
    // 将initializers设置到context中
    configureContext(context, initializersToUse);
    // 默认是空操作
    postProcessContext(context);
}
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
    /*
     * 创建starter对象,这是一个特殊的servlet容器初始化器,封装其他多个servlet上下文初始化器
     * 注意区分servlet容器初始化器,和servlet上下文初始化器。
     * 前者是servlet规范中的定义,而后者是Spring Boot中的定义。
     */
    TomcatStarter starter = new TomcatStarter(initializers);
    if (context instanceof TomcatEmbeddedContext) {
        TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
        embeddedContext.setStarter(starter);
        embeddedContext.setFailCtxIfServletStartFails(true);
    }
    // 添加servlet容器初始化器
    context.addServletContainerInitializer(starter, NO_CLASSES);
    // 添加应用生命周期事件监听器,默认是空列表
    for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
        context.addLifecycleListener(lifecycleListener);
    }
    // 将上下文Valve组件添加到context中,默认是空列表
    for (Valve valve : this.contextValves) {
        context.getPipeline().addValve(valve);
    }
    // 添加错误页
    for (ErrorPage errorPage : getErrorPages()) {
        org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
        tomcatErrorPage.setLocation(errorPage.getPath());
        tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
        tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
        context.addErrorPage(tomcatErrorPage);
    }
    // 添加mime映射
    for (MimeMappings.Mapping mapping : getMimeMappings()) {
        context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
    }
    // 配置session
    configureSession(context);
    // 配置cookie处理器
    configureCookieProcessor(context);
    new DisableReferenceClearingContextCustomizer().customize(context);
    // 添加web监听器,默认是空列表
    for (String webListenerClassName : getWebListenerClassNames()) {
        context.addApplicationListener(webListenerClassName);
    }
    // 通过servlet上下文自定义器来对上下文进行自定义操作,这里默认有5个
    for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
        // 执行自定义操作
        customizer.customize(context);
    }
}

这里创建的是TomcatEmbeddedContext,继承了Tomcat中的StandardContext类,然后调用configureContext方法对其进行配置。 这里将所有的servlet上下文初始化器封装进Spring Boot自定义的servlet容器初始化器TomcatStarter中,而在该类的onStartup中,会调用所封装的其他servlet上下文初始化器。

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatStarter.java
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
    try {
        // 遍历servlet上下文初始化器
        for (ServletContextInitializer initializer : this.initializers) {
            // 调用其onStartup方法
            initializer.onStartup(servletContext);
        }
    }
    catch (Exception ex) {
        this.startUpException = ex;
        // Prevent Tomcat from logging and re-throwing when we know we can
        // deal with it in the main thread, but log for information here.
        if (logger.isErrorEnabled()) {
            logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
                    + ex.getMessage());
        }
    }
}

注意区分ServletContextInitializerServletContainerInitializer,前者是Spring Boot中的类,后者是Servlet规范定义的类。

启动Tomcat

v2.7.x
constructor
initialize
<
>
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatWebServer.java
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
    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
    initialize();
}
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatWebServer.java
private void initialize() throws WebServerException {
    logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
    synchronized (this.monitor) {
        try {
            addInstanceIdToEngineName();

            Context context = findContext();
            // 添加生命周期监听器
            context.addLifecycleListener((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, context.getNamingToken(), 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) {
            stopSilently();
            destroySilently();
            throw new WebServerException("Unable to start embedded Tomcat", ex);
        }
    }
}

可以看到,在initialize方法中就已经调用了Tomcat类的start方法来启动Tomcat了,后续过程参考Tomcat的启动流程

启动过程

上面不是以及启动过了吗,那这里的启动过程是什么情况?上面确实是启动过服务器了,但这里的”启动过程“更像是做一些启动后的事情,尽管Spring Boot中的代码将其叫作start。 启动的方式是根据SmartLifecycle来启动的,会调用到WebServerStartStopLifecyclestart方法。

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/WebServerStartStopLifecycle.java
@Override
public void start() {
    // 启动WebServer
    this.webServer.start();
    this.running = true;
    // 发布WebServer初始化完成的事件
    this.applicationContext
            .publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
}

SmartLifecycle又是怎么被调用到的呢? 实际上在上面AbstractApplicationrefresh方法中,调用了finishRefresh方法。

java
spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
@SuppressWarnings("deprecation")
protected void finishRefresh() {
    // Clear context-level resource caches (such as ASM metadata from scanning).
    clearResourceCaches();

    // Initialize lifecycle processor for this context.
    // 初始化生命周期处理器
    initLifecycleProcessor();

    // Propagate refresh to lifecycle processor first.
    getLifecycleProcessor().onRefresh();

    // Publish the final event.
    publishEvent(new ContextRefreshedEvent(this));

    // Participate in LiveBeansView MBean, if active.
    if (!NativeDetector.inNativeImage()) {
        LiveBeansView.registerApplicationContext(this);
    }
}

这里调用了LifecycleProcessoronRefresh方法,getLifecycleProcessor方法默认返回的是DefaultLifecycleProcessor

java
spring-context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java
@Override
public void onRefresh() {
    startBeans(true);
    this.running = true;
}
private void startBeans(boolean autoStartupOnly) {
    Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
    Map<Integer, LifecycleGroup> phases = new TreeMap<>();

    // 对lifecycle对象phrase进行分组,
    lifecycleBeans.forEach((beanName, bean) -> {
        if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
            int phase = getPhase(bean);
            // 如果phrases中不存在phase对应的组,
            phases.computeIfAbsent(
                    phase,
                    // 则创建新的组对象
                    p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
            ).add(beanName, bean);
        }
    });
    if (!phases.isEmpty()) {
        // 执行start方法
        phases.values().forEach(LifecycleGroup::start);
    }
}

在这里,调用了web服务器的start方法。还是以Tomcat为例:

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatWebServer.java
@Override
public void start() throws WebServerException {
    synchronized (this.monitor) {
        if (this.started) {
            return;
        }
        try {
            addPreviouslyRemovedConnectors();
            // 获取Connector
            Connector connector = this.tomcat.getConnector();
            if (connector != null && this.autoStart) {
                // 执行延迟启动时加载时加载
                performDeferredLoadOnStartup();
            }
            checkThatConnectorsHaveStarted();
            this.started = true;
            logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
                    + getContextPath() + "'");
        }
        catch (ConnectorStartFailedException ex) {
            stopSilently();
            throw ex;
        }
        catch (Exception ex) {
            PortInUseException.throwIfPortBindingException(ex, () -> this.tomcat.getConnector().getPort());
            throw new WebServerException("Unable to start embedded Tomcat server", ex);
        }
        finally {
            Context context = findContext();
            ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
        }
    }
}

Reactive

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java
private void createWebServer() {
    WebServerManager serverManager = this.serverManager;
    if (serverManager == null) {
        StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
        // 获取WebServer工厂的名称
        String webServerFactoryBeanName = getWebServerFactoryBeanName();
        // 从容器中获取WebServer工厂实例
        ReactiveWebServerFactory webServerFactory = getWebServerFactory(webServerFactoryBeanName);
        createWebServer.tag("factory", webServerFactory.getClass().toString());
        // 获取是否延迟初始化,对于NettyReactiveWebServerFactory来说是false
        boolean lazyInit = getBeanFactory().getBeanDefinition(webServerFactoryBeanName).isLazyInit();
        // 创建WebServerManager对象并传入WebServer工厂,内部会创建WebServer
        this.serverManager = new WebServerManager(this, webServerFactory, this::getHttpHandler, lazyInit);
        getBeanFactory().registerSingleton("webServerGracefulShutdown",
                new WebServerGracefulShutdownLifecycle(this.serverManager.getWebServer()));
        getBeanFactory().registerSingleton("webServerStartStop",
                /*
                 * 该bean是一个SmartLifecycle类型实例
                 * (AbstractApplicationContext#refresh方法的finishRefresh阶段会处理),
                 * 负责WebServer的启动和关闭
                 */
                new WebServerStartStopLifecycle(this.serverManager));
        createWebServer.end();
    }
    initPropertySources();
}

getWebServerFactoryBeanName方法

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java
protected String getWebServerFactoryBeanName() {
    // Use bean names so that we don't consider the hierarchy
    /*
     * 至于是哪种类型的server被注入到了bean容器中,参考ReactiveWebServerFactoryAutoConfiguration。
     */
    String[] beanNames = getBeanFactory().getBeanNamesForType(ReactiveWebServerFactory.class);
    if (beanNames.length == 0) {
        throw new MissingWebServerFactoryBeanException(getClass(), ReactiveWebServerFactory.class,
                WebApplicationType.REACTIVE);
    }
    if (beanNames.length > 1) { // 只允许有一个ReactiveWebServerFactory类型的bean
        throw new ApplicationContextException("Unable to start ReactiveWebApplicationContext due to multiple "
                + "ReactiveWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
    return beanNames[0];
}

该方法获取了bean工厂中ReactiveWebServerFactory类型实例的名称。那是什么时候添加了这些类型的bean呢?在ReactiveWebServerFactoryAutoConfiguration这个自动配置类上存在一个@Import注解,会引入几个与Web服务器相关的配置类。

v2.7.x
java
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfiguration.java
/*
 * 这里注入了多个,那么在ReactiveWebServerApplicationContext#getWebServerFactoryBeanName方法中
 * 会判断是否只有一个ReactiveWebServerFactory实例,如果有多个会报错,那为什么这里还会注入这么多个?
 *
 * 这些都是配置类,类上有注解@ConditionalOnMissingBean(ReactiveWebServerFactory.class),
 * 如果项目中配置了下面多种serverFactory需要的依赖,那么只有排在下面顺序中前面的才能生效。
 * 因为其先导入,后导入的在检测上面的注解时,发现已经存在了ReactiveWebServerFactory类型的bean,所以不会生效。
 */
@Import({ ReactiveWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ReactiveWebServerFactoryConfiguration.EmbeddedJetty.class,
		ReactiveWebServerFactoryConfiguration.EmbeddedUndertow.class,
		// 会注入NettyReactiveWebServerFactory
		ReactiveWebServerFactoryConfiguration.EmbeddedNetty.class,
		// 为了测试Reactor-Netty,把tomcat的放在后面
		ReactiveWebServerFactoryConfiguration.EmbeddedTomcat.class,
})

以Netty为例:

v2.7.x
java
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java
@Configuration(proxyBeanMethods = false)
// 此注解的目的是避免导入多种serverFactory,如果先导入了其他类型的,那么该配置类不会被加载。
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ HttpServer.class })
static class EmbeddedNetty {

    @Bean
    @ConditionalOnMissingBean
    ReactorResourceFactory reactorServerResourceFactory() {
        return new ReactorResourceFactory();
    }

    @Bean
    NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ReactorResourceFactory resourceFactory,
            ObjectProvider<NettyRouteProvider> routes, ObjectProvider<NettyServerCustomizer> serverCustomizers) {
        // 创建WebServer工厂对象
        NettyReactiveWebServerFactory serverFactory = new NettyReactiveWebServerFactory();
        serverFactory.setResourceFactory(resourceFactory);
        routes.orderedStream().forEach(serverFactory::addRouteProviders);
        serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
        return serverFactory; // 返回对象并被加载到spring容器中
    }

}

这里的两个条件注解和上面Servlet小节中介绍的是一样的,不再赘述。

getWebServerFactory方法

接下来会以上面获取到的Web服务器工厂bean的名称作为参数,调用getWebServerFactory方法来获取Web服务器工厂对象。

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java
protected ReactiveWebServerFactory getWebServerFactory(String factoryBeanName) {
    return getBeanFactory().getBean(factoryBeanName, ReactiveWebServerFactory.class);
}

创建WebServerManager

getHttpHandler方法作为处理器提供者(handlerSupplier)传递给WebServerManager的构造方法。

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java
protected HttpHandler getHttpHandler() {
    // Use bean names so that we don't consider the hierarchy
    /*
     * 从bean工厂中获取请求处理器信息,在HttpHandlerAutoConfiguration中会注入该类型的bean。
     */
    String[] beanNames = getBeanFactory().getBeanNamesForType(HttpHandler.class);
    // 不能一个都不存在
    if (beanNames.length == 0) {
        throw new ApplicationContextException(
                "Unable to start ReactiveWebApplicationContext due to missing HttpHandler bean.");
    }
    // 也不能存在多个
    if (beanNames.length > 1) {
        throw new ApplicationContextException(
                "Unable to start ReactiveWebApplicationContext due to multiple HttpHandler beans : "
                        + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
    return getBeanFactory().getBean(beanNames[0], HttpHandler.class);
}

该方法其实就是在向bean工厂中获取HttpHandler类型的实例,该类型的实例是在HttpHandlerAutoConfiguration这个自动配置类中被添加到bean工厂的。

v2.7.x
java
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/HttpHandlerAutoConfiguration.java
@AutoConfiguration(after = { WebFluxAutoConfiguration.class })
@ConditionalOnClass({ DispatcherHandler.class, HttpHandler.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnMissingBean(HttpHandler.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class HttpHandlerAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	public static class AnnotationConfig {

		private final ApplicationContext applicationContext;

		public AnnotationConfig(ApplicationContext applicationContext) {
			this.applicationContext = applicationContext;
		}

		@Bean
		public HttpHandler httpHandler(ObjectProvider<WebFluxProperties> propsProvider) {
			// 创建HTTP处理器,WebHttpHandlerBuilder属于spring-web模块,其实这里创建的是HttpWebHandlerAdapter,该bean同时也是WebHandler
			HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();
			WebFluxProperties properties = propsProvider.getIfAvailable();
			if (properties != null && StringUtils.hasText(properties.getBasePath())) {
				Map<String, HttpHandler> handlersMap = Collections.singletonMap(properties.getBasePath(), httpHandler);
				return new ContextPathCompositeHandler(handlersMap);
			}
			return httpHandler;
		}

	}

}

至于这个httpHandler是怎么创建和初始化的,后续新写文章来分析。

回到主线,来看看WebServerManager的构造方法:

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/WebServerManager.java
WebServerManager(ReactiveWebServerApplicationContext applicationContext, ReactiveWebServerFactory factory,
        Supplier<HttpHandler> handlerSupplier, boolean lazyInit) {
    this.applicationContext = applicationContext;
    Assert.notNull(factory, "Factory must not be null");
    this.handler = new DelayedInitializationHttpHandler(handlerSupplier, lazyInit);
    // 创建WebServer
    this.webServer = factory.getWebServer(this.handler);
}

可以看到,内部通过ReactiveWebServerFactory来创建Web服务器。

getWebServer方法

这里以Netty为例:

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactory.java
@Override
public WebServer getWebServer(HttpHandler httpHandler) {
    // 创建server,类型是HttpServerBind
    HttpServer httpServer = createHttpServer();
    /*
     * 创建适配器,该适配器是Reactor-Netty和Spring的适配器,
     * 在该适配器的apply方法中会调用httpHandler来处理请求,即请求进入了Spring的范畴。
     * 至于这里的httpHandler是什么,参考HttpHandlerAutoConfiguration,该类中会注入该类型的bean。
     */
    ReactorHttpHandlerAdapter handlerAdapter = new ReactorHttpHandlerAdapter(httpHandler);
    // 创建NettyWebServer实例,对httpServer进行了封装,并传入了请求处理器handlerAdapter
    NettyWebServer webServer = createNettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout,
            getShutdown());
    webServer.setRouteProviders(this.routeProviders);
    return webServer;
}

该方法内主要做了下面几件事情:

  • 创建HttpServerHttpServers是project-reactor这个项目提供的类。
  • 创建适配器:ReactorHttpHandlerAdapter是project-reactor也这个项目提供的类,后面单独写文章来分析。
  • 创建NettyWebServer

创建HttpServer

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactory.java
private HttpServer createHttpServer() {
    // 创建的是HttpServerBind类型的实例
    HttpServer server = HttpServer.create();
    if (this.resourceFactory != null) {
        LoopResources resources = this.resourceFactory.getLoopResources();
        Assert.notNull(resources, "No LoopResources: is ReactorResourceFactory not initialized yet?");
        // 设置绑定所需的地址信息,this::getListenAddress会返回所要监听的地址和端口信息
        server = server.runOn(resources).bindAddress(this::getListenAddress);
    }
    else {
        server = server.bindAddress(this::getListenAddress);
    }
    // SSL相关
    if (getSsl() != null && getSsl().isEnabled()) {
        server = customizeSslConfiguration(server);
    }
    // 压缩相关
    if (getCompression() != null && getCompression().getEnabled()) {
        CompressionCustomizer compressionCustomizer = new CompressionCustomizer(getCompression());
        server = compressionCustomizer.apply(server);
    }
    // 设置协议
    server = server.protocol(listProtocols()).forwarded(this.useForwardHeaders);
    // 应用自定义设置
    return applyCustomizers(server);
}
private InetSocketAddress getListenAddress() {
    if (getAddress() != null) {
        return new InetSocketAddress(getAddress().getHostAddress(), getPort());
    }
    return new InetSocketAddress(getPort());
}

getListenAddress方法中,又会获取配置的所要监听的地址和端口。和上面Servlet服务器中一样,是由ServletWebServerFactoryCustomizercustomize方法来设置配置信息的。

创建NettyWebServer

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactory.java
NettyWebServer createNettyWebServer(HttpServer httpServer, ReactorHttpHandlerAdapter handlerAdapter,
        Duration lifecycleTimeout, Shutdown shutdown) {
    return new NettyWebServer(httpServer, handlerAdapter, lifecycleTimeout, shutdown);
}

这里将上面创建的HttpHandlerReactorHttpHandlerAdapter对象一并传递给NettyWebServer的构造方法。这里通过HttpHandlerchannelGroup方法进行底层的创建工作,由于这里进入了project-reactor项目的范畴,所以后面单独写文章分析。 好了,现在创建好了NettyWebServer对象,可以交由getWebServer方法返回了。

启动过程

启动方式和上面Servlet服务器一样,通过SmartLifecycle的方式来启动Web服务器。

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/WebServerStartStopLifecycle.java
@Override
public void start() {
    // 启动webServer
    this.weServerManager.start();
    this.running = true;
}

这里会调用到WebServerManagerstart方法:

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/WebServerManager.java
void start() {
    // 初始化请求处理器(特殊的请求处理器,其实是适配器)
    this.handler.initializeHandler();
    // 启动WebServer
    this.webServer.start();
    this.applicationContext
            .publishEvent(new ReactiveWebServerInitializedEvent(this.webServer, this.applicationContext));
}

这里调用了具体类型的WebServerstart方法,还是以Netty为例。

v2.7.x
java
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java
@Override
public void start() throws WebServerException {
    if (this.disposableServer == null) {
        try {
            // 启动httpServer
            this.disposableServer = startHttpServer();
        }
        catch (Exception ex) {
            PortInUseException.ifCausedBy(ex, ChannelBindException.class, (bindException) -> {
                if (bindException.localPort() > 0 && !isPermissionDenied(bindException.getCause())) {
                    throw new PortInUseException(bindException.localPort(), ex);
                }
            });
            throw new WebServerException("Unable to start Netty", ex);
        }
        if (this.disposableServer != null) { // 启动成功,打印日志
            logger.info("Netty started" + getStartedOnMessage(this.disposableServer));
        }

        // 创建线程
        startDaemonAwaitThread(this.disposableServer);
    }
}
DisposableServer startHttpServer() {
    HttpServer server = this.httpServer;
    // 下面这个条件默认是true
    if (this.routeProviders.isEmpty()) {
        // 设置请求处理器
        server = server.handle(this.handler);
    }
    else {
        server = server.route(this::applyRouteProviders);
    }
    if (this.lifecycleTimeout != null) {
        return server.bindNow(this.lifecycleTimeout);
    }
    /*
     * 执行bind操作,HttpServer是reactor-netty提供的,所以离开了spring-boot范畴,
     * 最后会一直执行到netty层面的ServerBootstrap#bind()
     */
    return server.bindNow();
}
private void startDaemonAwaitThread(DisposableServer disposableServer) {
    Thread awaitThread = new Thread("server") {

        @Override
        public void run() {
            // 阻塞直到服务停止,onDispose方法在DisposeChannel类中
            disposableServer.onDispose().block();
        }

    };
    awaitThread.setContextClassLoader(getClass().getClassLoader());
    awaitThread.setDaemon(false);
    // 启动线程
    awaitThread.start();
}

startHttpServer方法中,调用了底层的HttpServer来完成启动过程。在startDaemonAwaitThread方法中创建了一个新线程来等待底层的server结束运行。

总结

本文分析了Spring Boot中Servlet和Reactive两种Web服务器的创建过程,并分别以Tomcat和Netty为例分析了详细创建和启动过程。由于调用链条太长,本文仅仅这对Spring Boot内部的实现进行了分析,相关依赖的项目中的实现后面写文章单独进行分析。