自动装配是Spring Boot的核心特性,理解其原理能帮助你更好地使用和定制Spring Boot。
本文深入剖析Spring Boot自动装配的实现原理、源码分析和实战应用,帮助你掌握Spring Boot的核心机制。
自动装配原理:
1. @EnableAutoConfiguration 触发自动配置
2. 扫描 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
3. 根据条件注解(@Conditional)判断是否加载配置类
4. 自动注册Bean到Spring容器
Spring Boot启动流程:
@SpringBootApplication
↓
@EnableAutoConfiguration
↓
@Import(AutoConfigurationImportSelector.class)
↓
spring.factories / AutoConfiguration.imports
↓
条件过滤(@Conditional)
↓
注册BeanDefinition
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
public @interface SpringBootApplication {
// ...
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
}
public class AutoConfigurationImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取自动配置类
AutoConfigurationEntry autoConfigurationEntry =
getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 获取排除的配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 移除排除的配置
configurations.removeAll(exclusions);
// 过滤(根据条件注解)
configurations = getConfigurationClassFilter().filter(configurations);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
| 注解 | 条件 | 示例 |
|---|
| @ConditionalOnClass | 类路径存在指定类 | DataSource.class |
| @ConditionalOnMissingBean | 容器中不存在指定Bean | DataSource.class |
| @ConditionalOnProperty | 配置属性满足条件 | spring.datasource.url |
| @ConditionalOnWebApplication | Web应用 | - |
| @ConditionalOnResource | 资源文件存在 | classpath:application.yml |
@AutoConfiguration
@ConditionalOnClass(DataSource.class)
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DataSourceProperties properties) {
return DataSourceBuilder.create()
.url(properties.getUrl())
.username(properties.getUsername())
.password(properties.getPassword())
.build();
}
}
// 1. 创建配置属性类
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
private String endpoint;
private int timeout = 3000;
private boolean enabled = true;
// getters/setters
}
// 2. 创建自动配置类
@AutoConfiguration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyServiceProperties properties) {
return new MyService(properties.getEndpoint(), properties.getTimeout());
}
}
// 3. 注册自动配置
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.autoconfigure.MyServiceAutoConfiguration
@AutoConfiguration
@ConditionalOnClass(Redis.class)
@ConditionalOnProperty(prefix = "spring.redis", name = "host")
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
return template;
}
}
# 启动时查看自动配置报告
java -jar app.jar --debug
# 或在application.yml中配置
debug: true
# 输出示例:
# DataSourceAutoConfiguration matched:
# - @ConditionalOnClass found required class 'javax.sql.DataSource'
# - @ConditionalOnProperty (spring.datasource.url) matched
# RedisAutoConfiguration did not match:
# - @ConditionalOnClass did not find required class 'redis.clients.jedis.Jedis'
// 方式1:注解排除
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {
// ...
}
// 方式2:配置排除
spring.autoconfigure.exclude=com.example.autoconfigure.MyServiceAutoConfiguration
// 1. 创建autoconfigure模块
// 2. 创建starter模块(空模块,依赖autoconfigure)
// autoconfigure模块结构:
my-spring-boot-starter-autoconfigure/
├── src/main/java/
│ └── com/example/autoconfigure/
│ ├── MyServiceProperties.java
│ ├── MyServiceAutoConfiguration.java
│ └── MyService.java
└── src/main/resources/
└── META-INF/
└── spring/
└── org.springframework.boot.autoconfigure.AutoConfiguration.imports
// 3. 使用starter
<dependency>
<groupId>com.example</groupId>
<artifactId>my-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
@AutoConfiguration
@ConditionalOnExpression("${my.feature.enabled:false}")
public class FeatureAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public FeatureService featureService() {
return new DefaultFeatureService();
}
}
| 原则 | 说明 |
|---|
| 单一职责 | 一个配置类只做一件事 |
| 条件优先 | 使用条件注解控制加载 |
| 属性绑定 | 使用@ConfigurationProperties |
| 默认值 | 提供合理的默认配置 |
| 问题 | 原因 | 解决方案 |
|---|
| 配置不生效 | 条件不满足 | 检查条件注解 |
| Bean冲突 | 多个配置类 | 使用@ConditionalOnMissingBean |
| 属性未绑定 | 前缀错误 | 检查@ConfigurationProperties |