当前位置: 首页 > article >正文

SpringBoot启动源码剖析:从入口到容器的诞生

文章目录

    • SpringBoot启动的核心入口
    • SpringApplication的初始化
    • SpringBoot的启动流程
      • 1. 准备环境(Environment)
      • 2. 创建应用上下文(ApplicationContext)
      • 3. 刷新应用上下文(Refresh Context)
      • 4. 调用Runner接口
    • SpringBoot启动的核心机制
      • 1. 自动配置(Auto-Configuration)
      • 2. 条件注解(Conditional Annotations)
      • 3. SpringFactoriesLoader
    • 总结

SpringBoot作为Java开发中最流行的框架之一,以其“约定优于配置”的理念深受开发者喜爱。但你是否好奇过,SpringBoot是如何启动的?它的内部机制是什么?今天,我们就从源码的角度,深入剖析SpringBoot的启动过程。


SpringBoot启动的核心入口

SpringBoot的启动入口是SpringApplication.run()方法。无论是通过main方法启动,还是通过其他方式,最终都会调用这个方法。下面是一个典型的SpringBoot启动代码:

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

接下来,我们将从SpringApplication.run()方法开始,逐步分析SpringBoot的启动流程。


SpringApplication的初始化

SpringApplication.run()方法的核心逻辑在SpringApplication类中。我们先来看一下SpringApplication的初始化过程:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList<>(
            getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

这段代码主要做了以下几件事:

  1. 设置主配置类(primarySources)。
  2. 推断应用类型(webApplicationType),比如是Servlet应用还是Reactive应用。
  3. 加载BootstrapRegistryInitializerApplicationContextInitializerApplicationListener等组件。

SpringBoot的启动流程

1. 准备环境(Environment)

SpringBoot在启动时,首先会准备运行环境。这一步的核心代码如下:

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

prepareEnvironment方法会加载配置文件(如application.properties),并将配置信息封装到Environment对象中。

2. 创建应用上下文(ApplicationContext)

接下来,SpringBoot会根据应用类型创建对应的ApplicationContext。例如,对于Web应用,会创建AnnotationConfigServletWebServerApplicationContext

context = createApplicationContext();

3. 刷新应用上下文(Refresh Context)

这是SpringBoot启动过程中最核心的一步。refreshContext方法会触发Spring容器的初始化,包括Bean的加载、依赖注入、AOP代理等。

refreshContext(context);

refresh方法的源码如下:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        try {
            postProcessBeanFactory(beanFactory);
            invokeBeanFactoryPostProcessors(beanFactory);
            registerBeanPostProcessors(beanFactory);
            initMessageSource();
            initApplicationEventMulticaster();
            onRefresh();
            registerListeners();
            finishBeanFactoryInitialization(beanFactory);
            finishRefresh();
        } catch (BeansException ex) {
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        }
    }
}

4. 调用Runner接口

在容器初始化完成后,SpringBoot会调用CommandLineRunnerApplicationRunner接口的实现类。这些接口通常用于执行一些启动后的任务。

callRunners(context, applicationArguments);

SpringBoot启动的核心机制

1. 自动配置(Auto-Configuration)

SpringBoot的自动配置是通过@EnableAutoConfiguration注解实现的。它会根据类路径下的依赖,自动加载相应的配置类。例如,如果引入了spring-boot-starter-web,SpringBoot会自动配置Tomcat和Spring MVC。

2. 条件注解(Conditional Annotations)

SpringBoot大量使用了条件注解(如@ConditionalOnClass@ConditionalOnMissingBean),这些注解决定了某个配置类是否生效。例如:

@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })
public class TomcatAutoConfiguration {
    // 自动配置Tomcat
}

3. SpringFactoriesLoader

SpringBoot通过SpringFactoriesLoader加载META-INF/spring.factories文件中的配置类。这是SpringBoot扩展机制的核心。


总结

通过以上分析,我们可以看到,SpringBoot的启动过程是一个复杂但有序的过程。从环境准备到容器刷新,再到自动配置和Runner调用,每一步都体现了SpringBoot设计的精妙之处。

如果你对SpringBoot的源码感兴趣,可以尝试下载SpringBoot的源码,结合本文的分析,深入探索其中的奥秘。相信你会对SpringBoot有更深刻的理解!


关于作者
我是双非二本从创业公司一路杀到中厂的程序员,喜欢研究技术底层原理。如果你也对技术感兴趣,欢迎关注我的CSDN博客,我们一起交流学习!


http://www.kler.cn/a/536407.html

相关文章:

  • 安卓/ios脚本开发按键精灵经验小分享
  • feign 远程调用详解
  • 13.PPT:诺贝尔奖【28】
  • 链式结构二叉树(递归暴力美学)
  • Mac上搭建k8s环境——Minikube
  • 【VM】VirtualBox安装ubuntu22.04虚拟机
  • 瞎想:控制论、信息论与系统论:未来汽车产品的“三论融合”与深度思考
  • Python3中异常处理:try-finally语句
  • Oracle CDB自动处理表空间不足脚本
  • Hackmyvm Atom
  • 【吾爱出品】 [Windows] 多线程下载器,Ghost Downloader v3.5.1
  • UnityShader学习笔记——动态效果
  • ubuntu20.04离线安装docker和docker-compose
  • 基于CLIP视觉语言大模型的行人重识别方法的简单框架设计
  • 【AI大模型】deepseek 相关资料和使用 【媲美 GPT-o1?】
  • 02.07 TCP服务器与客户端的搭建
  • 建筑兔零基础自学python记录13|实战人脸识别项目——灰度转换02
  • C/C++ 面试智能指针
  • C++ 中的环形线性动态规划
  • 攻防世界baigeiRSA
  • 【补充】RustDesk一键部署及账号登录配置
  • 深入理解Python上下文管理器:从基础到高级应用
  • java版本
  • 8.stack和queue
  • Linux交叉编译gpsd移植至arm板
  • CI/CD相关概念