Spring Boot 启动过程源码解析,看这篇就够了!


在本篇文章中,我们将深入探索 Spring Boot 的核心源码,了解它是如何实现自动配置的。通过源码分析,你将对 Spring Boot 有更深入的理解,同时也为后续学习 Spring Cloud 打下坚实基础。


1. SpringApplication.run 方法解析
1.1 为什么需要分析源码
为什么要读源码?
很多同学可能会问:”我会用 Spring Boot 不就行了,为什么要去看源码?”
其实,看源码有以下几个好处:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
1.2 SpringApplication.run 源码分析
SpringApplication.run 是 Spring Boot 应用的入口方法,让我们一步步分析它做了什么。
// 这里是 SpringApplication 类的 run 方法// 第一个参数是主应用类(包含 main 方法的类)// 第二个参数是命令行参数publicstatic ConfigurableApplicationContext run(Class<?> primarySource, String... args){// 这里调用了重载的 run 方法return run(new Class<?>[] { primarySource }, args);}
接下来看核心的 run 方法实现:
publicstatic ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args){// 1. 创建 SpringApplication 实例// 2. 执行 run 方法returnnew SpringApplication(primarySources).run(args);}
然后是真正的 run 方法执行流程:
public ConfigurableApplicationContext run(String... args){// 1. 创建并启动计时器,用于记录启动时间 StopWatch stopWatch = new StopWatch(); stopWatch.start();// 2. 初始化应用上下文和Environment ConfigurableApplicationContext context = null;// 3. 配置 Spring Boot 的监听器集合 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();// 4. 设置 Headless 模式(在没有显示器的情况下也能运行) configureHeadlessProperty();// 5. 获取并启动运行监听器 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting();try {// 6. 准备环境(ApplicationArguments) ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 7. 准备环境变量(PropertySources、Profile等) ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// 8. 处理 Bean 的资源加载 configureIgnoreBeanInfo(environment);// 9. 打印 Banner(启动时的 Logo) Banner printedBanner = printBanner(environment);// 10. 创建应用上下文 context = createApplicationContext();// 11. 准备异常报告器 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,newClass<?>[] { ConfigurableApplicationContext.class }, context);// 12. 准备应用上下文(核心步骤!) prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 13. 刷新应用上下文(核心步骤!加载 Bean) refreshContext(context);// 14. 执行刷新后的操作 afterRefresh(context, applicationArguments);// 15. 停止计时器 stopWatch.stop();// 16. 输出启动日志if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); }// 17. 发布应用启动事件 listeners.started(context);// 18. 执行 ApplicationRunner 和 CommandLineRunner callRunners(context, applicationArguments); }catch (Throwable ex) {// 如果启动失败,报告异常 handleRunFailure(context, ex, exceptionReporters, listeners);thrownew IllegalStateException(ex); }try {// 19. 发布应用就绪事件 listeners.running(context); }catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null);thrownew IllegalStateException(ex); }// 20. 返回应用上下文return context;}
流程图解:
┌─────────────────────────────────────────────────────────┐│ Spring Boot 启动流程 │├─────────────────────────────────────────────────────────┤│ 1. StopWatch 计时器启动 ││ 2. 配置 Headless 模式 ││ 3. 获取并启动监听器 ││ 4. 准备 Environment(环境变量) ││ 5. 打印 Banner ││ 6. 创建 ApplicationContext(应用上下文) ││ 7. prepareContext(准备上下文) ← 重要 ││ 8. refreshContext(刷新上下文) ← 重要 ││ 9. 执行 Runner(ApplicationRunner/CommandLineRunner) ││ 10. 发布启动完成事件 │└─────────────────────────────────────────────────────────┘
2. 自动配置原理分析
2.1 什么是自动配置
为什么需要自动配置?
在没有 Spring Boot 之前,我们需要编写大量的 XML 配置或者 Java 配置。比如要整合 MyBatis,我们需要:
// 传统 Spring 配置@ConfigurationpublicclassMyBatisConfig{// 配置数据源@Beanpublic DataSource dataSource(){returnnew DruidDataSource(); }// 配置 SqlSessionFactory@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource){ SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource);// 还需要配置 mapper 路径、类型别名等return factoryBean.getObject(); }// 配置 MapperScannerConfigurer@Beanpublic MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer configurer = new MapperScannerConfigurer(); configurer.setBasePackage("com.example.mapper");return configurer; }}
而有了 Spring Boot,我们只需要引入依赖:
<!-- 只需要引入依赖,其他都自动配置好了 --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version></dependency>
这就是自动配置的魔力!
2.2 @SpringBootApplication 注解解析
@SpringBootApplication 是 Spring Boot 应用的入口注解,让我们看看它的定义:
@Target(ElementType.TYPE) // 作用在类上@Retention(RetentionPolicy.RUNTIME) // 运行时保留@Documented// 它是一个组合注解,由三个注解组成@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type= FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interfaceSpringBootApplication{// ... 省略部分属性}
拆解来看:
|
|
|
|---|---|
| @SpringBootConfiguration |
|
| @EnableAutoConfiguration |
|
| @ComponentScan |
|
2.3 @EnableAutoConfiguration 原理
@EnableAutoConfiguration 是自动配置的核心注解:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AutoConfigurationImportSelector.class) // 关键!导入自动配置选择器public @interfaceEnableAutoConfiguration{// 配置排除的自动配置类 Class<?>[] exclude() default {};// 根据名称排除 String[] excludeName() default {};// 配置名称String name()default "";}
AutoConfigurationImportSelector 实现了 ImportSelector 接口:
publicclassAutoConfigurationImportSelectorimplementsDeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered{// 这个方法返回需要导入的配置类@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {// 如果没有启用自动配置,返回空数组if (!isEnabled(annotationMetadata)) {return NO_IMPORTS; }// 获取自动配置候选类 AutoConfigurationEntry entry = getAutoConfigurationEntry(annotationMetadata);// 返回需要导入的类名数组return entry.getConfigurations(); }// 获取自动配置条目protected AutoConfigurationEntry getAutoConfigurationEntry( AnnotationMetadata annotationMetadata){// 1. 判断是否启用自动配置if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY; }// 2. 获取属性配置 AnnotationAttributes attributes = getAttributes(annotationMetadata);// 3. 获取所有候选配置类(核心方法!) List<String> configurations = getCandidateConfigurations( annotationMetadata, attributes);// 4. 去除重复的配置类 configurations = removeDuplicates(configurations);// 5. 处理排除的配置 Set<String> exclusions = getExclusions(annotationMetadata, attributes); configurations.removeAll(exclusions);// 6. 过滤掉不满足条件 的配置类 configurations = filter(configurations, attributes);// 7. 触发自动配置导入事件 fireAutoConfigurationImportEvents(configurations, attributes);// 8. 排序并返回returnnew AutoConfigurationEntry(configurations, exclusions); }// 获取候选配置类protected List<String> getCandidateConfigurations( AnnotationMetadata metadata, AnnotationAttributes attributes){// 使用 SpringFactoriesLoader 加载配置// 这个方法会从 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; }}
2.4 spring.factories 文件解析
Spring Boot 是如何知道要加载哪些自动配置类的?
答案是通过META-INF/spring.factories 文件!以 Spring Boot 内置的 mybatis-spring-boot-starter 为例:
# 文件位置:mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories# org.springframework.boot.autoconfigure.EnableAutoConfiguration 是自动配置的keyorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.boot.autoconfigure.MybatisAutoConfiguration,\org.mybatis.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration
Spring Boot 官方也定义了大量自动配置:
# Spring Boot 官方自动配置(部分)org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\...
3. 自动配置条件注解
3.1 条件注解的作用
为什么需要条件注解?
自动配置虽然方便,但不是所有场景都需要。比如:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
Spring Boot 通过条件注解来解决这个问题:
3.2 常用条件注解
|
|
|
|---|---|
| @ConditionalOnClass |
|
| @ConditionalOnMissingClass |
|
| @ConditionalOnBean |
|
| @ConditionalOnMissingBean |
|
| @ConditionalOnProperty |
|
| @ConditionalOnWebApplication |
|
3.3 自动配置源码示例
以 Redis 自动配置为例:
// 文件位置:org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration// 标识这是一个配置类@Configuration(proxyBeanMethods = false)// 当类路径下存在 RedisOperations 类时才生效// 也就是引入了 spring-boot-starter-data-redis 依赖时@ConditionalOnClass(RedisOperations.class)// 当容器中没有 RedisTemplateBean 时才生效// 这样用户自定义的 RedisTemplate 会覆盖默认配置@ConditionalOnMissingBean(name= "redisTemplate")// 启用 Redis 的配置属性@EnableConfigurationProperties(RedisProperties.class)// 导入配置类@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })publicclassRedisAutoConfiguration{// 配置 RedisTemplate@Bean@ConditionalOnMissingBean// 当没有 RedisTemplate 时才创建public RedisTemplate<Object, Object> redisTemplate( RedisConnectionFactory connectionFactory){// 创建并配置 RedisTemplate RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory);// 配置默认的序列化器// 使用 JDK 序列化器 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new JdkSerializationRedisSerializer());// 配置 Hash 类型的序列化 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new JdkSerializationRedisSerializer()); template.afterPropertiesSet();return template; }// 配置 StringRedisTemplate(常用)@Bean@ConditionalOnMissingBeanpublic StringRedisTemplate stringRedisTemplate( RedisConnectionFactory connectionFactory){ StringRedisTemplate template = new StringRedisTemplate(connectionFactory);return template; }}
4. 自动配置的执行时机
4.1 执行顺序
自动配置的执行顺序如下:
1.SpringBoot启动↓2.@SpringBootApplication扫描↓3.@EnableAutoConfiguration触发↓4.AutoConfigurationImportSelector执行↓5.加载spring.factories中的配置类↓6.根据条件注解判断是否生效↓7.执行配置类的@Bean方法↓8.应用启动完成
4.2 如何控制配置顺序
有时代我们需要控制自动配置的顺序,Spring Boot 提供了以下方式:
// 方式1:使用 @Order 注解// 指定 Bean 的加载顺序,数值越小越先加载@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public DataSource dataSource(){returnnew HikariDataSource();}// 方式2:使用 @AutoConfigureOrder// 指定自动配置类的加载顺序@Configuration@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)publicclassMyAutoConfiguration{// ...}// 方式3:使用 @AutoConfigureBefore / @AutoConfigureAfter// 指定在某个配置类之前/之后加载@Configuration@AutoConfigureBefore(WebMvcAutoConfiguration.class)publicclassMyWebAutoConfiguration{// ...}
5. 自定义自动配置
5.1 为什么要自定义自动配置
自定义自动配置的好处:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
5.2 自定义 starter 示例
假设我们要创建一个通用的文件上传 starter:
第一步:创建配置属性类
// 文件位置:file-spring-boot-starter/src/main/java/com/example/file/FileProperties.java// 启用配置属性绑定@ConfigurationProperties(prefix = "spring.file")publicclassFileProperties{// 文件存储路径private String storagePath = "/tmp/files";// 最大文件大小(默认 10MB)privatelong maxSize = 10485760;// 允许的文件类型private List<String> allowedTypes = Arrays.asList("jpg", "png", "pdf");// 是否启用privateboolean enabled = true;// Getter 和 Setter 省略...}
第二步:创建自动配置类
// 文件位置:file-spring-boot-autoconfigure/src/main/java/com/example/file/FileAutoConfiguration.java@Configuration// 条件:当类路径下存在 FileService 类时生效@ConditionalOnClass(FileService.class)// 绑定配置属性@EnableConfigurationProperties(FileProperties.class)publicclassFileAutoConfiguration{// 注入配置属性privatefinal FileProperties fileProperties;publicFileAutoConfiguration(FileProperties fileProperties){this.fileProperties = fileProperties; }// 创建 FileService Bean@Bean// 条件:当容器中没有 FileService 时创建@ConditionalOnMissingBeanpublic FileService fileService(){returnnew FileServiceImpl(fileProperties); }}
第三步:创建 spring.factories 文件
# 文件位置:file-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.example.file.FileAutoConfiguration
第四步:在 starter 中引用自动配置模块
<!-- file-spring-boot-starter/pom.xml --><dependencies><!-- 引入自动配置模块 --><dependency><groupId>com.example</groupId><artifactId>file-spring-boot-autoconfigure</artifactId><version>1.0.0</version></dependency></dependencies>
使用方式:
# 只需要在 application.yml 中配置即可spring:file:storage-path:/data/filesmax-size:20971520enabled:true
6. 总结
6.1 核心知识点
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6.2 启动流程图
┌──────────────────────────────────────────────────────────────────┐│SpringBoot启动流程│├──────────────────────────────────────────────────────────────────┤│││┌────────────────┐│││main方法入口│││└───────┬────────┘││↓││┌─────────────────────────────────────┐│││SpringApplication.run()││││-创建SpringApplication││││-执行run()方法│││└───────┬────────────────────────────┘││↓││┌─────────────────────────────────────┐│││@EnableAutoConfiguration││││-导入AutoConfigurationImportSelector│││└───────┬────────────────────────────┘││↓││┌─────────────────────────────────────┐│││加载spring.factories││││-读取自动配置类│││└───────┬────────────────────────────┘││↓││┌─────────────────────────────────────┐│││条件过滤││││-@ConditionalOnClass││││-@ConditionalOnBean│││└───────┬────────────────────────────┘││↓││┌─────────────────────────────────────┐│││注册Bean││││-执行@Bean方法││││-应用启动完成│││└─────────────────────────────────────┘│││└──────────────────────────────────────────────────────────────────┘
end





夜雨聆风