Hello,大家好,我是此林。
今天来深入底层讲一讲SpringBoot是如何启动的,也就是我们单击运行SpringBoot启动类,它底层发生了什么?
SpringBoot启动类很简单,只有一行代码。
我们点进run() 方法。
我们发现,它底层其实进行了两步操作。
第一步是new出一个SpringApplication对象,第二个是执行run() 方法。
我们先看看是如何new SpringApplication的。
继续点进SpringApplication的构造方法。
可以看到,这里初始化设置了非常多的属性。
我们主要最后几行关键代码。
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
通过DEBUG打断点我们发现,this.primarySources 就是当前启动类com.itheima.test.App。
也就是我们之前之前启动类里传入的参数App.class本身。
继续执行程序,这里判断了应用的类型。这里是servlet类型(传统Web应用),此外还有None类型(非Web应用)、Reactive类型。
再看这两行代码。
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
1. 这里this.getSpringFactoriesInstances(ApplicationContextInitializer.class) 底层是从 META-INF/spring.factories 文件里找到实现了 ApplicationContextInitializer 接口的所有实现类。它们用于在 ApplicationContext 初始化之前进行一些自定义的初始化操作。
以下是spring.factories 文件:
2. this.getSpringFactoriesInstances(ApplicationListener.class) 也一样,底层是从 META-INF/spring.factories 文件里找到键名为
org.springframework.context.ApplicationListener
的值,也就是找到所有实现了 ApplicationListener 接口的类。它们用于监听 Spring 应用上下文中的事件。
以下是相关的spring.factories 文件:
为了更易于理解,我们举个例子,再深入看看 this.getSpringFactoriesInstances(ApplicationContextInitializer.class) 源码。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 返回了注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 对每一个实现类进行实例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 对实例数组进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
这里有三步:
1. 获取注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)
2. 对每一个实现类进行实例化(返回一个实例数组)
3. 对实例数组进行排序,表示执行顺序。排序规则如下:如果实现了 PriorityOrdered 接口,优先级最高。如果实现了Ordered接口或者基于注解@Order,数值越小优先级越高。
点进AnnotationAwareOrderComparator.sort() 方法。发现它调用了java.util.List的 sort(Comparator<? super E> c) 方法。这里传进去的INSTANCE是 AnnotationAwareOrderComparator 本身,说明 AnnotationAwareOrderComparator 实现了Comparator 接口,重写了 compare() 方法。
但是我们又发现 AnnotationAwareOrderComparator 类里似乎没有实现Comparator 接口实现自定义排序。
我们查看AnnotationAwareOrderComparator继承关系链发现,它继承了OrderComparator类;OrderComparator又实现了Comparator接口,所以AnnotationAwareOrderComparator 间接实现了Comparator 接口来自定义排序。
public int compare(@Nullable Object o1, @Nullable Object o2) {
return this.doCompare(o1, o2, (OrderSourceProvider)null);
}
private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
boolean p1 = o1 instanceof PriorityOrdered;
boolean p2 = o2 instanceof PriorityOrdered;
if (p1 && !p2) {
return -1;
} else if (p2 && !p1) {
return 1;
} else {
int i1 = this.getOrder(o1, sourceProvider);
int i2 = this.getOrder(o2, sourceProvider);
return Integer.compare(i1, i2);
}
}
可以发现,主要功能是比较两个对象 o1 和 o2 的优先级顺序。
先检查 o1 和 o2 是否实现了 PriorityOrdered 接口。
如果 o1 实现了而 o2 没有实现,则 o1 的优先级更高,返回 -1。
如果 o2 实现了而 o1 没有实现,则 o2 的优先级更高,返回 1。
如果两者都实现了或都没有实现 PriorityOrdered 接口,则通过 getOrder 方法获取它们的顺序值,并比较这两个值。
我们再回到this.getSpringFactoriesInstances(ApplicationContextInitializer.class) 源码。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 返回了注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 对每一个实现类进行实例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 对实例数组进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
我们再来详细地看看这行代码的底层,它是如何获取ApplicationContextInitializer接口的所有实现类的?
// 返回了注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
点进loadFactoryNames() 方法。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
这里 loadSpringFactories(classLoaderToUse) 返回的是一个 Map<String, List<String>> ,getOrDefault() 则是取到键为 factoryTypeName 的 List,取不到则返回空集合。那么factoryTypeName到底是什么?我们发现它就是 ApplicationContextInitializer 接口。
由于刚才我们看过spring.factories 文件了,它就是一个键值对文件,它的值也就是一个类名列表,所以我们推测 Map<String, List<String>> 就是整个spring.factories。
继续点进 loadSpringFactories() 方法。果然如此,详细请看注释。
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
Map<String, List<String>> result = new HashMap();
try {
// 加载出了所有META_INF/spring.factories文件
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
// 遍历全部的META_INF/spring.factories文件
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
// 每一个META_INF/spring.factories文件相当于是一个properties文件
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
// 转成Map格式,key是接口名,value是实现类的列表
while(var6.hasNext()) {
Map.Entry<?, ?> entry = (Map.Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
我们要扫描所有的所有META_INF/spring.factories文件,因为它可能不止一个。
我们再来总结一下,以上流程就是创建初始化SpringApplication对象,并设置相关属性。关键的还是这几行代码。
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
至此,我们SpringApplication对象的创建就结束了。
明天我们再来详细讲一讲SpringApplication对象的run() 方法。
本文持续更新中.......
关注我吧!老朋友此林,带你看不一样的世界!有疑惑的小伙伴可以私信我或者评论区留言,我看到后会及时回复!
往期文章: