springboot启动过程中,自动注册配置的初始化顺序可以通过@AutoConfigureBefore @AutoConfigureAfter 实现。比如Mybatis自动配置需要在DataSource 自动配置之后初始化,AutoConfigureAfter 是如何实现的
@AutoConfigureAfter 使用场景
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {}
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
public class ErrorMvcAutoConfiguration {}
AutoConfigureAfter 原理
解释:
- AutoConfigureAfter 声明 当前配置应该在 指定配置之后初始化
- AutoConfigureBefore 声明 当前配置应该在 指定配置之前初始化
- 一种宽泛的顺序指定,代码中会有算法实现,AutoConfigureBefore 逻辑稍微有些绕
实现关键类:org.springframework.boot.autoconfigure.AutoConfigurationSorter
上下文:@EnableAutoConfiguration 的实现过程中,会选择适合的auto-configuration 配置项,如上述的 DataSourceAutoConfiguration 和 MybatisAutoConfiguration ,筛选完毕后会进行排序,排序首先根据order,然后会根据指定的@AutoConfigureBefore @AutoConfigureAfter 再次调整先后顺序,最后根据此顺序进行初始化。
AutoConfigureAfter 实现
排序过程源码如下:
// AutoConfigurationSorter#getInPriorityOrder
public List<String> getInPriorityOrder(Collection<String> classNames) {
List<String> orderedClassNames = new ArrayList<String>(classNames);
// Initially sort alphabetically
Collections.sort(orderedClassNames);
// Then sort by order
Collections.sort(orderedClassNames, new Comparator<String>() {
//...
});
// Then respect @AutoConfigureBefore @AutoConfigureAfter
// sortByAnnotation 方法是的排序的实现。一个LinkedHashSet 存储和调整顺序,一个LinkedHashSet 用于循环依赖检测
// 排序算法:循环toSort,从toSort 逐个取出当前元素。递归检测当前元素前后依赖,如果遇到依赖元素(源码-变量after),则先添加依赖after至sorted,再把当前元素添加至sorted
orderedClassNames = sortByAnnotation(classes, orderedClassNames);
return orderedClassNames;
}
特别的,需要明确AutoConfigureAfter 、AutoConfigureBefore 的直接作用:
- 内部类AutoConfigurationClasse 封装以上两个注解的作用,分别体现在属性before、 after
- 内部类AutoConfigurationClasses 封装依赖的检测和计算
- 关键的计算方法 AutoConfigurationClasses#getClassesRequestedAfter,如下
// 获取当前class 的所有依赖配置class(强依赖,弱依赖)
public Set<String> getClassesRequestedAfter(String className) {
Set<String> rtn = new LinkedHashSet<String>();
// 获取所有的前置class
rtn.addAll(get(className).getAfter());
for (Map.Entry<String, AutoConfigurationClass> entry : this.classes
.entrySet()) {
// 获取别的class 的后置class,也就是,当前class 的前置class
if (entry.getValue().getBefore().contains(className)) {
rtn.add(entry.getKey());
}
}
return rtn;
}