Java OpenResty Spring Spring Boot MySQL Redis MongoDB PostgreSQL Linux Android Nginx 面试 小程序 Arthas JVM AQS juc Kubernetes Docker 诊断工具


Spring Boot Tomcat 启动流程

Tomcat Spring Boot 面试 大约 8561 字

版本

Spring Boot 2.6.0

启动流程

加载自动配置

spring-boot-autoconfigure-2.6.0.jarMETA-INF/spring.factories中配置的Auto Configure的值。

@Configuration(proxyBeanMethods = false)
@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 {

}

ServletWebServerFactoryAutoConfiguration注解@Import导入的配置类

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedTomcat {
        @Bean
        TomcatServletWebServerFactory tomcatServletWebServerFactory(//...) {
        }
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedJetty {

        @Bean
        JettyServletWebServerFactory JettyServletWebServerFactory(//...) {
        }
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedUndertow {
        @Bean
        UndertowServletWebServerFactory undertowServletWebServerFactory(//...) {
        }
    }


}

IOC 容器 onRefresh

onRefresh中重写了父类AbstractApplicationContext的空实现方法onRefresh

public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            // 创建 Web 容器
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }

    private void createWebServer() {
        WebServer webServer = this.webServer;
        // 刚开始 webServer 和 servletContext 都为 null
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {
            // 获取
            ServletWebServerFactory factory = getWebServerFactory();
            this.webServer = factory.getWebServer(getSelfInitializer());
            getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
            getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
        }
        else if (servletContext != null) {
            try {
                getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context", ex);
            }
        }
        initPropertySources();
    }

    protected ServletWebServerFactory getWebServerFactory() {
        // 根据 ServletWebServerFactory 类型获取
        // 在文章前面读取到的 ServletWebServerFactoryConfiguration 配置类
        // 如果是 Tomcat 就是 TomcatServletWebServerFactory
        String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);

        // 如果有多个 Web 容器会抛出异常
        if (beanNames.length > 1) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
        }
        return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
    }
}

TomcatServletWebServerFactorygetWebServer方法中创建Tomcat容器

public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
    @Override
    public WebServer getWebServer(ServletContextInitializer... initializers) {
        // 创建 Tomcat 容器
        Tomcat tomcat = new Tomcat();
        File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        // 新建连接器
        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        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);
        }
        prepareContext(tomcat.getHost(), initializers);
        // 调用自身方法,传入 tomcat 对象
        return getTomcatWebServer(tomcat);
    }

    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
        // 创建 TomcatWebServer 对象
        return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
    }
}

TomcatWebServer构造中启动Tomcat服务

public class TomcatWebServer implements WebServer {

    private final Object monitor = new Object();

    // 构造函数
    public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
        // 调用初始化方法
        initialize();
    }

    private void initialize() throws WebServerException {
        synchronized (this.monitor) {
            // 启动 Tomcat 服务
            this.tomcat.start();

            // 所有 Tomcat 现场都是守护线程(意味着 main 方法执行结束,线程就关闭了)
            // 并且创建一个新的非守护线程运行,等待 tomcat 的 StandSever 结束
            startDaemonAwaitThread();
        }
    }

    private void startDaemonAwaitThread() {
        Thread awaitThread = new Thread("container-" + (containerCounter.get())) {

            @Override
            public void run() {
                // 等待应用结束,线程退出
                TomcatWebServer.this.tomcat.getServer().await();
            }

        };
        awaitThread.setContextClassLoader(getClass().getClassLoader());
        awaitThread.setDaemon(false);
        awaitThread.start();
    }

}

完整调用链

initialize:123, TomcatWebServer (org.springframework.boot.web.embedded.tomcat)
<init>:104, TomcatWebServer (org.springframework.boot.web.embedded.tomcat)
getTomcatWebServer:473, TomcatServletWebServerFactory (org.springframework.boot.web.embedded.tomcat)
getWebServer:206, TomcatServletWebServerFactory (org.springframework.boot.web.embedded.tomcat)
createWebServer:182, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
onRefresh:160, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:577, AbstractApplicationContext (org.springframework.context.support)
refresh:145, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:730, SpringApplication (org.springframework.boot)
refreshContext:412, SpringApplication (org.springframework.boot)
run:302, SpringApplication (org.springframework.boot)
run:1301, SpringApplication (org.springframework.boot)
run:1290, SpringApplication (org.springframework.boot)
main:25, DemoApplication (com.example.demo)

备注

DispatcherServlet是在finishBeanFactoryInitialization实例化所有的单例bean时加载的,但默认懒加载,没有调用initStrategies完成映射扫描等。

所以:Spring Boot首次请求比较慢。

public class DispatcherServlet extends FrameworkServlet {

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
}
阅读 1565 · 发布于 2022-01-01

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批