SpringBoot 自动装配原理
早期的Spring项目需要添加需要配置繁琐的xml,比如MVC、事务、数据库连接等繁琐的配置。SpringBoot的出现就无需这些繁琐的配置,因为SpringBoot基于约定大于配置的理念,在项目启动时候,将约定的配置类自动配置到IOC容器里。这些都因为SpringBoot有自动配置的特性。
Sping Boot 如何实现自动配置
Spring Boot都需要创建一个mian启动类,而启动类都含有@SpringBootApplication注解,从启动类,一步步探索源码。
@SpringBootApplication注解
Spring Boot 启动类上都有一个 @SpringBootApplication注解:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.runyApplication.class,args);
}
}
@EnableAutoConfiguration注解
@SpringBootApplication 里面有 @EnableAutoConfiguration 的注解:
...
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
...
)
...
public @interface SpringBootApplication {...}
AutoConfigurationImportSelector类
@EnableAutoConfiguration注解导入AutoConfigurationImportSelector类:
...
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
...
}
selectImports()方法
AutoConfigurationImportSelector类找到 selectImports 方法,里面有getAutoConfigurationEntry方法:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// 调用 getAutoConfigurationEntry() 方法
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
...
}
SpringFactoriesLoader.loadFactoryNames() 方法
getAutoConfigurationEntry方法通过SpringFactoriesLoader.loadFactoryNames() 扫描所有含有META-INF/spring.factories的jar包:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...
// getAutoConfigurationEntry() 方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 调用 getCandidateConfigurations() 方法
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
...
}
// AutoConfigurationImportSelector.class
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 调用 SpringFactoriesLoader.loadFactoryNames() 方法
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
// SpringFactoriesLoader.class
// loadFactoryNames() 方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
// 调用 loadSpringFactories() 方法
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
// SpringFactoriesLoader.class
// loadSpringFactories() 方法
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 {
// 扫描所有含有META-INF/spring.factories的jar包
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);
}
}
}
spring-boot-autoconfigure-xxx.jar项目包含META-INF/spring.factories文件,spring.factories是一个键值对的形式,扫描该文件下@EnableAutoConfiguration对应类:
总结
- 自动配置主要由
@EnableAutoConfiguration实现 - 添加了
@EnableAutoConfiguration注解,会导入AutoConfigurationImportSelector类 - 里面的
selectImports方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有含有META-INF/spring.factories的jar包 spring.factories是一个键值对的形式,扫描该文件下@EnableAutoConfiguration对应类:- 将对应
key为@EnableAutoConfiguration注解全名对应的value类全部装配到IOC容器中。
这些属性自动配置到IOC之后就无需自己手动配置bean了,Spring Boot中的约定大于配置理念,约定是将需要的配置以约定的方式添加到IOC容器中。
自动配置生效条件
那是不是spring.factories文件对应的配置都会加载到IOC容器中?
其中有几个注解: @ConditionalOnClass, @ConditionalOnMissingBean
@ConditionalOnClass表示在类路径中存在类才会配置该配置类。只有引入相关依赖才会自动配置该配置类。@ConditionalOnMissingBean表示只有不存在对应的类的bean才会自动配置该类。
所以spring.factories里面并不是所有的bean都会装配到IOC容器中,只会按需配置对应的bean。