版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35704236/article/details/84193671
探索 SpringBoot (二) 启动流程详解
文章目录
1 缘起
从 SpringApplication.run(xxx.class, args); 开始,看看 SpringBoot 为我们做了什么
2 环境配置
- 1 本文源码基于 spring-boot-2.1.0.RELEASE
- 2 本文将从两个阶段来讲解 SpringBoot 的具体启动流程,分别是 准备阶段 和 运行阶段
- 3 因为内容比较多 打算分成两篇,上篇将 准备阶段 下篇将运行阶段
3 SpringApplication 准备阶段
new SpringApplication(primarySources)
- 1 调用 run 方法 传入配置主类,和运行时候参数
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
- 2 进入准备阶段并且运行,我们接着往下看看 SpringApplication 的构造方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
- 3 这里主要做了两件事情
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
3.1 推断
// 推断 web app 类型
WebApplicationType.deduceFromClasspath();
// 推断运行主类
this.mainApplicationClass = deduceMainApplicationClass();
3.2 设置
// 根据 Spring 工厂方法加载 Initializers
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
3.1 推断 web app 类型
WebApplicationType.deduceFromClasspath();
- 我们接着往下看看
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
- 这里就只做了 3 件事情
1 判断 常量 WEBFLUX_INDICATOR_CLASS 所表示的类 是否存在并且可以加载 如果 true 则返回 WebApplicationType.REACTIVE
2 判断 常量 SERVLET_INDICATOR_CLASSES 所表示的类 是否存在并且可以加载
如果有一个 false 返回 WebApplicationType.NONE
3 如果上面条件都没满足 返回 WebApplicationType.SERVLET
3.2 推断 运行主类
this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
- 这里就是获取当前运行时候的堆栈信息,找到 “main” 方法的类并且返回
3.3 根据 (order )采用 SpringFactory 的方式初始化 ApplicationContextInitializer 的实现类,并且排序
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
...
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
- 这个方法就是把初始化得到的实现类设置成成员变量,我们关键看下面这个方法
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
....
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
这里主要做了 2 件事
1 从 classpath 下面获取 spring.facoty 中, ApplicationContextInitializer 获取 该接口实现类的全类名
2 根据获取的 全类名 初始化 实现类 并且排序
- 好吧 结合代码再看看
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
...
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
-
这里的主要作用就是 加载 classpath 下面所有叫 META-INF/spring.factories 的文件,并且解析里面的 key 和 value 放入 map 中返回
-
我们再来看看第二点
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass
.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
- 这里无非就是通过反射 初始化 上面的实现类
3.4 根据 (order )采用 SpringFactory 的方式初始化 ApplicationListener 的实现类,并且排序
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
- 这个的实现就和上面类似了 参照上面吧。
到此为止 SpringApplicaiton 的准备阶段就结束啦,我们一起看看 运行阶段做了啥吧。