Spring boot内嵌tomcat源码解析

阅读须知

  • 版本:2.0.4
  • 文章中使用/* */注释的方法会做深入分析

正文

我们知道,在使用Spring boot搭建web工程时,我们不需要自己搭建一个tomcat服务器,只需要引入spring-boot-starter-web,在应用启动时会自动启动嵌入版的tomcat作为应用服务器,下面我们来分析这个过程。

之前我们分析了Spring boot的启动流程和自动配置流程,而嵌入tomcat的启动同样需要依赖一个配置ServletWebServerFactoryAutoConfiguration,我们来看一下这个配置的内容:

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }

    @Bean
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }

    public static class BeanPostProcessorsRegistrar
            implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

        private ConfigurableListableBeanFactory beanFactory;

        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            if (beanFactory instanceof ConfigurableListableBeanFactory) {
                this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
            }
        }

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                BeanDefinitionRegistry registry) {
            if (this.beanFactory == null) {
                return;
            }
            registerSyntheticBeanIfMissing(registry,
                    "webServerFactoryCustomizerBeanPostProcessor",
                    WebServerFactoryCustomizerBeanPostProcessor.class);
            registerSyntheticBeanIfMissing(registry,
                    "errorPageRegistrarBeanPostProcessor",
                    ErrorPageRegistrarBeanPostProcessor.class);
        }

        private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
                String name, Class<?> beanClass) {
            if (ObjectUtils.isEmpty(
                    this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
                RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
                beanDefinition.setSynthetic(true);
                registry.registerBeanDefinition(name, beanDefinition);
            }
        }

    }

}

配置中主要做这么几件事,导入了内部类BeanPostProcessorsRegistrar,它实现了ImportBeanDefinitionRegistrar,作用我们在之前的文章中已经介绍过,我们可以实现ImportBeanDefinitionRegistrar来注册额外的BeanDefinition。导入了ServletWebServerFactoryConfiguration.EmbeddedTomcat配置(我们主要关注tomcat相关的配置)。注册了ServletWebServerFactoryCustomizer、TomcatServletWebServerFactoryCustomizer两个WebServerFactoryCustomizer类型的bean。我们来分别看一下上面提到的这些类的作用,首先来看内部类BeanPostProcessorsRegistrar:
ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar:

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

这里注册了两个BeanDefinition,WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor,它们都实现了BeanPostProcessor,后面我们会介绍它们的作用。
ServletWebServerFactoryConfiguration.EmbeddedTomcat:

@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {

    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }

}

这个配置很简单,就是注册了一个TomcatServletWebServerFactory类型的bean。

最后剩下两个WebServerFactoryCustomizer类型的bean,任何WebServerFactoryCustomizer类型的bean都可以收到WebServerFactory的回调,所以我们可以实现WebServerFactoryCustomizer来定义WebServerFactory。ServletWebServerFactoryCustomizer的作用是将ServerProperties的属性配置设置到ConfigurableServletWebServerFactory的对应属性上,这里主要设置的是一些通用的属性,上面注册的TomcatServletWebServerFactory就实现了ConfigurableServletWebServerFactory。而TomcatServletWebServerFactoryCustomizer则是为TomcatServletWebServerFactory设置了一些它需要的特有属性。

在分析Spring boot启动流程时,我们看到Spring boot会根据是否能够成功加载一些关键的类来确认web应用类型,而我们通常的web应用类型为WebApplicationType.SERVLET,接下来就会根据这个类型来创建ApplicationContext(对Spring boot启动流程不了解的童鞋可以参考:Spring boot启动流程源码解析):
SpringApplication:

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                // DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"
                contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, "
                            + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

所以在refresh上下文时调用的就是AnnotationConfigServletWebServerApplicationContext的refresh方法,在Spring源码解析之ApplicationContext这篇文章中我们分析了整个Spring上下文的初始化流程,主要分析的就是refresh方法,AnnotationConfigServletWebServerApplicationContext对refresh流程做了扩展,我们来看扩展实现:
ServletWebServerApplicationContext:

protected void onRefresh() {
    super.onRefresh();
    try {
        /* 创建web服务 */
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

ServletWebServerApplicationContext:

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        // 获取ServletWebServerFactory,这里就会获取到上文中注册bean TomcatServletWebServerFactory
        ServletWebServerFactory factory = getWebServerFactory();
        /* 获取ServletContextInitializer,获取WebServer */
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context",
                    ex);
        }
    }
    // 替换Servlet相关的属性资源
    initPropertySources();
}

ServletWebServerApplicationContext:

// ServletContextInitializer用于以编程方式配置Servlet 3.0+
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    return this::selfInitialize;
}

private void selfInitialize(ServletContext servletContext) throws ServletException {
    // 使用给定的完全加载的ServletContext准备WebApplicationContext
    prepareWebApplicationContext(servletContext);
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
            beanFactory);
    // 使用给定BeanFactory注册特定于Web的作用域(“request”,“session”,“globalSession”,“application”)
    WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
            getServletContext());
    existingScopes.restore();
    // 使用给定BeanFactory注册特定于Web的环境bean(“contextParameters”,“contextAttributes”)
    WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
            getServletContext());
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
}

TomcatServletWebServerFactory:

public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 这里主要是一些embed tomcat的api调用
    Tomcat tomcat = new 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);
    }
    // 准备TomcatEmbeddedContext并设置到tomcat中
    prepareContext(tomcat.getHost(), initializers);
    /* 构建TomcatWebServer */
    return getTomcatWebServer(tomcat);
}

TomcatServletWebServerFactory:

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    return new TomcatWebServer(tomcat, getPort() >= 0);
}

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    /* 初始化 */
    initialize();
}

TomcatWebServer:

private void initialize() throws WebServerException {
    TomcatWebServer.logger
            .info("Tomcat initialized with port(s): " + getPortsDescription(false));
    synchronized (this.monitor) {
        try {
            // egnineName拼接instanceId
            addInstanceIdToEngineName();
            Context context = findContext();
            context.addLifecycleListener((event) -> {
                if (context.equals(event.getSource())
                        && Lifecycle.START_EVENT.equals(event.getType())) {
                    /* 删除Connectors,以便在启动服务时不发生协议绑定 */
                    removeServiceConnectors();
                }
            });
            // 启动服务触发初始化侦听器
            this.tomcat.start();
            // 我们可以直接在主线程中重新抛出失败异常
            rethrowDeferredStartupExceptions();
            try {
                ContextBindings.bindClassLoader(context, context.getNamingToken(),
                        getClass().getClassLoader());
            }
            catch (NamingException ex) {
                // 未启用命名,继续
            }
            // 与Jetty不同,所有Tomcat线程都是守护线程。我们创建一个阻塞非守护线程来避免立即关闭
            startDaemonAwaitThread();
        }
        catch (Exception ex) {
            // 异常停止tomcat
            stopSilently();
            throw new WebServerException("Unable to start embedded Tomcat", ex);
        }
    }
}

TomcatWebServer:

private void removeServiceConnectors() {
    for (Service service : this.tomcat.getServer().findServices()) {
        Connector[] connectors = service.findConnectors().clone();
        // 将将要移除的connector放到缓存中暂存
        this.serviceConnectors.put(service, connectors);
        for (Connector connector : connectors) {
            // 移除connector
            service.removeConnector(connector);
        }
    }
}

这里涉及到一些embed tomcat的api调用,读者可以先去了解一下相关API。

下面我们再看另一个扩展refresh流程的方法:
ServletWebServerApplicationContext:

protected void finishRefresh() {
    // 首先调用父类的finishRefresh方法,这个过程我们在上文提供的文章中分析过
    super.finishRefresh();
    /* 启动WebServer */
    WebServer webServer = startWebServer();
    if (webServer != null) {
        // 发布WebServer初始化完成事件
        publishEvent(new ServletWebServerInitializedEvent(webServer, this));
    }
}

ServletWebServerApplicationContext:

private WebServer startWebServer() {
    WebServer webServer = this.webServer;
    if (webServer != null) {
        /* 启动WebServer */
        webServer.start();
    }
    return webServer;
}

TomcatWebServer:

public void start() throws WebServerException {
    synchronized (this.monitor) {
        if (this.started) {
            return;
        }
        try {
            /* 添加之前移除的connector */
            addPreviouslyRemovedConnectors();
            Connector connector = this.tomcat.getConnector();
            if (connector != null && this.autoStart) {
                /* 延迟加载启动 */
                performDeferredLoadOnStartup();
            }
            // 检查connector启动状态是否为失败,失败抛出异常
            checkThatConnectorsHaveStarted();
            this.started = true;
            TomcatWebServer.logger
                    .info("Tomcat started on port(s): " + getPortsDescription(true)
                            + " with context path '" + getContextPath() + "'");
        }
        catch (ConnectorStartFailedException ex) {
            // 异常停止tomcat
            stopSilently();
            throw ex;
        }
        catch (Exception ex) {
            throw new WebServerException("Unable to start embedded Tomcat server",
                    ex);
        }
        finally {
            Context context = findContext();
            // context解绑ClassLoader
            ContextBindings.unbindClassLoader(context, context.getNamingToken(),
                    getClass().getClassLoader());
        }
    }
}

TomcatWebServer:

private void addPreviouslyRemovedConnectors() {
    Service[] services = this.tomcat.getServer().findServices();
    for (Service service : services) {
        // 从上面移除connector添加的缓存中取出connector
        Connector[] connectors = this.serviceConnectors.get(service);
        if (connectors != null) {
            for (Connector connector : connectors) {
                // 添加到tomcat service中
                service.addConnector(connector);
                if (!this.autoStart) {
                    // 如果不是自动启动,则暂停connector
                    stopProtocolHandler(connector);
                }
            }
            // 添加完成后移除
            this.serviceConnectors.remove(service);
        }
    }
}

TomcatWebServer:

private void performDeferredLoadOnStartup() {
    try {
        for (Container child : this.tomcat.getHost().findChildren()) {
            if (child instanceof TomcatEmbeddedContext) {
                /* 延迟加载启动 */
                ((TomcatEmbeddedContext) child).deferredLoadOnStartup();
            }
        }
    }
    catch (Exception ex) {
        TomcatWebServer.logger.error("Cannot start connector: ", ex);
        throw new WebServerException("Unable to start embedded Tomcat connectors",
                ex);
    }
}

TomcatEmbeddedContext:

public void deferredLoadOnStartup() {
    // 一些较旧的Servlet框架(例如Struts,BIRT)使用Thread上下文类加载器在此阶段创建servlet实例
    // 如果他们这样做然后尝试初始化它们,那么类加载器可能已经改变了
    // 所以将调用包装在loadOnStartup中我们认为它将成为运行时的主要webapp类加载器
    ClassLoader classLoader = getLoader().getClassLoader();
    ClassLoader existingLoader = null;
    if (classLoader != null) {
        existingLoader = ClassUtils.overrideThreadContextClassLoader(classLoader);
    }
    if (this.overrideLoadOnStart) {
        // 早期版本的Tomcat使用了返回void的版本
        // 如果使用该版本,则不会调用我们重写的loadOnStart方法,并且原来的方法已经运行
        // 在Web应用程序部署描述符中加载并初始化标记为“load on startup”的所有servlet
        super.loadOnStartup(findChildren());
    }
    if (existingLoader != null) {
        ClassUtils.overrideThreadContextClassLoader(existingLoader);
    }
}

最后我们来看在应用上下文关闭时调用的扩展:
ServletWebServerApplicationContext:

protected void onClose() {
    super.onClose();
    /* 停止并释放WebServer */
    stopAndReleaseWebServer();
}

ServletWebServerApplicationContext:

private void stopAndReleaseWebServer() {
    WebServer webServer = this.webServer;
    if (webServer != null) {
        try {
            /* 停止WebServer */
            webServer.stop();
            this.webServer = null;
        }
        catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
    }
}

TomcatWebServer:

public void stop() throws WebServerException {
    synchronized (this.monitor) {
        boolean wasStarted = this.started;
        try {
            this.started = false;
            try {
                // 停止tomcat
                stopTomcat();
                // 销毁服务,调用后对象不能再次使用
                this.tomcat.destroy();
            }
            catch (LifecycleException ex) {
            }
        }
        catch (Exception ex) {
            throw new WebServerException("Unable to stop embedded Tomcat", ex);
        }
        finally {
            if (wasStarted) {
                containerCounter.decrementAndGet();
            }
        }
    }
}

到这里,整个Spring boot内嵌tomcat的流程就分析完了。

你可能感兴趣的:(Spring,Spring,boot,Spring源码解析)