本文是《自定义spring boot starter三部曲》系列的终篇,前文中我们开发了一个starter并做了验证,发现关键点在于spring.factories的自动加载能力,让应用只要依赖starter的jar包即可,今天我们来分析Spring和Spring boot源码,了解spring.factories自动加载原理;
本文中涉及到的库的版本:
先回顾customizeservicestarter模块中spring.factories文件的内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.bolingcavalry.customizeservicestarter.CustomizeConfiguration
从上述内容可以确定今天源码学习目标:
private void processDeferredImportSelectors() {
//这里就是Configuration注解中的Import注解的值,
//例如EnableAutoConfiguration注解的源码中,Import注解的值是EnableAutoConfigurationImportSelector.class
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
ConfigurationClass configClass = deferredImport.getConfigurationClass();
try {
//以EnableAutoConfiguration注解为例,其Import注解的值为EnableAutoConfigurationImportSelector.class,
//那么此处就是在调用EnableAutoConfigurationImportSelector的selectImports方法,返回了一个字符串数组
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
//字符串数组中的每个字符串都代表一个类,此处做实例化
processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
}
}
小结一下spring容器配置类的逻辑:
有了上面的结论就可以结合Spring boot的源码来分析加载了哪些数据了;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
......
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
......
/**
* {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration
* auto-configuration}. This class can also be subclassed if a custom variant of
* {@link EnableAutoConfiguration @EnableAutoConfiguration}. is needed.
*
* @deprecated as of 1.5 in favor of {@link AutoConfigurationImportSelector}
* @author Phillip Webb
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Madhura Bhave
* @since 1.3.0
* @see EnableAutoConfiguration
*/
@Deprecated
public class EnableAutoConfigurationImportSelector
extends AutoConfigurationImportSelector {
@Override
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass().equals(EnableAutoConfigurationImportSelector.class)) {
return getEnvironment().getProperty(
EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,
true);
}
return true;
}
}
上述源码有三处重点需要关注:
第一,EnableAutoConfigurationImportSelector是AutoConfigurationImportSelector的子类;
第二,EnableAutoConfigurationImportSelector已经被废弃了,不推荐使用;
第三,文档中已经写明废弃原因:从1.5版本开始,其特性由父类AutoConfigurationImportSelector实现;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
//将所有spring-autoconfigure-metadata.properties文件中的键值对保存在autoConfigurationMetadata中
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//取得所有配置类的名称
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
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;
}
getCandidateConfigurations方法中,调用了静态方法SpringFactoriesLoader.loadFactoryNames,上面提到的SpringFactoriesLoader.loadFactoryNames方法是关键,看看官方文档对此静态方法的描述,如下图红框所示,该方法会在spring.factories文件中寻找指定接口对应的实现类的全名(包名+实现类):
在getCandidateConfigurations方法中,调用SpringFactoriesLoader.loadFactoryNames的时候传入的指定类型是getSpringFactoriesLoaderFactoryClass方法的返回值:
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
现在可以梳理一下了:
现在真相大白了:只要我们在spring.factories文件内配置了EnableAutoConfiguration,那么对于的类就会被实例化后注册到spring容器;
至此,《自定义spring boot starter三部曲》系列就完结了,希望实战加源码分析的三篇文章,能帮助您理解和实现自定义starter这种简单快捷的扩展方式;