springboot的自动配置是她的核心之一,为我们简化配置文件起到了主要作用。本文就简单的探索下springboot的自动配置原理,欢迎大家评论与探讨。
1、SpringBoot启动的时候会加载主配置类,开启了自动配置功能 @EnableAutoConfiguration
这里不妨点开这个注解,看看其内部实现:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
2、@EnableAutoConfiguration 作用:
利用EnableAutoConfigurationImportSelector给容器中导入一些组件
点开这个导入选择器可以查看selectImports()方法的内容;
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
这里面主要涉及到了两个方法
1.AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader):
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
}
//继续看loadMetadata()方法
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while(urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource((URL)urls.nextElement())));
}
return loadMetadata(properties);
} catch (IOException var4) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", var4);
}
}
static AutoConfigurationMetadata loadMetadata(Properties properties) {
return new AutoConfigurationMetadataLoader.PropertiesAutoConfigurationMetadata(properties);
}
从源码可以看出该方法是为了扫描所有jar包类路径下 META‐INF/spring-autoconfigure-metadata.properties,并获取文件中的值将其封装为AutoConfigurationMetadata对象返回,这个文件中记录了相关配置类的加载条件,符合条件的配置类才会被允许加载。
若不满足,则该类是会从自动配置类列表中排除,这样能加快springboot的启动速度。spring官方文档(https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html)对该文件的作用描述如下:
Spring Boot uses an annotation processor to collect the conditions on auto-configurations in a metadata file (META-INF/spring-autoconfigure-metadata.properties). If that file is present, it is used to eagerly filter auto-configurations that do not match, which will improve startup time.
然后根据匹配结果,将真正满足配置条件的配置类放入list集合中 boolean[] skip记录了对应的候选自动配置类是否需要跳过,true-不满足条件,需要跳过,false-满足条件,不需要跳过。
2.getAutoConfigurationEntry,
我们来看看这个方法的实现:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
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.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
该方法将第一步的AutoConfigurationMetadata的对象作为参数,核心就是返回一个configurations ,而这个是通过getCandidateConfigurations方法得到的,下面看看这个方法的实现:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
我们可以看到这个方法调用了SpringFactoriesLoader.loadFactoryNames的静态方法:
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
}
可以看到,方法2就是扫描META-INF/spring.factories(spring-boot-autoconfigure-1.5.9.RELEASE-sources.jar)文件并获取自动配置类key=EnableAutoConfiguration.class的配置就这样找到对应的要加载的配置类。
总的来说,归为以下几点:
1、读取META-INF/spring-autoconfigure-metadata.properties文件中的配置类的条件;
2、获取需要排除的自动配置类;
3、读取spring-boot-autoconfigure.RELEASE.jar中的META-INF/spring.factories文件内容,作为候选自动配置类;
4、对候选自动配置类进行去重、排序、去除所有排除项;
5、利用META-INF/spring-autoconfigure-metadata.properties文件的配置对META-INF/spring.factories经历第四步处理后的候选自动配置类进行过滤,去除不满足加载条件的类,得到最终的自动配置类供SpringBoot加载。