You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
source-code-hunter/docs/SpringBoot/SpringBoot-自动装配.md

631 lines
17 KiB

# Spring Boot 自动装配
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring-boot](https://github.com/SourceHot/spring-boot-read)
- `org.springframework.boot.autoconfigure.SpringBootApplication`
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
```
## EnableAutoConfiguration
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
```
## AutoConfigurationImportSelector
- 类图
![image-20200320150642022](/images/SpringBoot/image-20200320150642022.png)
## getAutoConfigurationMetadata()
```JAVA
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(
// 加载配置元数据
getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
if (this.autoConfigurationMetadata == null) {
// 加载配置信息
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
}
return this.autoConfigurationMetadata;
}
```
- `org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader#loadMetadata(java.lang.ClassLoader)`
```JAVA
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
// 获取资源路径
Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
: ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while (urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
return loadMetadata(properties);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
}
}
```
![image-20200320160423991](/images/SpringBoot/image-20200320160423991.png)
- `protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";`
注意: 这个文件在**target**编译后的文件夹中
相关 Issues : https://github.com/spring-projects/spring-boot/issues/11282
- 自动装配
`spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories`
该文件内存有:
```
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
```
![image-20200320162835665](/images/SpringBoot/image-20200320162835665.png)
同样找一下redis
![image-20200320163001728](/images/SpringBoot/image-20200320163001728.png)
- 仔细看`org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration`类
先说注解
```JAVA
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
```
### EnableConfigurationProperties
`自动映射一个POJO到Spring Boot配置文件默认是application.properties文件的属性集。`
- `org.springframework.boot.autoconfigure.data.redis.RedisProperties`
- 部分redis配置属性
```JAVA
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
/**
* Database index used by the connection factory.
*/
private int database = 0;
/**
* Connection URL. Overrides host, port, and password. User is ignored. Example:
* redis://user:password@example.com:6379
*/
private String url;
/**
* Redis server host.
*/
private String host = "localhost";
/**
* Login password of the redis server.
*/
private String password;
/**
* Redis server port.
*/
private int port = 6379;
/**
* Whether to enable SSL support.
*/
private boolean ssl;
/**
* Connection timeout.
*/
private Duration timeout;
/**
* Client name to be set on connections with CLIENT SETNAME.
*/
private String clientName;
}
```
- 找到一个我们用相同方式去寻找到别的一些属性处理如`org.springframework.boot.autoconfigure.jdbc.JdbcProperties` 具体展开请各位读者自行了解了
### AnnotationMetadata
回过头继续我们的主要流程
- `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process`
![image-20200320163806852](/images/SpringBoot/image-20200320163806852.png)
再此之前我们看过了`getAutoConfigurationMetadata()`的相关操作
关注 `AnnotationMetadata annotationMetadata` 存储了一些什么
![image-20200320164145286](/images/SpringBoot/image-20200320164145286.png)
这里简单理解
1. mergedAnnotations 类相关的注解信息
2. annotationTypes 在启动类上的注解列表
### getAutoConfigurationEntry
```JAVA
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取注解属性值
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取候选配置信息
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 删除重复配置
configurations = removeDuplicates(configurations);
// 获取 exclude 属性
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 校验 exclude 类
checkExcludedClasses(configurations, exclusions);
// 配置中删除 exclude 的属性值
configurations.removeAll(exclusions);
// 过滤
configurations = filter(configurations, autoConfigurationMetadata);
// 触发自动配置事件
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回
return new AutoConfigurationEntry(configurations, exclusions);
}
```
### getAttributes
```java
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
// name = org.springframework.boot.autoconfigure.EnableAutoConfiguration , 这是一个固定的值
String name = getAnnotationClass().getName();
// 获取注解的属性
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
+ " annotated with " + ClassUtils.getShortName(name) + "?");
return attributes;
}
```
![image-20200320171138431](/images/SpringBoot/image-20200320171138431.png)
### getCandidateConfigurations
- 读取`spring.factories`数据
```java
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 读取 org.springframework.boot.autoconfigure.EnableAutoConfiguration 相关配置
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
```
![image-20200320171734270](/images/SpringBoot/image-20200320171734270.png)
- 第一个是我自己写的一个测试用
```properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.sourcehot.service.HelloServiceAutoConfiguration
```
### removeDuplicates
- new 两个对象直接做数据转换,去重
```JAVA
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
```
### getExclusions
```JAVA
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
// 获取属性 exclude 值转换成list
excluded.addAll(asList(attributes, "exclude"));
// 获取属性 excludeName 值转换成list
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
// 获取 SpringBoot 本身的忽略配置属性
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
```
### getExcludeAutoConfigurationsProperty
```JAVA
private List<String> getExcludeAutoConfigurationsProperty() {
if (getEnvironment() instanceof ConfigurableEnvironment) {
Binder binder = Binder.get(getEnvironment());
// 取出 "spring.autoconfigure.exclude" 转换成list
return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList)
.orElse(Collections.emptyList());
}
String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}
```
![image-20200323080611527](/images/SpringBoot/image-20200323080611527.png)
- 修改启动类
```JAVA
@SpringBootApplication(excludeName = { "org.sourcehot.service.HelloServiceAutoConfiguration" })
```
![image-20200323081009823](/images/SpringBoot/image-20200323081009823.png)
### checkExcludedClasses
```JAVA
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
for (String exclusion : exclusions) {
//
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
if (!invalidExcludes.isEmpty()) {
// 处理忽略的类
handleInvalidExcludes(invalidExcludes);
}
}
```
- `configurations.removeAll(exclusions)`
移除忽略的类
### filter
```JAVA
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
// 获取 AutoConfigurationImportFilter 相关配置
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
// 执行 aware 相关接口
invokeAwareMethods(filter);
// 比较
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return new ArrayList<>(result);
}
```
- `getAutoConfigurationImportFilters()` 从`spring.factories` 获取 `AutoConfigurationImportFilter`的接口
![image-20200323081903145](/images/SpringBoot/image-20200323081903145.png)
- 循环内执行`Aware`系列接口
`match`方法: `org.springframework.boot.autoconfigure.AutoConfigurationImportFilter#match`
- `filter.match(candidates, autoConfigurationMetadata)` 比较判断哪些是需要自动注入的类
![image-20200323082553595](/images/SpringBoot/image-20200323082553595.png)
### fireAutoConfigurationImportEvents
```JAVA
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
// 获取自动配置的监听器列表
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
// 创建 自动配置事件
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
// 执行 Aware 相关接口
invokeAwareMethods(listener);
// 监听器执行自动配置事件
listener.onAutoConfigurationImportEvent(event);
}
}
}
```
![image-20200323083149737](/images/SpringBoot/image-20200323083149737.png)
- `AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);`
![image-20200323083247061](/images/SpringBoot/image-20200323083247061.png)
- `org.springframework.boot.autoconfigure.AutoConfigurationImportListener#onAutoConfigurationImportEvent` 在执行自动配置时触发 , 实现类只有 **`ConditionEvaluationReportAutoConfigurationImportListener`**
```JAVA
@Override
public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
if (this.beanFactory != null) {
ConditionEvaluationReport report = ConditionEvaluationReport.get(this.beanFactory);
// 记录需要加载的配置
report.recordEvaluationCandidates(event.getCandidateConfigurations());
// 记录不需要加载的配置
report.recordExclusions(event.getExclusions());
}
}
```
![image-20200323083656670](/images/SpringBoot/image-20200323083656670.png)
- 初始化完
## process
- `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process`
![image-20200323084922159](/images/SpringBoot/image-20200323084922159.png)
- 后续的一些行为相对简单,直接放个源码了.
```JAVA
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
// 自动装配信息
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(
// 加载配置元数据
getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
// 循环需要自动注入的类
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
// 继续放入k,v
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
```
## selectImports
```java
@Override
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
// 获取忽略的类
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
// 获取需要注入的类
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
// 把不需要自动注入的类从需要注入的类中移除
processedConfigurations.removeAll(allExclusions);
// 排序
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
```
后续由spring进行不再继续跟踪