文章目录
SpringBoot中的spi机制(1): 一个Demo了解什么是spi中,通过demo了解了spi机制的应用,本文将学习其原理。
1 @EnableAutoConfiguration
在springBoot的启动类的复合注解@SpringBootApplication,主要包括图中三个注解
这里只说@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
@EnableAutoConfiguration 的 主 要 作 用 其 实 就 是 帮 助springboot 应用把所有符合条件的@Configuration 配置都加载到当前 SpringBoot 创建并使用的IOC容器中。
2 @Import(AutoConfigurationImportSelector.class)
@Import的作用就是把多个容器配置合并到一个配置中。里面的值可以是三大类:
- 普通bean或者带@Configuration的bean进行注入
- 实现ImportSlector接口进行动态注入
- 实现ImportBeanDefinitionRegistrar接口进行动态注入
AutoConfigurationImportSelector就是上面的第二类,接下来开始看源码。
3 AutoConfigurationImportSelector
简单概述下流程:
先扫描spring-autoconfigure-metadata.properties文件,再扫描spring.factories,然后用前者过滤后者数据,最后返回需要注入的类的全路径数组。
为什么要过滤呢? 原因是很多的@Configuration 其实是依托于其他的框架来加载的,如果当前的 classpath 环境下没有相关联的依赖,则意味着这些类没必要进行加载,所以,通过这种条件过滤可以
有效的减少@configuration 类的数量从而降低SpringBoot 的启动时间。
3.1 AutoConfigurationImportSelector#selectImports
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
//返回需要加载的类路径数组
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
3.2 META-INF/spring-autoconfigure-metadata.properties
protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
可以看到加载META-INF/spring-autoconfigure-metadata.properties文件。
3.3 META-INF/spring.factories
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//获取注解里的值,比如exclue、exclueName
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取所有Spring.factories的类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//去重
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//根据spring-autoconfigure-metadata.properties里的条件过滤spring.factories里扫描到的类
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
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;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
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 ?
//META-INF/spring.factories
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
//dosomething ......
//..................
}