📋 学习目标
- 自动配置原理 - 理解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绑定配置属性
🎯 自动配置实现
// 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