深入Spring Boot源码(二):启动过程深度剖析

前言

在上一篇中,我们已经成功搭建了Spring Boot源码研究环境。

现在,让我们深入Spring Boot的核心------启动过程。

当你运行一个Spring Boot应用的main方法时,背后究竟发生了什么?

本文将带你从SpringApplication.run()开始,一步步揭开Spring Boot启动的神秘面纱。

在正式开始介绍之前,先用形象的方式类比SpringApplication这个类用途。

0、把SpringApplication想象成**"汽车制造工厂"**

复制代码
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

这就像在说:"我们要建造一个汽车制造工厂,这是我们的原料供应商 (resourceLoader)和核心设计图纸(primarySources)"


🔧 一步步拆解这个"汽车工厂"的建造过程:

1. 接收核心设计图纸

复制代码
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

通俗解释

工厂收到你给的核心设计图纸(就是你的@SpringBootApplication主类)。比如你传入了DemoApplication.class,工厂就知道:"哦,我要按照这个设计来造车!"

2. 判断要造什么类型的车

复制代码
this.properties.setWebApplicationType(WebApplicationType.deduceFromClasspath());

通俗解释

工厂自动检查你的"零件仓库"(类路径),判断你要造什么车:

  • 发现有Servlet零件 → 造传统Web汽车(SERVLET)
  • 发现有WebFlux零件 → 造新能源响应式汽车(REACTIVE)
  • 什么Web零件都没有 → 造普通家用轿车(NONE)

3. 招聘第一批基础工人

复制代码
this.bootstrapRegistryInitializers = new ArrayList<>(
        getSpringFactoriesInstances(BootstrapRegistryInitializer.class));

通俗解释

工厂先招聘地基施工队(BootstrapRegistryInitializer),这些工人在工厂刚建好时就进场,负责打地基、铺管线等最基础的工作。

4. 招聘车间主任和质检员

复制代码
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

通俗解释

接着招聘车间主任(ApplicationContextInitializer),他们负责在各个车间(ApplicationContext)正式开工前,检查设备、调整生产线布局。

5. 安装监控摄像头和警报器

复制代码
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

通俗解释

安装监控系统(ApplicationListener),这些"耳朵"会监听工厂里发生的各种事件:机器启动、零件到位、故障报警等。

6. 确定总设计师办公室

复制代码
this.mainApplicationClass = deduceMainApplicationClass();

通俗解释

自动找到总设计师的办公室(主应用类),这样工厂有任何问题都知道该找谁汇报。


🚗 完整造车流程比喻:

想象一下整个Spring Boot启动就像造一辆汽车

  1. 设计阶段(SpringApplication构造器)
    • 拿到设计图纸(primarySources
    • 决定造什么车(WebApplicationType
    • 招聘各种工人(各种InitializerListener
  1. 生产线准备(run()方法开始)
    • 地基工人进场(BootstrapRegistryInitializer
    • 车间主任检查设备(ApplicationContextInitializer
    • 启动监控系统(ApplicationListener
  1. 正式生产(refresh()方法)
    • 零件生产线运转(Bean创建)
    • 组装整车(Bean依赖注入)
    • 质量检测(Bean后置处理)
  1. 出厂上路(启动完成)
    • 汽车可以开了(应用可以接收请求)
    • 监控系统持续工作(事件监听)

🎯 给新手的核心理解要点:

为什么要有这么多"工人"?

  • 分工明确:每个工人只负责自己的专业领域
  • 可扩展性:你可以自己招聘新工人(自定义Initializer/Listener)
  • 生命周期管理:工人们按照固定顺序工作,不会乱套

这个构造器的作用总结:

复制代码
// 用一句话总结这个构造器:
"收集所有必要的工具和工人,为后续的汽车制造做好准备"

下一步会发生什么:

构造器只是准备工作 ,真正的"造车"是在run()方法中:

复制代码
// 准备工作完成,开始造车!
SpringApplication.run(DemoApplication.class, args);

💡 形象记忆技巧:

把SpringApplication想象成:

  • resourceLoader = 原料供应商
  • primarySources = 核心设计图纸
  • BootstrapRegistryInitializer = 地基施工队
  • ApplicationContextInitializer = 车间主任
  • ApplicationListener = 监控摄像头
  • mainApplicationClass = 总设计师办公室

这样下次看到这段代码,你就能立即在脑海中构建出这个"汽车工厂"的生动画面了!

理解了这个比喻,后续学习run()方法时就会轻松很多,因为那只是按部就班地执行这个"造车流程"而已。

1. SpringApplication初始化:启动的起点

1.1 入口点分析

让我们从最简单的Spring Boot应用开始:

复制代码
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

关键源码位置: spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

接下来用一个非常形象的比喻来理解SpringApplication的作用和执行流程。

1.2 构造器初始化过程

复制代码
// SpringApplication构造器核心逻辑
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    
    // 关键步骤1:推断Web应用类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
    // 关键步骤2:加载应用上下文初始化器
    setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class));
    
    // 关键步骤3:加载应用事件监听器
    setListeners(getSpringFactoriesInstances(ApplicationListener.class));
    
    // 关键步骤4:推断主应用类
    this.mainApplicationClass = deduceMainApplicationClass();
}

1.3 Web应用类型推断

复制代码
// WebApplicationType.deduceFromClasspath() 源码分析
static WebApplicationType deduceFromClasspath() {
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) 
        && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) 
        && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
        return WebApplicationType.REACTIVE;  // 响应式Web应用
    }
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;   // 非Web应用
        }
    }
    return WebApplicationType.SERVLET;        // Servlet Web应用
}

调试技巧:deduceFromClasspath()方法设置断点,观察类路径检测逻辑。

2. run方法执行流程:启动的核心

2.1 run方法整体架构

复制代码
public ConfigurableApplicationContext run(String... args) {
    // 1. 启动计时器
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    // 2. 创建引导上下文和应用上下文
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    
    // 3. 获取SpringApplicationRunListeners
    SpringApplicationRunListeners listeners = getRunListeners(args);
    
    // 4. 发布ApplicationStartingEvent事件
    listeners.starting();
    
    try {
        // 5. 准备环境
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        
        // 6. 打印Banner
        Banner printedBanner = printBanner(environment);
        
        // 7. 创建应用上下文
        context = createApplicationContext();
        
        // 8. 准备应用上下文
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        
        // 9. 刷新上下文(Spring核心流程)
        refreshContext(context);
        
        // 10. 后置处理
        afterRefresh(context, applicationArguments);
        
        // 11. 停止计时器
        stopWatch.stop();
        
        // 12. 发布启动完成事件
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        
        // 13. 执行Runner
        callRunners(context, applicationArguments);
        
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }
    
    listeners.running(context);
    return context;
}

2.2 环境准备阶段

复制代码
private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners, 
        ApplicationArguments applicationArguments) {
    
    // 创建和配置环境
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    
    // 发布EnvironmentPreparedEvent事件
    listeners.environmentPrepared(environment);
    
    // 绑定环境到SpringApplication
    bindToSpringApplication(environment);
    
    // 转换环境(如果需要)
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader())
            .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
    }
    
    // 配置PropertySources
    ConfigurationPropertySources.attach(environment);
    return environment;
}

2.3 应用上下文创建

复制代码
protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            // 根据Web应用类型选择不同的上下文实现
            switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_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);
}

3. 上下文刷新:Spring核心流程

3.1 refreshContext核心流程

复制代码
// 这是Spring框架的核心方法,Spring Boot进行了封装
private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException ex) {
            // 不在允许的环境中
        }
    }
}

// 实际调用Spring的refresh方法
protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

3.2 Spring Framework的refresh方法

复制代码
// 在AbstractApplicationContext中定义,包含12个关键步骤
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 准备刷新上下文
        prepareRefresh();
        
        // 2. 获取新的BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        
        // 3. 准备BeanFactory
        prepareBeanFactory(beanFactory);
        
        try {
            // 4. 后置处理BeanFactory
            postProcessBeanFactory(beanFactory);
            
            // 5. 调用BeanFactoryPostProcessors
            invokeBeanFactoryPostProcessors(beanFactory);
            
            // 6. 注册BeanPostProcessors
            registerBeanPostProcessors(beanFactory);
            
            // 7. 初始化MessageSource
            initMessageSource();
            
            // 8. 初始化事件广播器
            initApplicationEventMulticaster();
            
            // 9. 初始化特殊Bean(由子类实现)
            onRefresh();
            
            // 10. 注册监听器
            registerListeners();
            
            // 11. 完成BeanFactory初始化,实例化所有单例Bean
            finishBeanFactoryInitialization(beanFactory);
            
            // 12. 完成刷新过程,发布ContextRefreshedEvent事件
            finishRefresh();
        } catch (BeansException ex) {
            // 清理资源
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        } finally {
            resetCommonCaches();
        }
    }
}

3.3 Spring Boot的onRefresh扩展

复制代码
// 在SpringApplication中,onRefresh方法被重写以支持Web服务器启动
protected void onRefresh(ApplicationContext context) {
    // 调用父类方法
    super.onRefresh(context);
    
    // Spring Boot特有的:创建Web服务器
    if (context instanceof ConfigurableWebServerApplicationContext) {
        ((ConfigurableWebServerApplicationContext) context)
            .setServerNamespace(this.serverNamespace);
    }
}

4. 自动配置机制:Spring Boot的魔法核心

4.1 @SpringBootApplication注解解析

复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { 
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) 
})
public @interface SpringBootApplication {
    // 省略属性定义
}

4.2 @EnableAutoConfiguration核心机制

复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

4.3 AutoConfigurationImportSelector工作流程

复制代码
// 这是自动配置的核心类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        
        // 1. 获取自动配置条目
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
    
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        // 2. 检查自动配置是否启用
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        
        // 3. 获取注解属性
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        
        // 4. 获取候选配置
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        
        // 5. 移除重复配置
        configurations = removeDuplicates(configurations);
        
        // 6. 根据exclude属性排除配置
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        
        // 7. 应用过滤条件
        configurations = getConfigurationClassFilter().filter(configurations);
        
        // 8. 触发自动配置导入事件
        fireAutoConfigurationImportEvents(configurations, exclusions);
        
        return new AutoConfigurationEntry(configurations, exclusions);
    }
    
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 从META-INF/spring.factories加载自动配置类
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
            getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
        Assert.notEmpty(configurations, 
            "No auto configuration classes found in META-INF/spring.factories. " +
            "If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }
    
    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }
}

5. Web服务器启动:内嵌容器的奥秘

5.1 Servlet Web服务器启动

复制代码
// 在ServletWebServerApplicationContext中
@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        createWebServer();  // 创建Web服务器
    } 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) {
        // 从BeanFactory获取WebServerFactory
        ServletWebServerFactory factory = getWebServerFactory();
        
        // 创建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);
        }
    }
    
    initPropertySources();
}

5.2 Tomcat服务器创建过程

复制代码
// 在TomcatServletWebServerFactory中
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 1. 创建Tomcat实例
    Tomcat tomcat = new Tomcat();
    
    // 2. 配置基础目录
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    
    // 3. 创建Connector
    Connector connector = new Connector(this.protocol);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    
    // 4. 创建Engine和Host
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    
    // 5. 准备Context
    prepareContext(tomcat.getHost(), initializers);
    
    // 6. 返回自定义的TomcatWebServer
    return getTomcatWebServer(tomcat);
}

6. 启动扩展点:自定义启动行为

6.1 ApplicationRunner vs CommandLineRunner

复制代码
// ApplicationRunner示例
@Component
@Order(1)  // 指定执行顺序
public class DemoApplicationRunner implements ApplicationRunner {
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner执行,参数: " + Arrays.toString(args.getSourceArgs()));
        // 应用启动后需要执行的逻辑
    }
}

// CommandLineRunner示例  
@Component
@Order(2)
public class DemoCommandLineRunner implements CommandLineRunner {
    
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner执行,参数: " + Arrays.toString(args));
        // 应用启动后需要执行的逻辑
    }
}

6.2 应用事件监听机制

复制代码
// 监听应用启动事件
@Component
public class ApplicationStartupListener implements ApplicationListener<ApplicationStartedEvent> {
    
    private static final Logger logger = LoggerFactory.getLogger(ApplicationStartupListener.class);
    
    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        logger.info("应用启动完成,开始执行初始化任务...");
        
        // 执行自定义初始化逻辑
        performInitialization();
    }
    
    private void performInitialization() {
        // 初始化缓存、连接池等
    }
}

7. 调试与实验:验证启动过程

7.1 创建调试配置

在IDEA中创建调试配置,添加以下VM参数:

复制代码
-Ddebug=true -Dspring.output.ansi.enabled=ALWAYS

7.2 关键断点设置建议

设置断点观察启动流程:

  1. SpringApplication构造器
  2. SpringApplication.run()方法入口
  3. prepareEnvironment()方法
  4. createApplicationContext()方法
  5. refreshContext()方法
  6. onRefresh()方法(Web服务器启动)

7.3 启动日志分析

启用debug日志观察启动细节:

复制代码
# application.properties
logging.level.org.springframework.boot=DEBUG
logging.level.org.springframework.context=DEBUG

8. 启动过程总结与核心流程图

8.1 启动过程核心步骤

8.2 关键设计模式应用

  1. 模板方法模式AbstractApplicationContext.refresh()定义了算法骨架
  2. 观察者模式:Spring事件机制
  3. 工厂模式ApplicationContext创建
  4. 策略模式:不同的Web应用类型处理
  5. 责任链模式BeanPostProcessor调用链

结语

通过本文的深入分析,我们揭示了Spring Boot启动过程的完整流程。从SpringApplication初始化到Web服务器启动,每一个步骤都体现了Spring Boot"约定大于配置"的设计哲学。

关键收获:

  • Spring Boot启动是Spring Framework生命周期与Boot特有扩展的结合
  • 自动配置通过SpringFactoriesLoader机制实现
  • Web服务器启动在onRefresh阶段触发
  • 事件机制为启动过程提供了丰富的扩展点

下篇预告: 在下一篇文章中,我们将深入Spring Boot的自动配置机制,解析@Conditional注解体系,并学习如何编写自定义的Starter。


思考题:

  1. 如果在启动过程中需要读取配置文件中的自定义配置,应该在哪个阶段进行?
  2. 如何在不修改源码的情况下,在Bean创建前后插入自定义逻辑?
  3. Spring Boot如何支持多种内嵌Web服务器(Tomcat、Jetty、Undertow)的自动切换?

欢迎在评论区分享你的理解和遇到的问题!

相关推荐
听风吟丶2 小时前
Spring Boot 自动配置原理深度解析与实战
java·spring boot·后端
原来是好奇心2 小时前
深入Spring Boot源码(一):环境搭建与初探项目架构
java·gradle·源码·springboot
韩凡2 小时前
JAVA微服务与分布式(概念版)
java·分布式·微服务
bing.shao2 小时前
Golang 之闭包
java·算法·golang
济南壹软网络科技有限公司2 小时前
下一代盲盒系统核心架构解析:JAVA-S1如何打造极致公平与全球化体验
java·开源·盲盒源码·盲盒h5·国际盲盒源码
qq_336313932 小时前
HashMap
java·开发语言
就叫飞六吧2 小时前
Spring 框架中的 Bean 继承:`parent` 属性 (XML配置)
xml·java·spring
故渊ZY2 小时前
SpringBean核心机制与实战应用详解
java·spring
专注VB编程开发20年2 小时前
C# int*指向 int 的指针类型(unsafe 上下文)
java·开发语言·c#