⚙️ 第11章 SpringBoot AutoConfig

深入理解自动配置机制和条件注解

💻 查看完整代码 - 在线IDE体验

📋 学习目标

  • 自动配置原理 - 理解SpringBoot自动配置机制
  • 条件注解 - 掌握@Conditional系列注解
  • 配置类编写 - 创建自定义自动配置
  • Starter开发 - 开发自定义Starter
  • 配置属性 - 使用@ConfigurationProperties
  • 调试技巧 - 自动配置调试和排查

🔍 自动配置核心概念

  • @EnableAutoConfiguration - 启用自动配置
  • @AutoConfiguration - 自动配置类
  • @Conditional - 条件装配
  • spring.factories - 配置文件
  • @ConfigurationProperties - 属性绑定
  • AutoConfigurationImportSelector - 导入选择器

📦 自动配置依赖配置

<!-- pom.xml 自动配置依赖 --> <dependencies> <!-- SpringBoot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- 自动配置处理器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <!-- 配置处理器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- 条件注解支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-conditional</artifactId> </dependency> </dependencies> # application.yml 自动配置相关配置 spring: # 应用基本信息 application: name: autoconfig-demo # 自动配置调试 main: # 显示自动配置报告 show-banner: true # 允许Bean定义覆盖 allow-bean-definition-overriding: true # 自定义配置属性 custom: service: # 是否启用自定义服务 enabled: true # 服务名称 name: "Custom Service" # 超时时间 timeout: 30000 # 重试次数 retry-count: 3 # 配置列表 endpoints: - name: "endpoint1" url: "http://localhost:8081" - name: "endpoint2" url: "http://localhost:8082" # 日志配置 logging: level: # 显示自动配置日志 org.springframework.boot.autoconfigure: DEBUG # 显示条件评估日志 org.springframework.boot.autoconfigure.condition: TRACE cn.bugstack.springframework.boot.autoconfig: DEBUG

🔄 自动配置流程

1
启动扫描
@EnableAutoConfiguration触发自动配置扫描
2
加载配置类
从spring.factories文件加载自动配置类
3
条件评估
根据@Conditional注解评估是否满足条件
4
Bean注册
满足条件的配置类注册Bean到容器
5
属性绑定
@ConfigurationProperties绑定配置属性
6
完成装配
所有自动配置完成,应用启动

🎯 自动配置实现

// CustomServiceProperties.java - 配置属性类 @ConfigurationProperties(prefix = "custom.service") @Data public class CustomServiceProperties { /** * 是否启用自定义服务 */ private boolean enabled = true; /** * 服务名称 */ private String name = "Default Service"; /** * 超时时间(毫秒) */ private long timeout = 30000; /** * 重试次数 */ private int retryCount = 3; /** * 端点配置列表 */ private List<Endpoint> endpoints = new ArrayList<>(); @Data public static class Endpoint { private String name; private String url; } } // CustomService.java - 自定义服务 @Slf4j public class CustomService { private final CustomServiceProperties properties; public CustomService(CustomServiceProperties properties) { this.properties = properties; log.info("CustomService initialized with name: {}", properties.getName()); } /** * 执行服务调用 */ public String execute(String request) { log.info("Executing request: {} with timeout: {}ms", request, properties.getTimeout()); // 模拟服务调用 try { Thread.sleep(100); // 模拟处理时间 return "Response for: " + request; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Service execution interrupted", e); } } /** * 获取服务信息 */ public Map<String, Object> getServiceInfo() { Map<String, Object> info = new HashMap<>(); info.put("name", properties.getName()); info.put("timeout", properties.getTimeout()); info.put("retryCount", properties.getRetryCount()); info.put("endpointCount", properties.getEndpoints().size()); return info; } /** * 健康检查 */ public boolean isHealthy() { // 实际应用中可以检查服务状态 return properties.isEnabled(); } } // CustomServiceAutoConfiguration.java - 自动配置类 @AutoConfiguration @ConditionalOnClass(CustomService.class) @ConditionalOnProperty(prefix = "custom.service", name = "enabled", havingValue = "true", matchIfMissing = true) @EnableConfigurationProperties(CustomServiceProperties.class) @Slf4j public class CustomServiceAutoConfiguration { /** * 自定义服务Bean */ @Bean @ConditionalOnMissingBean public CustomService customService(CustomServiceProperties properties) { log.info("Auto-configuring CustomService with properties: {}", properties); return new CustomService(properties); } /** * 自定义服务管理器 */ @Bean @ConditionalOnBean(CustomService.class) @ConditionalOnProperty(prefix = "custom.service", name = "manager.enabled", havingValue = "true") public CustomServiceManager customServiceManager(CustomService customService) { log.info("Auto-configuring CustomServiceManager"); return new CustomServiceManager(customService); } /** * 自定义健康检查指示器 */ @Bean @ConditionalOnClass(name = "org.springframework.boot.actuator.health.HealthIndicator") @ConditionalOnBean(CustomService.class) public CustomServiceHealthIndicator customServiceHealthIndicator(CustomService customService) { log.info("Auto-configuring CustomServiceHealthIndicator"); return new CustomServiceHealthIndicator(customService); } } // CustomServiceManager.java - 服务管理器 @Component @Slf4j public class CustomServiceManager { private final CustomService customService; private final AtomicLong requestCount = new AtomicLong(0); private final AtomicLong successCount = new AtomicLong(0); private final AtomicLong errorCount = new AtomicLong(0); public CustomServiceManager(CustomService customService) { this.customService = customService; } /** * 执行带统计的服务调用 */ public String executeWithStats(String request) { requestCount.incrementAndGet(); try { String result = customService.execute(request); successCount.incrementAndGet(); return result; } catch (Exception e) { errorCount.incrementAndGet(); log.error("Service execution failed for request: {}", request, e); throw e; } } /** * 获取统计信息 */ public Map<String, Object> getStatistics() { Map<String, Object> stats = new HashMap<>(); stats.put("requestCount", requestCount.get()); stats.put("successCount", successCount.get()); stats.put("errorCount", errorCount.get()); stats.put("successRate", calculateSuccessRate()); return stats; } private double calculateSuccessRate() { long total = requestCount.get(); if (total == 0) { return 0.0; } return (double) successCount.get() / total * 100; } } // CustomServiceHealthIndicator.java - 健康检查指示器 public class CustomServiceHealthIndicator implements HealthIndicator { private final CustomService customService; public CustomServiceHealthIndicator(CustomService customService) { this.customService = customService; } @Override public Health health() { try { if (customService.isHealthy()) { Map<String, Object> serviceInfo = customService.getServiceInfo(); return Health.up() .withDetails(serviceInfo) .build(); } else { return Health.down() .withDetail("reason", "Service is disabled") .build(); } } catch (Exception e) { return Health.down() .withDetail("error", e.getMessage()) .build(); } } }

🎨 自动配置架构图示

Starter
启动器
Condition
条件评估
Bean
Bean注册
Property
属性绑定

自动配置流程:Starter触发 → 条件评估 → Bean注册 → 属性绑定

每个环节都有相应的注解和机制支持

📍 条件注解

  • @ConditionalOnClass - 类存在时生效
  • @ConditionalOnMissingClass - 类不存在时生效
  • @ConditionalOnBean - Bean存在时生效
  • @ConditionalOnMissingBean - Bean不存在时生效
  • @ConditionalOnProperty - 属性匹配时生效
  • @ConditionalOnResource - 资源存在时生效

📄 配置文件

  • spring.factories - 自动配置类声明
  • spring-configuration-metadata.json - 配置元数据
  • additional-spring-configuration-metadata.json - 额外元数据
  • application.yml - 应用配置
  • bootstrap.yml - 引导配置
  • META-INF/spring.factories - 工厂配置

🔧 自定义Starter开发

// 1. 创建spring.factories文件 # META-INF/spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.bugstack.springframework.boot.autoconfig.CustomServiceAutoConfiguration // 2. 创建配置元数据文件 # META-INF/spring-configuration-metadata.json { "groups": [ { "name": "custom.service", "type": "cn.bugstack.springframework.boot.autoconfig.CustomServiceProperties", "sourceType": "cn.bugstack.springframework.boot.autoconfig.CustomServiceProperties" } ], "properties": [ { "name": "custom.service.enabled", "type": "java.lang.Boolean", "description": "Whether to enable the custom service.", "defaultValue": true }, { "name": "custom.service.name", "type": "java.lang.String", "description": "The name of the custom service.", "defaultValue": "Default Service" }, { "name": "custom.service.timeout", "type": "java.lang.Long", "description": "The timeout for service calls in milliseconds.", "defaultValue": 30000 }, { "name": "custom.service.retry-count", "type": "java.lang.Integer", "description": "The number of retry attempts.", "defaultValue": 3 } ] } // 3. 创建自定义条件注解 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnCustomServiceCondition.class) public @interface ConditionalOnCustomService { /** * 服务名称 */ String name() default ""; /** * 是否必须启用 */ boolean enabled() default true; } // OnCustomServiceCondition.java - 自定义条件实现 public class OnCustomServiceCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); // 获取注解属性 Map<String, Object> attributes = metadata.getAnnotationAttributes( ConditionalOnCustomService.class.getName()); if (attributes == null) { return false; } String name = (String) attributes.get("name"); boolean enabled = (Boolean) attributes.get("enabled"); // 检查服务是否启用 boolean serviceEnabled = environment.getProperty( "custom.service.enabled", Boolean.class, true); if (!serviceEnabled && enabled) { return false; } // 检查特定服务名称 if (StringUtils.hasText(name)) { String serviceName = environment.getProperty("custom.service.name"); return name.equals(serviceName); } return true; } } // 4. 使用自定义条件 @Configuration @ConditionalOnCustomService(name = "special-service", enabled = true) public class SpecialServiceConfiguration { @Bean public SpecialService specialService() { return new SpecialService(); } } // 5. 创建Starter模块的pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <groupId>cn.bugstack.springframework.boot</groupId> <artifactId>custom-service-spring-boot-starter</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <name>Custom Service Spring Boot Starter</name> <description>Spring Boot Starter for Custom Service</description> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> </project> // 6. 使用自定义Starter # 在其他项目的pom.xml中引入 <dependency> <groupId>cn.bugstack.springframework.boot</groupId> <artifactId>custom-service-spring-boot-starter</artifactId> <version>1.0.0</version> </dependency> # 在application.yml中配置 custom: service: enabled: true name: "My Custom Service" timeout: 5000 retry-count: 5

📊 条件注解对比

条件注解 作用 使用场景 示例
@ConditionalOnClass 类存在时生效 依赖特定类库 @ConditionalOnClass(DataSource.class)
@ConditionalOnMissingClass 类不存在时生效 提供默认实现 @ConditionalOnMissingClass("com.example.Service")
@ConditionalOnBean Bean存在时生效 依赖其他Bean @ConditionalOnBean(DataSource.class)
@ConditionalOnMissingBean Bean不存在时生效 提供默认Bean @ConditionalOnMissingBean(name = "dataSource")
@ConditionalOnProperty 属性匹配时生效 基于配置启用 @ConditionalOnProperty("app.feature.enabled")
@ConditionalOnResource 资源存在时生效 依赖配置文件 @ConditionalOnResource("classpath:config.xml")

🔍 自动配置调试和排查

// AutoConfigurationController.java - 自动配置调试控制器 @RestController @RequestMapping("/autoconfig") @Slf4j public class AutoConfigurationController { @Autowired private ApplicationContext applicationContext; @Autowired private Environment environment; /** * 获取自动配置报告 */ @GetMapping("/report") public ResponseEntity<Map<String, Object>> getAutoConfigurationReport() { Map<String, Object> report = new HashMap<>(); // 获取所有Bean名称 String[] beanNames = applicationContext.getBeanDefinitionNames(); report.put("totalBeans", beanNames.length); report.put("beanNames", Arrays.asList(beanNames)); // 获取自动配置相关的Bean List<String> autoConfigBeans = Arrays.stream(beanNames) .filter(name -> name.contains("AutoConfiguration")) .collect(Collectors.toList()); report.put("autoConfigBeans", autoConfigBeans); return ResponseEntity.ok(report); } /** * 获取条件评估报告 */ @GetMapping("/conditions") public ResponseEntity<Map<String, Object>> getConditionEvaluationReport() { Map<String, Object> report = new HashMap<>(); // 这里可以通过ConditionEvaluationReport获取详细信息 // 实际实现需要访问SpringBoot内部API report.put("message", "Condition evaluation report"); report.put("timestamp", System.currentTimeMillis()); return ResponseEntity.ok(report); } /** * 获取配置属性 */ @GetMapping("/properties") public ResponseEntity<Map<String, Object>> getConfigurationProperties() { Map<String, Object> properties = new HashMap<>(); // 获取自定义配置属性 properties.put("custom.service.enabled", environment.getProperty("custom.service.enabled")); properties.put("custom.service.name", environment.getProperty("custom.service.name")); properties.put("custom.service.timeout", environment.getProperty("custom.service.timeout")); // 获取所有以custom开头的属性 Map<String, Object> customProperties = new HashMap<>(); if (environment instanceof ConfigurableEnvironment) { ConfigurableEnvironment configurableEnv = (ConfigurableEnvironment) environment; for (PropertySource<?> propertySource : configurableEnv.getPropertySources()) { if (propertySource instanceof EnumerablePropertySource) { EnumerablePropertySource<?> enumerablePropertySource = (EnumerablePropertySource<?>) propertySource; for (String propertyName : enumerablePropertySource.getPropertyNames()) { if (propertyName.startsWith("custom.")) { customProperties.put(propertyName, environment.getProperty(propertyName)); } } } } } properties.put("customProperties", customProperties); return ResponseEntity.ok(properties); } /** * 测试自定义服务 */ @GetMapping("/test-service") public ResponseEntity<Map<String, Object>> testCustomService() { Map<String, Object> result = new HashMap<>(); try { // 检查CustomService是否存在 if (applicationContext.containsBean("customService")) { CustomService customService = applicationContext.getBean(CustomService.class); // 测试服务调用 String response = customService.execute("test-request"); Map<String, Object> serviceInfo = customService.getServiceInfo(); result.put("serviceExists", true); result.put("response", response); result.put("serviceInfo", serviceInfo); result.put("healthy", customService.isHealthy()); } else { result.put("serviceExists", false); result.put("message", "CustomService bean not found"); } } catch (Exception e) { result.put("error", e.getMessage()); log.error("Error testing custom service", e); } return ResponseEntity.ok(result); } } # 启用自动配置调试 # application.yml debug: true # 或者通过启动参数 # java -jar app.jar --debug # 查看自动配置报告的方法: # 1. 启动时添加 --debug 参数 # 2. 查看控制台输出的CONDITIONS EVALUATION REPORT # 3. 访问 /autoconfig/report 端点 # 4. 使用Spring Boot Actuator的 /actuator/conditions 端点 # 常用调试命令 # 查看所有Bean curl http://localhost:8080/autoconfig/report # 查看配置属性 curl http://localhost:8080/autoconfig/properties # 测试自定义服务 curl http://localhost:8080/autoconfig/test-service # 查看条件评估(需要Actuator) curl http://localhost:8080/actuator/conditions

💡 自动配置最佳实践

🎯 开发建议

合理使用条件注解 - 确保自动配置在正确的条件下生效

提供配置属性 - 使用@ConfigurationProperties提供可配置选项

编写配置元数据 - 提供IDE智能提示和文档

测试自动配置 - 编写单元测试验证配置逻辑

文档和示例 - 提供清晰的使用文档和示例

# 自动配置开发检查清单 ## 1. 配置类设计 - [ ] 使用@AutoConfiguration注解 - [ ] 添加适当的条件注解 - [ ] 提供@ConfigurationProperties类 - [ ] 实现配置的默认值 ## 2. 条件设计 - [ ] @ConditionalOnClass检查必要的类 - [ ] @ConditionalOnMissingBean避免重复配置 - [ ] @ConditionalOnProperty提供开关控制 - [ ] 自定义条件注解(如需要) ## 3. 配置文件 - [ ] 创建spring.factories文件 - [ ] 编写配置元数据JSON - [ ] 提供默认配置示例 - [ ] 文档化所有配置选项 ## 4. 测试验证 - [ ] 单元测试自动配置逻辑 - [ ] 集成测试完整流程 - [ ] 测试不同条件下的行为 - [ ] 验证配置属性绑定 ## 5. 文档和发布 - [ ] 编写README文档 - [ ] 提供使用示例 - [ ] 版本兼容性说明 - [ ] 发布到Maven仓库 # 示例项目结构 custom-service-spring-boot-starter/ ├── src/main/java/ │ └── cn/bugstack/springframework/boot/autoconfig/ │ ├── CustomService.java │ ├── CustomServiceProperties.java │ ├── CustomServiceAutoConfiguration.java │ └── condition/ │ └── OnCustomServiceCondition.java ├── src/main/resources/ │ └── META-INF/ │ ├── spring.factories │ └── spring-configuration-metadata.json ├── src/test/java/ │ └── cn/bugstack/springframework/boot/autoconfig/ │ └── CustomServiceAutoConfigurationTest.java ├── README.md └── pom.xml

🎉 恭喜完成第12章学习!

你已经掌握了SpringBoot自动配置的核心技能,接下来学习应用部署和Tomcat配置!

🚀 下一章:SpringBoot Tomcat ⬅️ 返回第11章 🏠 返回课程首页