当前位置: 首页
AI教程
从入门到放弃SpringBoot启动源码分析

从入门到放弃SpringBoot启动源码分析

热心网友 时间:2026-06-06
转载

前言 上一篇文章我们简单聊了MySQL的数据库连接过程,写完之后自己读了一遍,感觉分析得还不够透。很多细节其实并没有真正捋清楚,所以这次干脆从Springboot的源码入手,重新从头走一遍。 main 入口分析 package com springboot demo; import org myba

前言

上一篇文章我们简单聊了MySQL的数据库连接过程,写完之后自己读了一遍,感觉分析得还不够透。很多细节其实并没有真正捋清楚,所以这次干脆从Springboot的源码入手,重新从头走一遍。

main 入口分析

package com.springboot.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;

@MapperScan("com.springboot.demo.repository.dao")
@SpringBootApplication
@EnableScheduling
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

一个最简单的Springboot项目,启动类里就一行代码。别小看这一行,背后藏着一整套路。我们就从这行开始拆。

SpringApplication::SpringApplication

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();
}

构造方法的主要工作就是初始化一堆成员变量:参数列表、应用类型、监听器等等。注意这里的 WebApplicationType.deduceFromClasspath(),它会根据classpath下有没有某些类来判断是web环境还是非web环境。

getSpringFactoriesInstances

private  Collection getSpringFactoriesInstances(Class type,
        Class[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

这个方法说白了就是根据传入的type,从 META-INF/spring.factories 里捞出一堆配置类名,然后一一实例化。过程中还做了去重和排序,挺严谨。

loadFactoryNames 负责读文件,createSpringFactoriesInstances 负责校验类型(必须是parameterTypes的子类)并通过反射创建实例。

\

SpringApplication::run

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Ja va main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }
    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

整个启动流程都在这个 run 方法里了。下面我们拆开看每一步。

StopWatch

一个计时器,先记下启动的时间点,最后用来算耗时。

变量初始化

各种临时变量的准备工作,后面会一步步用上。

configureHeadlessProperty

设置系统属性 ja va.awt.headless 为 true。说白了就是告诉操作系统:这个服务端程序不需要显示器、鼠标、键盘这些外设,如果代码里用到了图形资源,自己模拟就好了。

getRunListeners

spring.factories 中加载并实例化所有 SpringApplicationRunListener,这些监听器负责在启动的不同阶段广播事件。

prepareEnvironment

配置环境:加载配置文件、系统变量、命令行参数等,然后通过监听器广播环境准备完毕的事件。

printBanner

打印启动时那个标志性的横幅。

createApplicationContext

根据 webApplicationType 创建对应的 Spring 上下文实例。

exceptionReporters

初始化异常报告器,一旦启动过程中间出异常,这些报告器会负责输出友好信息。

prepareContext

private void prepareContext(ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    applyInitializers(context);
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    // Load the sources
    Set sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    listeners.contextLoaded(context);
}

这一步做了几件事:

  • setEnvironment:把前面准备好的环境塞进上下文。
  • postProcessApplicationContext:默认空实现,留给子类扩展。
  • applyInitializers:调用之前实例化的 ApplicationContextInitializerinitialize 方法。
  • listeners.contextPrepared:通知监听器上下文准备完毕。
  • 注册两个单例 bean:springApplicationArgumentsspringBootBanner
  • load:通过 BeanDefinitionLoader 加载各种来源的 bean(注解、XML、package 等),最终全部注册到上下文中。
  • listeners.contextLoaded:通知监听器上下文加载完毕。

refreshContext

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();
        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);
        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);
            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            // Initialize message source for this context.
            initMessageSource();
            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();
            // Initialize other special beans in specific context subclasses.
            onRefresh();
            // Check for listener beans and register them.
            registerListeners();
            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);
            // Last step: publish corresponding event.
            finishRefresh();
        }
        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }
            // Destroy already created singletons to a void dangling resources.
            destroyBeans();
            // Reset 'active' flag.
            cancelRefresh(ex);
            // Propagate exception to caller.
            throw ex;
        }
        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

refreshContext 是整个容器初始化的核心,调用了 AbstractApplicationContext.refresh()。下面拆解关键步骤:

  • prepareRefresh:设置启动时间、初始化占位符、校验必备属性等。
  • obtainFreshBeanFactory:通知子类刷新内部的 BeanFactory。
  • prepareBeanFactory:为 BeanFactory 做准备工作,比如设置 ClassLoader、注册一些特殊的 bean 等。
  • invokeBeanFactoryPostProcessors:重点来了——它会调用 ConfigurationClassPostProcessor.processConfigBeanDefinitions,这个方法是处理 @Configuration、@Component 等注解的入口。

咱们重点看看 processConfigBeanDefinitions 干了什么:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }
    // Return immediately if no @Configuration classes were found
    if (configCandidates.isEmpty()) {
        return;
    }
    // ... 排序、设置BeanNameGenerator等
    // Parse each @Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(...);
    Set candidates = new LinkedHashSet<>(configCandidates);
    Set alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        parser.parse(candidates);
        parser.validate();
        Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);
        // Read the model and create bean definitions based on its content
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(...);
        }
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
        candidates.clear();
        // ... 循环处理新发现的配置类
    } while (!candidates.isEmpty());
    // ... 注册 ImportRegistry
}

简单说,ConfigurationClassParser 解析 @Configuration、@ComponentScan、@Import 等注解,扫描出所有需要注册的类;ConfigurationClassBeanDefinitionReader 则负责把这些类转成 BeanDefinition 并注册到容器里。

后面还有 invokeBeanDefinitionRegistryPostProcessorsinvokeBeanFactoryPostProcessors 等回调,这里就不展开了。

  • registerBeanPostProcessors:注册用于拦截 bean 创建的处理器(比如 AOP 的切面)。
  • initMessageSource:初始化国际化消息源。
  • initApplicationEventMulticaster:初始化事件广播器。
  • onRefresh:模板方法,对于 web 环境(如 ServletWebServerApplicationContext)会在这里创建内嵌 Web 服务器。
  • registerListeners:把容器中实现了 ApplicationListener 的 bean 注册为监听器。
  • finishBeanFactoryInitialization:实例化所有非懒加载的单例 bean。像我们之前分析过的 DataSource,就是在这个阶段被创建的。

finishRefresh

结束上下文的刷新,发布 ContextRefreshedEvent 事件。

callRunners

如果容器中定义了 ApplicationRunnerCommandLineRunner 类型的 bean,就会调用它们的 run 方法,让应用启动后执行一些自定义逻辑。

总结

到这里,Springboot 的启动过程就串起来了。整体脉络可以概括为:

  • 初始化环境
  • 加载默认配置
  • 初始化各类监听器和事件
  • 创建上下文
  • 往上下文中添加默认的 bean
  • 扫描各类注解(@Component、@Bean、@Import 等),注册所有 bean 定义
  • 实例化所有单例 bean
  • 启动完毕,执行 Runner

整个过程虽然代码量大,但每一步都职责分明。理解了这个流程,以后遇到启动相关的问题,心里就有底了。

来源:https://developer.aliyun.com/article/704370

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
跨境物流AI Agent感知决策全链路自动化技术拆解

跨境物流AI Agent感知决策全链路自动化技术拆解

2026年,跨境物流领域正迎来一场根本性变革——从“被动记录”全面迈向“主动执行”,AI智能体正在重塑整个行业格局。 你可能已经深有体会:每天一到岗,光是后台处理货件创建、物流追踪等重复操作,就要耗费数小时之久。行业调研数据显示,超过60%的运营人员每天至少花费2小时在这些机械性劳动上,而手动操作引

时间:2026-07-11 16:50
天学会AI应用开发第十二课:从PDF、Word和网页构建RAG

天学会AI应用开发第十二课:从PDF、Word和网页构建RAG

上一篇文章介绍了如何从TXT文件中读取文本内容,但在日常办公场景中,纯文本文件的使用频率并不高。更常见的需求是处理PDF、Word等文档格式,同时还需要从网页中提取知识信息。 因此,本篇文章将重点讲解如何从PDF、Word以及网页中提取内容,并将其应用于RAG(检索增强生成)系统。 一、从PDF文件

时间:2026-07-11 16:50
Hy3+WorkBuddy组合国产顶级Agent附完整提示词

Hy3+WorkBuddy组合国产顶级Agent附完整提示词

五个 Case 跑完,总结一下整体体验。工具使用能力:能不能自己开网页、找信息、标出处。规划能力:能不能把多约束需求拆成可执行步骤。长程执行能力:跨多步任务时会不会丢状态。复杂推理能力:能不能先推导、再写代码、再执行验证。WorkBuddy+Hy3 的表现完全符合预期。趁着 WorkBuddy 里的

时间:2026-07-11 16:50
AI Agent是什么?一文理解大语言模型、记忆、技能、工具、MCP、工作流与上下文

AI Agent是什么?一文理解大语言模型、记忆、技能、工具、MCP、工作流与上下文

智能体并非单一模型,而是由大语言模型、记忆、工具、工作流等模块协同构成的自主系统。它通过理解目标、检索记忆、调用工具、构建上下文、推理决策,并基于反馈闭环持续迭代,最终自主完成复杂任务。

时间:2026-07-11 16:50
DeepSeek决定自研芯片打造人工智能全新算力芯脏

DeepSeek决定自研芯片打造人工智能全新算力芯脏

被“逼”出来的第三条路。 一直以模型技术见长的DeepSeek,这次在算力供应链上迈出了让所有人侧目的一步。 2026年7月7日,路透社援引三位知情人士消息,DeepSeek正在悄然开发自有AI芯片。值得玩味的是,这颗芯片的定位非常精准——专攻推理,不涉足训练。消息人士称,项目大约启动于一年前,目前

时间:2026-07-11 16:28
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 热门数据榜