Spring Boot Tomcat 启动流程

Tomcat Spring Boot 面试 About 8,561 words

版本

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);
    }
}
Views: 2,645 · Posted: 2022-01-01

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb/LiteNote

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

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


Today On History
Browsing Refresh