SpringBoot自动装配简述

​ 最近在学习SpringBoot的相关技术,因为还没有很认真的阅读源码,所以对于自动装配进行一个简单的总结。

xxx-spring-boot-starter自动装配

springbootspring最大的区别就是springboot的开箱即用,直接在pom文件中引入对应的依赖启动就可以了,比如引入springmvc我们可以引入spring-boot-starter-web来一起和springmvc相关的一些依赖,包括我们使用文件上传的commons-io等等,就不用像spring中一个一个的引入依赖了。

​ 那么这种不用配置xml并且自动装配的原理又是什么呢?在SpringBoot中,有一种自动装配的过程,是通过在文件classpath:/META-INF/spring.factories 中添加配置org.springframework.boot.autoconfigure.EnableAutoConfiguration 的方式来配置Bean。换句话说,我们引入的依赖中会在对应文件classpath:/META-INF/spring.factories 中添加配置org.springframework.boot.autoconfigure.EnableAutoConfiguration,SpringBoot会根据这个配置里面的字符串(包的全路径名)来反射,创建Bean

​ 自动装配,还得是从我们的入口来看,我们进入SpringBootApplication注解。

​ 再进入EnableAutoConfiguration注解。

​ 可以看见这里面我们import了一个Bean叫做AutoConfigurationImportSelector

​ 那么我们直接看EnableAutoConfiguration 功能实现的类org.springframework.boot.autoconfigure.AutoConfigurationImportSelector , AutoConfigurationImportSelector 依赖于方法selectImports 实现,方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取候选配置类,将准备加载成为Bean的类添加到configurations集合中
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// 移除重复的类
configurations = removeDuplicates(configurations);
// 根据类的配置,获取需要排除的类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 根据AutoConfigurationImportFilter 过滤类,过滤掉无需加载的类
configurations = filter(configurations, autoConfigurationMetadata);
// 触发自动导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回待加载的Bean的字符串数组
return StringUtils.toStringArray(configurations);
}

​ 后面的都可以忽略,最主要的就是List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);这段代码,这段代码用来获取我们所有的/META-INF/spring.factorieskey值为org.springframework.boot.autoconfigure.EnableAutoConfiguration包全路径名的List

​ 进一步分析getCandidateConfigurations 方法。

1
2
3
4
5
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}

​ 这里看第3行代码,可以关联到上小结提到的SpringFactoriesLoader 类,类SpringFactoriesLoader#loadFactoryNames 方法定义如下:这里面我们传入的factoryType就是org.springframework.boot.autoconfigure.EnableAutoConfiguration

1
2
3
4
5
6
7
8
9
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}

String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

​ 继续深挖loadSpringFactories方法:我们可以看见这里面就是读取META-INF/spring.factories路径的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
Map<String, List<String>> result = new HashMap();

try {
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");

while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();

while(var6.hasNext()) {
Map.Entry<?, ?> entry = (Map.Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;

for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}

result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}

classLoader.getResources("META-INF/spring.factories");这个是获取所有的spring.factories