Spring Boot 2系列(四十一):源码分析自动配置实现及手写自动配置
Spring Boot 的一大特性是基于 习惯优与配置 原则为很多组件提供了 自动配置 ,这个强大的特性可以快速将其它功能整合,个人认为这是促成该框架流行的主要原因,特别适合互联网项目的分布式开发(基于业务的单一职责原则),开发可以将更多精力集中在业务上,而不是配置上。
Spring Boot自动配置的源码在spring-boot-starter-2.0.0.RELEASE.jar
的依赖包spring-boot-autoconfigure-2.0.0.RELEASE.jar
里;若需要了解SpringBoot为我们做了哪些自动配置,可以看这个包里的源码。
原理分析
autoconfigure-xx.jar
查看当前项目已启用和未用启的自动配置报告,在application.properties
文件添加:debug=true
,Positive matches是已启动的,Negative matches是未启动的。
自动配置
Spring Boot自动配置功能分析需要从入口类的@SpringBootApplication
注解开始,该注解是一个组合注解,组合了核心注解@EnableAutoConfiguration
,自动配置功能是由@EnableAutoConfiguration
注解提供的。
- @EnableAutoConfiguration 注解,该注解是自动配置的入口。 该注解导入了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}AutoConfigurationImportSelector
类,该类可理解为自动配置选择器,是核心类。 - AutoConfigurationImportSelector 自动配置选择器 通过
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
40public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);//1
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());//2
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;
}
}SpringFactoriesLoader.loadFactoryNames()
方法从类路径或系统路径下读取用于自动配置资源文件META-INF/spring.factories
, 并将其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项通过反射实例化为对应的标注了 @Configuration 的 JavaConfig 形式的 IoC 容器配置类,并汇总为一个加载到 IoC 容器。 - loadFactoryNames()方法, 当找到
spring.factories
文件后,SpringFactoriesLoader
将查询配置文件命名的属性。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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61public abstract class SpringFactoriesLoader {
//包根路径下 META-INF/spring.factories 文件
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
public static <T> List<T> loadFactories(Class<T> factoryClass, { ClassLoader classLoader)
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List<T> result = new ArrayList<>(factoryNames.size());
for (String factoryName : factoryNames) {
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, { ClassLoader classLoader)
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories( { ClassLoader classLoader)
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null)
return result;
try {
//加载文件
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
} - spring.factories
spring.factories
文件声明了有哪些自动配置。
1 | # Initializers |
自定义自动配置
- 在元文件里创建
src/main/resources/META-INF/spring.factories
文件,添加开启自动配置并指向自动配置的类。
该文件会被SpringBoot自动配置扫描到并加载指定的配置类。 - 注册配置类:
- 注册为Bean
- 开启配置属性,指定业务属性类
- 按条件指定业务类
- 注入业务属性类对象
- 创建业务类对象,并注册为Bean,在创建业务对象的方法中调用业务属性对象加载属性参数
- 将此自定义的自动配置安装到 Maven 类,其它项目引入添加该依赖,并配置属性。
项目1:自动配置Jar
**需求:**一个自动配置的项目,被其它主项目引入直接调用;自动配置项目中有业务需要执行,业务参数需要从配置文件中读取,而该参数是在主项目中配置,只要在主项目中按特定的格式配置好参数,自动配置项目就可以获取得并执行。
- 创建业务类
1 | public class HelloService { |
- 创建绑定参数类
1 | package Properties; |
- 创建自动配置类
1 | package com.springboot.AutoConfiguration; |
- 创建自动配置文件
src/main/resources/META-INF/spring.factories
,在文件中输入开启自动配置和指定配置的类的参数。
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
- 把自动配置项目通过
mvn install
安装到maven
库。
项目2: 调用自动配置
- 添加自动配置jar为依赖, 在业务层注入自动配jar中的业务类。
1 |
|
- 在
application.properties
添加参数:hello.msg=Hello Word
。
可以看到,项目2在注入HelloService
之前,没在该项目其它地方声明为Bean
; 声明Bean
是在项目1中通过自动配置实现的。
关闭相关配置
1 |
|
其它参考
这篇文章写的非常不错:这样讲 SpringBoot 自动配置原理,你应该能明白了吧
Spring Boot 2系列(四十一):源码分析自动配置实现及手写自动配置
http://blog.gxitsky.com/2019/04/27/SpringBoot-41-autoConfig-explain/