Spring boot内嵌tomcat源码解析

 2019-11-02 21:19  阅读(918)
文章分类:Spring boot

阅读须知

  • 版本: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的流程就分析完了。


来源:http://ddrv.cn

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring boot内嵌tomcat源码解析

相关推荐