上一篇文章中提到SpringBoot中实现自动配置时,用到了SPI机制。不知道会不会有有心人去看看我推荐的那篇博文。本篇文章将从代码的层次深入解读Springboot的SPI机制。
首先,是一个很重要的注解@EnableAutoConfiguration,它的源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
Class>[] exclude() default {};
String[] excludeName() default {};
}
@EnableAutoConfiguration注解就是SpringBoot实现自动配置的关键了。因为注解在底层会被翻译为接口,继承注解本质上等同于继承接口,所以@SpringBootApplication注解继承了@EnableAutoConfiguration注解后,就有了@EnableAutoConfiguration注解的能力。
我们再来说说@Import注解,上篇文章提到了它的作用机制。这篇文章中又要用到它的特性了。我们来看看EnableAutoConfigurationImportSelector类的部分源码:
public class EnableAutoConfigurationImportSelector implements DeferredImportSelector,
BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware {
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
@Override
public String[] selectImports(AnnotationMetadata metadata) {
try {
AnnotationAttributes attributes = getAttributes(metadata);
List configurations = getCandidateConfigurations(metadata,
attributes);
configurations = removeDuplicates(configurations);
Set exclusions = getExclusions(metadata, attributes);
configurations.removeAll(exclusions);
configurations = sort(configurations);
recordWithConditionEvaluationReport(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}
这是精简了很大篇幅代码后的部分源码(其他部分多数都是被此方法调用的方法)。我们不知道它是干嘛的,但是可以知道是谁调用了它,我们来看看调用关系:
已经很说明问题了吧?在Spring容器启动过程中,会通过invokeBeanFactoryPostProcessors()方法执行很多BeanFactoryPostProcessors()方法来完成BeanFactory的初始化。在这个过程中,EnableAutoConfigurationImportSelector类的selectImports()方法也被调用了。
接下来,我们来看看selectImports()方法都做了些什么?EnableAutoConfigurationImportSelector类导入了一个这样的包:
import org.springframework.core.io.support.SpringFactoriesLoader;
我们看看它的源码(精简):
public abstract class SpringFactoriesLoader {
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List loadFactories(Class factoryClass, ClassLoader classLoader) {
}
public static List loadFactoryNames(Class> factoryClass, ClassLoader classLoader) {
}
}
这个类我们有利于我们继续往下找线索的代码是这一行:
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
有了这一行代码,我们大致就可以猜出来这个SpringFactoriesLoader类大概是干嘛的了吧?它的两个核心方法一个是用来寻找spring.factories文件中的Factory名称的,一个是用来寻找类的。我们再来看看EnableAutoConfigurationImportSelector类中是怎样使用SpringFactoriesLoader类的,以一段代码如下:
protected List getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
return SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
}
我们现在再来看看selectImports()方法:
public String[] selectImports(AnnotationMetadata metadata) {
try {
AnnotationAttributes attributes = getAttributes(metadata);
//从spring.factories文件中找出所有配置好的factory的名称
List configurations = getCandidateConfigurations(metadata,
attributes);
//去重
configurations = removeDuplicates(configurations);
//分析所有需要排除掉的factory类(EnableAutoConfiguration注解中配置的)
Set exclusions = getExclusions(metadata, attributes);
//移除所有需要过滤放入factory类
configurations.removeAll(exclusions);
//排序
configurations = sort(configurations);
//记录
recordWithConditionEvaluationReport(configurations, exclusions);
//数组形式返回
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}