文章目录
原理概述
通过注解@SpringBootApplication 引入注解@EnableAutoConfiguration, @EnableAutoConfiguration的作用是:将 classpath目录下的META-INF/spring.factories文件中的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration所有的类加载到Spring的ioc容器中,从而实现在动装配提示:以下是本篇文章正文内容,下面案例可供参考
一、@EnableAutoConfiguration说明
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
....
}
以上代码可以看出@EnableAutoConfiguration由@AutoConfigurationPackage、@Import 两个注解组成,这里最为关键的是@Import(AutoConfigurationImportSelector.class),借助AutoConfigurationImportSelector可以将所有满足条件的
@Configuration配置类都加载到当前SpringBoot的IoC容器中
二、AutoConfigurationImportSelector说明
温馨提示:所有实现ImportSelector的类,都会在启动时被ConfigurationClassParser中的processImports进行实例化,并执行selectImports方法。## selectImports 从AutoConfigurationImportSelector代码中可以看到selectImports方法,该方法获取
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
getAutoConfigurationEntry 方法
从上述代码再进入getAutoConfigurationEntry 方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取注解的属性值
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取所有候选配置类EnableAutoConfiguration
// 使用了内部工具使用SpringFactoriesLoader,查找classpath上所有jar包中的
// META-INF\spring.factories,找出其中key为
// org.springframework.boot.autoconfigure.EnableAutoConfiguration
// 的属性定义的工厂类名称。
// 虽然参数有annotationMetadata,attributes,但在 AutoConfigurationImportSelector 的
// 实现 getCandidateConfigurations()中,这两个参数并未使用
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 应用过滤器AutoConfigurationImportFilter,
// 对于 spring boot autoconfigure,定义了一个需要被应用的过滤器 :
// org.springframework.boot.autoconfigure.condition.OnClassCondition,
// 此过滤器检查候选配置类上的注解@ConditionalOnClass,如果要求的类在classpath
// 中不存在,则这个候选配置类会被排除掉
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
getCandidateConfigurations
从上述代码再进入getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
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;
}
SpringFactoriesLoader.loadFactoryNames() 方法
其中常量 FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”; 先从缓存cache中获取,当为空时再去META-INF/spring.factories中加载(由于在SpringApplication 实例化时,会加载监听器、初始化器,而加载这些时会去加载spring.factories,此时cache不会为空)
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(@Nullable 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()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
参考
https://www.despairyoke.com/2018/12/05/2018/2018-12-05-spring-boot-factories/