SpringBootApplication
springboot项目一般都会有一个主程序入口,使用@SpringBootApplication注解标注:1
2
3
4
5
6
7
public class SpringbootdemoApplication implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
点进@SpringBootApplication,可以看到又引入了其他的注解,主要关注以下几个注解:
- @SpringBootConfiguration
- @ComponentScan
- @EnableAutoConfiguration
1 |
|
@SpringBootConfiguration
@SpringBootConfiguration比较简单,它只导入了一个@Configuration注解,所以可以理解为@SpringBootConfiguration就是一个配置类。
1 | ({ElementType.TYPE}) |
@ComponentScan
使用过Spring的应该都很熟悉了,这个注解用来配置扫描包里的文件,将符合条件的bean注册到Spring容器中。
excludeFilters:配置不进行扫描的过滤器。
1 | ( |
@EnableAutoConfiguration
@EnableAutoConfiguration是SpringBoot自动装配的核心,它由@AutoConfigurationPackage和@Import组成,@Import导入了AutoConfigurationImportSelector类:
1 | @Target({ElementType.TYPE}) |
@AutoConfigurationPackages
1 | ({ElementType.TYPE}) |
AutoConfigurationPackages表示使用该注解的类所在的包会被加入到自动扫描package中,也就是SpringBoot会将主程序类所在的包及其下面的所有子包下的组件扫描到Spring容器中,主要是通过导入Registrar类实现的:
1 | static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { |
AutoConfigurationImportSelector
在AutoConfigurationImportSelector的selectImports方法中,调用了getAutoConfigurationEntry方法,在getAutoConfigurationEntry又调用了getCandidateConfigurations,获取所有候选的配置:
1 | public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { |
在getCandidateConfigurations中,有一步是调用loadFactoryNames,并且传入了两个参数,其中有一个是通过getSpringFactoriesLoaderFactoryClass返回的EnableAutoConfiguration类,另外一个传入的是类的加载器:1
2
3
4
5
6
7
8
9// 返回EnableAutoConfiguration类
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
// 返回类的加载器
protected ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}
再进入到SpringFactoriesLoader的loadFactoryNames方法,主要的处理在loadSpringFactories方法中,在这个方法中,先通过给定的类的加载器从缓存中获取对应的自动配置类,如果不为空直接返回接口,如果为空,根据指定的类加载器加载META-INF下的spring.factories文件(使用了SPI机制),然后将内容解析放到Map中,并加入到缓存中,之后就可以根据类的加载器名称获取对应自动配置类,加载到Spring容器中: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
61
62public final class SpringFactoriesLoader {
......
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());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 根据当前的类加载器,从缓存中获取数据
Map<String, List<String>> result = (Map)cache.get(classLoader);
if(result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
// 通过给定的类加载器从META-INF下的spring.factories文件
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
// 解析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()) {
Entry<?, ?> entry = (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) {
// 将spring.factories文件的内容解析放入到result中
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));
});
// 将result的内容加入到缓存中
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
}
选取了spring-boot-autoconfiguer下的spring.factories文件内容,key为接口名称,value为对应的自动装配类: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# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# 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,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
回到getCandidateConfigurations方法中,调用loadFactoryNames方法时传入的类为EnableAutoConfiguration类,所以loadFactoryNames最终可以根据传入的类加载器,从缓存中获取EnableAutoConfiguration在spring.factories中对应的自动配置类(如果缓存中没有获取到,就去加载spring.factories文件并解析后放入缓存),之后将自动配置类导入Spring容器,自动配置类生效,完成自动装配。
参考:
掘金小册-SpringBoot 源码解读与原理分析
SpringBoot自动装配原理解析
SpringBoot自动装配原理初探
SpringBoot自动配置的原理详解
SpringBoot 2.X课程学习 | 第三篇:自动配置(Auto-configuration)
SpringBoot版本:2.4.1