AutoConfigurationImportSelector
AutoConfigurationImportSelector 类实现 DeferredImportSelector接口,在项目启动过程中,会自动调用其 selectImports 方法,找出需要自动注入的类信息,下面将对其执行过程进行简要分析。
selectImports
该方法比较简单,主要核心逻辑在 getAutoConfigurationEntry 中。
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());
}
protected boolean isEnabled(AnnotationMetadata metadata) {
// 判断当前类是否是 AutoConfigurationImportSelector
if (getClass() == AutoConfigurationImportSelector.class) {
// 判断 spring.boot.enableautoconfiguration 配置是否为 true
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
getAutoConfigurationEntry
该方法主要是获取自动配置的信息,主要包含需要注入的以及需要排除掉的类信息,主要过程如下:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
// 判断是否需要进行自动注入
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取元数据的参数
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取候选需要注入的类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 类去重, 通过 Set 实现
configurations = removeDuplicates(configurations);
// 获取需要排除不加载的类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// exclusions 类进行检查
checkExcludedClasses(configurations, exclusions);
// 从 configurations 中去除掉不需要加载的类
configurations.removeAll(exclusions);
// configurations 通过过滤器进行过滤,只留下符合要求的类
configurations = filter(configurations, autoConfigurationMetadata);
// 发布自动注入的消息事件,有监听器对其事件进行处理
fireAutoConfigurationImportEvents(configurations, exclusions);
// 新建AutoConfigurationEntry对象返回
return new AutoConfigurationEntry(configurations, exclusions);
}
1、getAttributes
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
// getAnnotationClass() 返回的内容为 EnableAutoConfiguration.class
String name = getAnnotationClass().getName();
// 获取注解里的参数信息
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
+ " annotated with " + ClassUtils.getShortName(name) + "?");
return attributes;
}
此方法主要获取参数的信息。
2、getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 通过SpringFactoriesLoader加载出需要自动导入的类信息
// getSpringFactoriesLoaderFactoryClass() 返回的是 EnableAutoConfiguration.class
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;
}
简要来说,configurations 主要是通过 SpringFactoriesLoader 所加载出来的,具体的原理详见文章~
3、getExclusions
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
// 获取 exclude 配置的类
excluded.addAll(asList(attributes, "exclude"));
// 获取 excludeName 配置的类
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
// 获取 spring.autoconfigure.exclude 配置的类
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
此方法获取不需要进行自动注入的类,通过提取注解里所配置的类以及配置文件里面所配置的类。
4、checkExcludedClasses
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
// 遍历所有排除的类
for (String exclusion : exclusions) {
// 如果类能被加载,但是候选类里面不包含此类 将其加入到无效的集合中
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
// 如果存在无效类,则抛出异常 IllegalStateException
if (!invalidExcludes.isEmpty()) {
handleInvalidExcludes(invalidExcludes);
}
}
此方法主要是检查排除的类是否合理,如果存在不满足要求的类,则抛出异常。
5、filter
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
// skip 用来记录是否有需要跳过不加载
boolean[] skip = new boolean[candidates.length];
// skipped 用来记录是否有类不需要被加载
boolean skipped = false;
// getAutoConfigurationImportFilters()该方法是获取自动注入的过滤器
// 同样是通过 SpringFactoriesLoader 进行加载
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
// 对过滤器进行一些初始赋值设置
invokeAwareMethods(filter);
// 过滤器对每个候选类进行匹配,match 用来记录匹配结果
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
// 如果都需要被加载,则可以直接返回 configurations
if (!skipped) {
return configurations;
}
// 否则需要将不需要加载的类剔除
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return new ArrayList<>(result);
}
此方法主要是通过 SpringFactoriesLoader 对过滤器进行加载,通过过滤器对候选的类进行下一步的过滤。我们知道Bean的注入我们可以添加一些条件,比如说 @Condition 注解 ,这里过滤器就是对每个候选类的注入条件进行检查,将不满足要求的类进行排除。
6、fireAutoConfigurationImportEvents
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
// 获取监听器,这里同样是通过 SpringFactoriesLoader 进行加载
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
// 新建一个 AutoConfigurationImportEvent 事件
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
// 遍历每个监听器,如果能够处理该事件类型,则会对其进行响应
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
此方法主要用到的是监听器,通过发布事件,让对应能够监听该事件类型的监听器对其进行响应,额外增加一些操作。
7、new AutoConfigurationEntry(configurations, exclusions)
最后是新建一个 AutoConfigurationEntry 对象,这里包含需要主动注入的类和需要排除的类,在 selectImports 方法的最后,也是将 configurations 提出,转成数组进行返回,该数组记录的就是经过筛选后的需要导入的类的全限定名。
总结
整个自动导入的过程,回过头来看的话,其实并不是很复杂,但是很值得学习。有些方法看似很简单,就是获取某某某内容,但是去看如何去获取,其实又是一个新的知识点。在学习的过程中,其实有很多不明白的概念,也是希望多查,尽量不要留下不明白的知识点,茅塞顿开的感觉才是最开心的点。以下是我觉得需要进一步学习的内容,本文包含一部分内容的链接,当然源码更需要的是自己去理解,别人的内容都是仅供参考。
1、SpringFactoriesLoader.loadFactoryNames 方法,看一下它是如果找到我们要加载的类信息;
2、了解监听器的概念,发布事件、监听器响应等过程,其中 SpringBoot在启动过程中就有许多发布事件的过程,可以仔细学习;
3、过滤器如果对候选类进行筛选;
4、SpringBoot在启动过程中,何时去调用 selectImports 方法。