Spring Boot 自动装配
理论基础
从启动类上的 @SpringBootApplication
开始。
@EnableAutoConfiguration
-> @Import(AutoConfigurationImportSelector.class)
。
查看 AutoConfigurationImportSelector#getAutoConfigurationEntry
// 导入配置类
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 导入 spring.factories 里的配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 移除重复的配置类
configurations = removeDuplicates(configurations);
// 获取 exclude 的配置类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
// 移除 exclude 的配置类
configurations.removeAll(exclusions);
// 过滤配置类
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
- Spring 会将
META-INF/spring.factories
下的配置类注册到容器中(参考 SPI) - Spring 会根据
META-INF/spring-autoconfigure-metadata.properties
的配置进行过滤 application.properties
的提示来自于META-INF/additional-spring-configuration-metadata.json
断点调试
导入配置类
一、引入 spring-boot-starter
,下断点 debug 启动
二、查看 spring-boot-autoconfigure.jar
的 spring.factories
三、再引入 mybatis-plus-boot-starter
,查看 mybatis-plus-boot-starter.jar
的 spring.factories
四、再次 debug 启动
127 + 3 = 130 127+3=130 127+3=130 对上
排除配置类
一、到 126 行
没有排除任何配置类
二、再到启动类,排除一些配置类
三、再次调试
四、跳到 128 行,移除完被排除的配置类
从 130 130 130 变到 129 129 129 排除了一个配置类
五、跳到 129 行,过滤完配置类
最后还剩下 31 个
过滤配置类
一、当前是没有引入 spring-boot-starter-data-mongodb
的 。所以自然没有 MongoDataAutoConfiguration
二、查看 spring-boot-autoconfigure.jar
的 spring-autoconfigure-metadata.properties
和 MongoAutoConfiguration
ConditionOnClass 只要存在后面跟着的类,就会加载 AutoConfigureAfter 后面跟着的类
三、创建一个 com.mongodb.client.MongoClient
或 org.springframework.data.mongodb.core.MongoTemplate
四、再次调试,过滤完配置类
有了 MongoDataAutoConfiguration
自动装配流程
从上述可知,读入配置类是通过 getAutoConfigurationEntry
,在这下入断点,反向推出流程。
一、118 行下断点,debug
二、熟悉的 refresh
这就和 Spring 源码连起来了
自定义 starter
创建 user-boot-starter
一、建配置类、属性类、Bean
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({UserProperties.class})
@ConditionalOnClass(User.class)
public class UserAutoConfiguration {
private final UserProperties properties;
public UserAutoConfiguration(UserProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public User user() {
User user = new User();
user.setId(properties.getId());
user.setName(properties.getName());
return user;
}
}
@ConfigurationProperties(prefix = "user.info")
public class UserProperties {
private String name;
private Integer id;
// 省略 getter/setter、toString
}
public class User {
private String name;
private Integer id;
// 省略 getter/setter、toString
}
二、配置 spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.wang.user.autoconfigure.UserAutoConfiguration
三、配置 spring-autoconfigure-metadata.properties
com.wang.user.autoconfigure.UserAutoConfiguration=
com.wang.user.autoconfigure.UserAutoConfiguration.AutoConfigureAfter=com.wang.user.autoconfigure.UserAutoConfiguration
com.wang.user.autoconfigure.UserAutoConfiguration.ConditionalOnClass=com.wang.user.bean.User
四、配置 additional-spring-configuration-metadata.json
{
"groups": [
{
"sourceType": "com.wang.user.autoconfigure.UserProperties",
"name": "user.info",
"type": "com.wang.user.autoconfigure.UserProperties"
}
],
"properties": [
{
"name": "user.info.id",
"type": "java.lang.Integer",
"description": "用户ID.",
"sourceType": "com.wang.user.autoconfigure.UserProperties"
},
{
"name": "user.info.name",
"type": "java.lang.String",
"description": "用户名.",
"sourceType": "com.wang.user.autoconfigure.UserProperties"
}
],
"hints": []
}
五、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
测试 user-boot-starter
一、测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
@Autowired
private User user;
@Test
public void test01() {
System.out.println(user);
}
}
二、写 application.yml
有提示
三、启动测试类
自动配置成功