图解SpringBoot启动流程:从main方法到Tomcat启动的完整生命周期

🎓博主介绍:Java、Python、js全栈开发 “多面手”,精通多种编程语言和技术,痴迷于人工智能领域。秉持着对技术的热爱与执着,持续探索创新,愿在此分享交流和学习,与大家共进步。
📖DeepSeek-行业融合之万象视界(附实战案例详解100+)
📖全栈开发环境搭建运行攻略:多语言一站式指南(环境搭建+运行+调试+发布+保姆级详解)
👉感兴趣的可以先收藏起来,希望帮助更多的人
在这里插入图片描述

图解 Spring Boot 启动流程:从 main 方法到 Tomcat 启动的完整生命周期

一、引言

Spring Boot 作为 Spring 生态系统中简化开发的利器,极大地提高了开发效率。它通过自动配置和嵌入式服务器,让开发者能够快速搭建和部署应用程序。深入理解 Spring Boot 的启动流程,不仅有助于我们排查问题,还能让我们更好地定制和优化应用。本文将详细介绍 Spring Boot 从 main 方法开始到 Tomcat 启动的完整生命周期,并通过图解和代码示例进行深入剖析。

二、Spring Boot 启动的入口:main 方法

2.1 典型的 Spring Boot 应用 main 方法

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MySpringBootApp {
    public static void main(String[] args) {
        SpringApplication.run(MySpringBootApp.class, args);
    }
}

在上述代码中,@SpringBootApplication 是一个组合注解,它包含了 @Configuration@EnableAutoConfiguration@ComponentScan 等注解,用于启用自动配置、组件扫描等功能。SpringApplication.run 方法是 Spring Boot 启动的核心入口。

2.2 SpringApplication.run 方法的执行过程

SpringApplication.run 方法有多个重载形式,最终会调用以下核心方法:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class<?>[] { primarySource }, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

这里,首先创建了一个 SpringApplication 对象,然后调用其 run 方法。

三、SpringApplication 对象的初始化

3.1 构造函数的作用

SpringApplication 的构造函数会完成一系列的初始化工作,包括:

  • 推断应用类型(如 Web 应用、非 Web 应用等)
  • 查找并加载初始化器(ApplicationContextInitializer
  • 查找并加载监听器(ApplicationListener
  • 推断主应用类

以下是构造函数的部分代码:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

3.2 推断应用类型

WebApplicationType.deduceFromClasspath() 方法会根据类路径中的类来推断应用类型,常见的应用类型有:

  • SERVLET:传统的基于 Servlet 的 Web 应用
  • REACTIVE:响应式 Web 应用
  • NONE:非 Web 应用

四、SpringApplication.run 方法的详细执行流程

4.1 启动计时

run 方法开始时,会记录启动时间:

StopWatch stopWatch = new StopWatch();
stopWatch.start();

4.2 配置应用环境

创建并配置 ConfigurableEnvironment 对象,加载配置文件(如 application.propertiesapplication.yml):

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

4.3 创建并刷新应用上下文

根据推断的应用类型创建相应的 ApplicationContext,并调用 refresh 方法进行刷新:

context = createApplicationContext();
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);

4.4 启动完成

停止计时并发布 ApplicationReadyEvent 事件:

stopWatch.stop();
if (this.logStartupInfo) {
    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.contextLoaded(context);
callRunners(context, applicationArguments);

五、应用上下文的刷新过程

5.1 准备刷新

设置上下文的启动日期、激活上下文、初始化属性源等:

@Override
protected void prepareRefresh() {
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);

    if (logger.isDebugEnabled()) {
        if (logger.isTraceEnabled()) {
            logger.trace("Refreshing " + this);
        }
        else {
            logger.debug("Refreshing " + getDisplayName());
        }
    }

    // Initialize any placeholder property sources in the context environment
    initPropertySources();

    // Validate that all properties marked as required are resolvable
    // see ConfigurablePropertyResolver#setRequiredProperties
    getEnvironment().validateRequiredProperties();

    // Store pre-refresh ApplicationListeners...
    if (this.earlyApplicationListeners == null) {
        this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    }
    else {
        // Reset local application listeners to pre-refresh state.
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }

    // Allow for the collection of early ApplicationEvents,
    // to be published once the multicaster is available...
    this.earlyApplicationEvents = new LinkedHashSet<>();
}

5.2 创建并刷新 BeanFactory

创建 DefaultListableBeanFactory 并加载 Bean 定义:

@Override
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}

@Override
protected void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        loadBeanDefinitions(beanFactory);
        this.beanFactory = beanFactory;
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

5.3 初始化 Bean

finishBeanFactoryInitialization 方法中,会初始化所有剩余的单例 Bean:

@Override
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // Initialize conversion service for this context.
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
            beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
        beanFactory.setConversionService(
                beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    }

    // Register a default embedded value resolver if no bean post-processor
    // (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
    // at this point, primarily for resolution in annotation attribute values.
    if (!beanFactory.hasEmbeddedValueResolver()) {
        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    }

    // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) {
        getBean(weaverAwareName);
    }

    // Stop using the temporary ClassLoader for type matching.
    beanFactory.setTempClassLoader(null);

    // Allow for caching all bean definition metadata, not expecting further changes.
    beanFactory.freezeConfiguration();

    // Instantiate all remaining (non-lazy-init) singletons.
    beanFactory.preInstantiateSingletons();
}

六、Tomcat 服务器的启动

6.1 嵌入式 Tomcat 的自动配置

Spring Boot 通过自动配置机制,根据类路径中的依赖自动配置嵌入式 Tomcat 服务器。在 ServletWebServerFactoryAutoConfiguration 类中,会自动配置 TomcatServletWebServerFactory

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

    @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;
    }
}

6.2 Tomcat 服务器的启动过程

在应用上下文刷新完成后,ServletWebServerApplicationContext 会调用 TomcatServletWebServerFactory 创建并启动 Tomcat 服务器:

@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}

七、总结

通过以上分析,我们详细了解了 Spring Boot 从 main 方法开始到 Tomcat 启动的完整生命周期。整个过程涉及到应用类型推断、环境配置、应用上下文刷新、Bean 初始化以及嵌入式服务器启动等多个关键步骤。深入理解这些步骤,有助于我们更好地开发和优化 Spring Boot 应用程序。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fanxbl957

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值