Spring Boot 自动配置原理深度解析
面试官:说说 Spring Boot 的自动配置原理?
你:Spring Boot 通过
@EnableAutoConfiguration读取 classpath 下所有META-INF/spring.factories(或 Spring Boot 3 的.imports文件)中注册的自动配置类,然后通过@ConditionalOnXxx条件注解过滤出当前环境需要的配置类并加载,实现”引入依赖即生效”的自动配置。面试官:那自动配置类是如何被加载的?条件注解在什么时候判断?
这个问题考察的是你对SPI 机制和条件注解执行时机的理解,只有掌握这些才能真正理解 Spring Boot 的”智能”自动配置。
链式追问一:@SpringBootApplication 组成
Section titled “链式追问一:@SpringBootApplication 组成”Q1:@SpringBootApplication 注解的组成?必考
Section titled “Q1:@SpringBootApplication 注解的组成?”回答要点:
@SpringBootApplication 源码:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration // 1. 等价于 @Configuration@EnableAutoConfiguration // 2. ★ 开启自动配置(核心)@ComponentScan( // 3. 组件扫描 excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication { // ...}三大核心注解:
┌─────────────────────────────────────────────────────────────┐│ @SpringBootConfiguration │├─────────────────────────────────────────────────────────────┤│ 等价于 @Configuration ││ 标识主类是一个配置类,可以声明 @Bean 方法 ││ ││ 示例: ││ @SpringBootApplication ││ public class Application { ││ @Bean ││ public RestTemplate restTemplate() { ││ return new RestTemplate(); ││ } ││ } │└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐│ @EnableAutoConfiguration │├─────────────────────────────────────────────────────────────┤│ 开启自动配置,核心注解 ││ ││ @EnableAutoConfiguration 源码: ││ @AutoConfigurationPackage ││ @Import(AutoConfigurationImportSelector.class) ││ ││ 作用: ││ 1. @AutoConfigurationPackage ││ → 注册主类所在包为自动扫描包 ││ → 保存到 BasePackages(供其他组件查询) ││ ││ 2. @Import(AutoConfigurationImportSelector.class) ││ → 导入自动配置选择器 ││ → 加载所有自动配置类 │└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐│ @ComponentScan │├─────────────────────────────────────────────────────────────┤│ 扫描当前包及子包的组件 ││ 默认扫描:@Component、@Service、@Repository、@Controller ││ ││ 排除过滤器: ││ • TypeExcludeFilter:排除指定类型的组件 ││ • AutoConfigurationExcludeFilter:排除自动配置类 ││ (避免被 @ComponentScan 扫描两次) │└─────────────────────────────────────────────────────────────┘本质一句话:@SpringBootApplication 是三个注解的组合:@Configuration(配置类)+ @EnableAutoConfiguration(自动配置)+ @ComponentScan(组件扫描)。
Q2:@EnableAutoConfiguration 如何加载自动配置类?必考
Section titled “Q2:@EnableAutoConfiguration 如何加载自动配置类?”自动配置加载流程:
Spring Boot 启动流程中的自动配置:
1. @EnableAutoConfiguration 触发 ↓2. @Import(AutoConfigurationImportSelector.class) ↓3. AutoConfigurationImportSelector.selectImports() ↓4. 读取 classpath 下所有 META-INF/spring.factories ↓5. 获取 key = EnableAutoConfiguration 的所有值(自动配置类列表) ↓6. 过滤 exclude 列表(通过 @SpringBootApplication.exclude 指定) ↓7. 去重、排序(@AutoConfigureBefore/After 控制顺序) ↓8. 条件注解过滤(@ConditionalOnXxx 判断是否生效) ↓9. 将满足条件的配置类注册为 BeanDefinition ↓10. 实例化自动配置类中的 BeanSpring Boot 2.x vs 3.x 对比:
Spring Boot 2.x(基于 spring.factories):
文件位置: META-INF/spring.factories
文件格式: # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
缺点: • Properties 格式,需解析 • 所有配置类写在一行,难以维护 • 性能稍差(需要加载并解析文件)
Spring Boot 3.x(基于 imports 文件):
文件位置: META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件格式: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
优点: • 每行一个类,简洁清晰 • 无需解析 Properties,性能更好 • 更符合 Java SPI 规范
兼容性: • Spring Boot 3 仍支持 spring.factories(向后兼容) • 推荐新项目使用 .imports 文件源码示例:
// AutoConfigurationImportSelector 核心源码(简化)public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware {
@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 metadata) { // 1. 检查是否启用自动配置 if (!isEnabled(metadata)) { return EMPTY_ENTRY; }
// 2. 获取注解属性(exclude、excludeName) AnnotationAttributes attributes = getAttributes(metadata);
// 3. ★ 读取 spring.factories 中的自动配置类 List<String> configurations = getCandidateConfigurations(metadata, attributes);
// 4. 去重 configurations = removeDuplicates(configurations);
// 5. 应用 exclude 列表 Set<String> exclusions = getExclusions(metadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions);
// 6. 过滤(条件注解) configurations = getConfigurationClassFilter().filter(configurations);
// 7. 触发自动配置导入事件 fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions); }
// 读取 spring.factories protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
return configurations; }}链式追问二:条件注解原理
Section titled “链式追问二:条件注解原理”Q1:@ConditionalOnXxx 条件注解是如何工作的?必考
Section titled “Q1:@ConditionalOnXxx 条件注解是如何工作的?”条件注解分类:
类条件注解: @ConditionalOnClass → classpath 中存在指定类 @ConditionalOnMissingClass → classpath 中不存在指定类
Bean 条件注解: @ConditionalOnBean → 容器中存在指定 Bean @ConditionalOnMissingBean → 容器中不存在指定 Bean
属性条件注解: @ConditionalOnProperty → 配置文件中存在且值匹配指定属性
应用类型条件注解: @ConditionalOnWebApplication → 当前是 Web 应用 @ConditionalOnNotWebApplication → 当前不是 Web 应用
表达式条件注解: @ConditionalOnExpression → SpEL 表达式为 true
资源条件注解: @ConditionalOnResource → classpath 中存在指定资源文件
其他: @ConditionalOnJava → JVM 版本满足条件 @ConditionalOnCloudPlatform → 运行在指定云平台条件注解工作流程:
自动配置类加载流程:
1. 读取候选配置类(spring.factories) ↓2. ConfigurationClassPostProcessor.processConfigBeanDefinitions() ↓3. ConfigurationClassParser.parse() 解析配置类 ↓4. 检查类上的 @Conditional 注解 ↓5. 调用 Condition.matches() 判断条件 ↓6. 条件满足 → 继续解析 条件不满足 → 跳过该配置类 ↓7. 解析配置类中的 @Bean 方法 ↓8. 检查 @Bean 方法上的 @Conditional 注解 ↓9. 条件满足 → 注册 BeanDefinition 条件不满足 → 跳过该 @Bean 方法代码示例:
@Configuration@ConditionalOnClass(DataSource.class) // 1. classpath 有 DataSource 类@ConditionalOnMissingBean(DataSourceInitializer.class) // 2. 容器中没有自定义 DataSourceInitializer@EnableConfigurationProperties(DataSourceProperties.class) // 3. 绑定配置属性public class DataSourceAutoConfiguration {
@Bean @ConditionalOnMissingBean // 4. 用户没有自定义时,才创建默认 Bean public DataSource dataSource(DataSourceProperties properties) { return properties.initializeDataSourceBuilder().build(); }
@Bean @ConditionalOnProperty(prefix = "spring.datasource", name = "continue-on-error", havingValue = "true") // 5. 配置为 true 时才创建 public DataSourceInitializer dataSourceInitializer(DataSource dataSource, DataSourceProperties properties) { DataSourceInitializer initializer = new DataSourceInitializer(); initializer.setDataSource(dataSource); initializer.setDatabasePopulator(properties.getDatabasePopulator()); return initializer; }}条件注解实现原理:
// @ConditionalOnClass 源码@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Conditional(OnClassCondition.class) // 指定 Condition 实现类public @interface OnClassCondition { Class<?>[] value() default {}; String[] name() default {};}
// OnClassCondition 实现类class OnClassCondition extends SpringBootCondition implements ConfigurationCondition {
@Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { // 1. 获取注解中指定的类名 List<String> classNames = getClasses(metadata);
// 2. 检查 classpath 是否存在这些类 ClassLoader classLoader = context.getClassLoader(); for (String className : classNames) { if (!ClassUtils.isPresent(className, classLoader)) { // 类不存在,条件不满足 return ConditionOutcome.noMatch( ConditionMessage.forCondition(ConditionalOnClass.class) .didNotFind("required class", "required classes") .items(Style.QUOTE, classNames)); } }
// 3. 所有类都存在,条件满足 return ConditionOutcome.match( ConditionMessage.forCondition(ConditionalOnClass.class) .found("required class", "required classes") .items(Style.QUOTE, classNames)); }}Q2:@ConditionalOnMissingBean 为什么能实现”允许用户覆盖”?高频
Section titled “Q2:@ConditionalOnMissingBean 为什么能实现”允许用户覆盖”?”核心设计理念:
自动配置的"约定大于配置":
1. 自动配置提供默认实现 ↓ 2. 用户可以通过自定义 Bean 覆盖默认配置 ↓ 3. @ConditionalOnMissingBean 检测容器中是否已有同类型 Bean ↓ 4. 如果用户已定义,自动配置的 Bean 不创建 ↓ 5. 如果用户未定义,自动配置的 Bean 创建
好处: • 零配置即可运行(使用默认配置) • 需要定制时只需自定义 Bean(覆盖默认配置) • 灵活且不侵入代码示例:
// RedisAutoConfiguration 源码(简化)@Configuration@ConditionalOnClass(RedisOperations.class)@EnableConfigurationProperties(RedisProperties.class)public class RedisAutoConfiguration {
@Bean @ConditionalOnMissingBean(name = "redisTemplate") // ★ 关键注解 public RedisTemplate<Object, Object> redisTemplate( RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; }
@Bean @ConditionalOnMissingBean // StringRedisTemplate 也可能被覆盖 public StringRedisTemplate stringRedisTemplate( RedisConnectionFactory redisConnectionFactory) { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; }}
// 用户自定义 RedisTemplate@Configurationpublic class RedisConfig {
@Bean public RedisTemplate<String, Object> redisTemplate( RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory);
// 自定义序列化 Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisRedisSerializer<>(Object.class); template.setDefaultSerializer(serializer);
return template; }}
// 结果:// 自动配置的 redisTemplate 不创建(@ConditionalOnMissingBean 生效)// 用户自定义的 redisTemplate 创建// 实现了覆盖对比表格:
| 注解 | 条件 | 使用场景 |
|---|---|---|
@ConditionalOnBean | 容器中存在指定 Bean | 依赖其他 Bean 才能生效 |
@ConditionalOnMissingBean | 容器中不存在指定 Bean | 允许用户覆盖(最常用) |
@ConditionalOnClass | classpath 存在指定类 | 依赖第三方库才生效 |
@ConditionalOnMissingClass | classpath 不存在指定类 | 不依赖第三方库时生效 |
链式追问三:Starter 机制
Section titled “链式追问三:Starter 机制”Q1:如何自定义一个 Spring Boot Starter?实战
Section titled “Q1:如何自定义一个 Spring Boot Starter?”Starter 开发规范:
Starter 命名规范: 官方 Starter:spring-boot-starter-{name} 例:spring-boot-starter-web、spring-boot-starter-data-redis
第三方 Starter:{name}-spring-boot-starter 例:mybatis-spring-boot-starter、druid-spring-boot-starter
Starter 结构: {name}-spring-boot-starter ├── pom.xml # 依赖管理 ├── src/main/java │ └── com.example │ ├── autoconfigure # 自动配置类 │ │ ├── XxxAutoConfiguration.java │ │ └── XxxProperties.java │ └── starter # 功能实现(可选) └── src/main/resources └── META-INF └── spring.factories # 注册自动配置类实战示例:自定义 Redis Lock Starter:
// 1. 创建 pom.xml<?xml version="1.0" encoding="UTF-8"?><project> <groupId>com.example</groupId> <artifactId>redis-lock-spring-boot-starter</artifactId> <version>1.0.0</version>
<dependencies> <!-- Spring Boot 自动配置依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency>
<!-- Redis 依赖(可选) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <optional>true</optional> </dependency>
<!-- 配置处理器(生成配置元数据) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies></project>
// 2. 创建配置属性类@ConfigurationProperties(prefix = "redis.lock")public class RedisLockProperties { private boolean enabled = true; private long timeout = 30000; // 默认超时30秒 private String keyPrefix = "lock:";
// getters/setters}
// 3. 创建核心功能类public class RedisLockService { private final RedisTemplate<String, String> redisTemplate; private final RedisLockProperties properties;
public RedisLockService(RedisTemplate<String, String> redisTemplate, RedisLockProperties properties) { this.redisTemplate = redisTemplate; this.properties = properties; }
public boolean tryLock(String key, long timeout) { String lockKey = properties.getKeyPrefix() + key; String value = UUID.randomUUID().toString();
Boolean success = redisTemplate.opsForValue() .setIfAbsent(lockKey, value, timeout, TimeUnit.MILLISECONDS);
return Boolean.TRUE.equals(success); }
public void unlock(String key) { String lockKey = properties.getKeyPrefix() + key; redisTemplate.delete(lockKey); }}
// 4. 创建自动配置类@Configuration@ConditionalOnClass(RedisTemplate.class) // classpath 有 Redis@ConditionalOnProperty(prefix = "redis.lock", name = "enabled", havingValue = "true", matchIfMissing = true)@EnableConfigurationProperties(RedisLockProperties.class)public class RedisLockAutoConfiguration {
@Bean @ConditionalOnMissingBean // 允许用户覆盖 public RedisLockService redisLockService( RedisTemplate<String, String> redisTemplate, RedisLockProperties properties) { return new RedisLockService(redisTemplate, properties); }}
// 5. 注册自动配置类(META-INF/spring.factories)org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.example.autoconfigure.RedisLockAutoConfiguration
// 6. 生成配置元数据(META-INF/spring-configuration-metadata.json){ "properties": [ { "name": "redis.lock.enabled", "type": "java.lang.Boolean", "defaultValue": true, "description": "Enable Redis lock." }, { "name": "redis.lock.timeout", "type": "java.lang.Long", "defaultValue": 30000, "description": "Lock timeout in milliseconds." }, { "name": "redis.lock.key-prefix", "type": "java.lang.String", "defaultValue": "lock:", "description": "Redis key prefix." } ]}使用示例:
// 引入依赖<dependency> <groupId>com.example</groupId> <artifactId>redis-lock-spring-boot-starter</artifactId> <version>1.0.0</version></dependency>
// 配置文件(application.yml)redis: lock: enabled: true timeout: 60000 # 60秒超时 key-prefix: "myapp:lock:"
// 使用@Servicepublic class OrderService {
@Autowired private RedisLockService lockService;
public void createOrder(String orderId) { String lockKey = "order:" + orderId;
if (lockService.tryLock(lockKey, 60000)) { try { // 业务逻辑 } finally { lockService.unlock(lockKey); } } else { throw new RuntimeException("获取锁失败"); } }}链式追问四:Spring Boot 启动流程
Section titled “链式追问四:Spring Boot 启动流程”Q1:Spring Boot 的启动流程是怎样的?必考
Section titled “Q1:Spring Boot 的启动流程是怎样的?”完整启动流程:
SpringApplication.run(Application.class, args) ↓┌─────────────────────────────────────────────────────────────┐│ 阶段1:创建 SpringApplication 对象 │├─────────────────────────────────────────────────────────────┤│ new SpringApplication(primarySource) ││ ↓ ││ 1. 推断应用类型(NONE / SERVLET / REACTIVE) ││ • 检查 classpath 是否有 Servlet 类 ││ • 检查 classpath 是否有 Reactive 类 ││ ││ 2. 加载 ApplicationContextInitializer(spring.factories) ││ • 用于在 ApplicationContext 刷新前进行初始化 ││ • 例:ContextIdApplicationContextInitializer ││ ││ 3. 加载 ApplicationListener(spring.factories) ││ • 监听 SpringApplication 事件 ││ • 例:LoggingApplicationListener、ConfigFileAppList ││ ││ 4. 推断主类(main 方法所在类) ││ • 通过异常堆栈找到 main 方法 │└─────────────────────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────────────────────┐│ 阶段2:执行 run() 方法 │├─────────────────────────────────────────────────────────────┤│ 1. StopWatch 计时开始 ││ ││ 2. 创建并启动 SpringApplicationRunListeners ││ • listeners.starting() ││ • 发布 ApplicationStartingEvent ││ ││ 3. 准备 Environment(环境变量、配置文件) ││ • 创建 StandardServletEnvironment ││ • 加载 application.yml / application.properties ││ • listeners.environmentPrepared() ││ • 发布 ApplicationEnvironmentPreparedEvent ││ ││ 4. 打印 Banner(彩色 Spring 字样) ││ • 检查 spring.main.banner-mode 配置 ││ ││ 5. 创建 ApplicationContext ││ • SERVLET → AnnotationConfigServletWebServerAppContext ││ • REACTIVE → AnnotationConfigReactiveWebServerAppCtx ││ • NONE → AnnotationConfigApplicationContext ││ ││ 6. prepareContext() ││ • 应用 ApplicationContextInitializer ││ • listeners.contextPrepared() ││ • 加载主类 BeanDefinition ││ • listeners.contextLoaded() ││ • 发布 ApplicationContextInitializedEvent ││ ││ 7. ★ refreshContext()(核心) ││ • 调用 AbstractApplicationContext.refresh() ││ • 扫描 + 解析 + 注册所有 BeanDefinition ││ • 加载自动配置类(此时 @ConditionalOnXxx 判断) ││ • 实例化所有非懒加载单例 Bean ││ • 初始化内嵌 Web 服务器(Tomcat) ││ • DispatcherServlet 等 MVC 组件初始化 ││ ││ 8. afterRefresh()(空实现,扩展点) ││ ││ 9. StopWatch 计时结束,打印启动耗时 ││ • listeners.started() ││ • 发布 ApplicationStartedEvent ││ ││ 10. callRunners() ││ • 执行 ApplicationRunner / CommandLineRunner ││ • 用于启动后执行自定义逻辑 ││ ││ 11. listeners.running() ││ • 发布 ApplicationReadyEvent ││ • 应用启动完成 │└─────────────────────────────────────────────────────────────┘关键扩展点:
// 1. ApplicationContextInitializerpublic class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext context) { // 在 ApplicationContext 刷新前执行 System.out.println("初始化 ApplicationContext"); }}
// 注册:META-INF/spring.factoriesorg.springframework.context.ApplicationContextInitializer=\com.example.MyInitializer
// 2. ApplicationRunner@Componentpublic class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { // 在应用启动后执行 System.out.println("应用启动完成,执行初始化逻辑"); }}
// 3. CommandLineRunner@Componentpublic class MyCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { // 在应用启动后执行(与 ApplicationRunner 类似) System.out.println("命令行参数: " + Arrays.toString(args)); }}Q2:内嵌 Tomcat 是如何启动的?高频
Section titled “Q2:内嵌 Tomcat 是如何启动的?”Tomcat 启动流程:
refreshContext() → AbstractApplicationContext.refresh() ↓onRefresh()(ServletWebServerApplicationContext) ↓createWebServer() ↓┌─────────────────────────────────────────────────────────────┐│ 1. 获取 ServletWebServerFactory │├─────────────────────────────────────────────────────────────┤│ • 从容器获取 ServletWebServerFactory Bean ││ • TomcatServletWebServerFactory(默认) ││ • JettyServletWebServerFactory ││ • UndertowServletWebServerFactory │└─────────────────────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────────────────────┐│ 2. 创建 WebServer │├─────────────────────────────────────────────────────────────┤│ factory.getWebServer(servletContext) ││ ↓ ││ • 创建 Tomcat 实例 ││ • 设置端口(server.port,默认8080) ││ • 配置连接器(Connector) ││ • 配置引擎(Engine)、主机(Host)、上下文(Context) │└─────────────────────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────────────────────┐│ 3. 注册 DispatcherServlet │├─────────────────────────────────────────────────────────────┤│ • ServletRegistrationBean<DispatcherServlet> ││ • 映射到 "/"(处理所有请求) ││ • 设置加载顺序(loadOnStartup = 1) │└─────────────────────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────────────────────┐│ 4. 启动 Tomcat │├─────────────────────────────────────────────────────────────┤│ tomcat.start() ││ ↓ ││ • 初始化连接器(绑定端口) ││ • 启动内部组件 ││ • 触发 LifecycleListener │└─────────────────────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────────────────────┐│ 5. 发布事件 │├─────────────────────────────────────────────────────────────┤│ ServletWebServerInitializedEvent ││ • 包含 WebServer 和 ApplicationContext 信息 ││ • 通知应用 Web 服务器已启动 │└─────────────────────────────────────────────────────────────┘性能数据:
Spring Boot 2.7 启动时间(MacBook Pro M1): 空项目: ~1.2s 引入 Web: ~2.1s 引入 JPA: ~2.8s 引入 Redis: ~2.5s
Tomcat 启动时间: 创建 Tomcat: ~200ms 启动连接器: ~100ms 总计: ~300ms
优化建议: 1. 延迟初始化(spring.main.lazy-initialization=true) → 启动时间减少 30-50% → 首次请求延迟增加
2. 禁用不必要的自动配置 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
3. 使用 Spring Boot 3.x(启动性能优化) → 比 Spring Boot 2.x 快 10-20%高频面试题速查
Section titled “高频面试题速查”Q:Spring Boot 自动配置原理?
@EnableAutoConfiguration 通过 AutoConfigurationImportSelector 读取 META-INF/spring.factories(或 Spring Boot 3 的 .imports 文件)中注册的自动配置类,通过 @ConditionalOnXxx 条件注解过滤出当前环境需要的配置类并加载,实现”引入依赖即生效”。
Q:@ConditionalOnMissingBean 的作用?
容器中不存在指定 Bean 时配置才生效,这是”允许用户覆盖”的核心机制。自动配置的 Bean 声明上都加了这个注解,用户自定义同类型 Bean 后,自动配置的 Bean 不会创建,实现用户自定义覆盖默认行为。
Q:如何自定义 Starter?
- 创建 Maven 模块(命名:
{name}-spring-boot-starter);2. 在META-INF/spring.factories配置EnableAutoConfiguration指向自动配置类;3. 自动配置类使用@ConditionalOnXxx条件注解;4. 使用@ConfigurationProperties绑定配置;5. 使用@ConditionalOnMissingBean允许用户覆盖。
Q:Spring Boot 启动流程?
核心在 SpringApplication.run():准备环境(加载配置文件)→ 创建 ApplicationContext → prepareContext(应用 Initializer、加载主类 BeanDefinition)→ refreshContext(扫描+实例化所有 Bean、启动内嵌 Tomcat)→ 执行 ApplicationRunner/CommandLineRunner。
Q:Spring Boot 2.x 和 3.x 自动配置的区别?
Spring Boot 2.x 基于 META-INF/spring.factories(Properties 格式);Spring Boot 3.x 基于 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(每行一个类,性能更好)。Spring Boot 3 仍兼容 spring.factories。