SpringBoot源码学习(二) 初始化环境,创建容器,初始化Failure Analyzers

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: SpringBoot 源码解析系列

前言

  • 第一篇文章我们大概了解了springboot启动的时候主要做了这么几件事
  • new了一个SpringApplication实例
  • 判断当前spring运行的环境
  • 加载META-INF/spring.factories 并初始化监听器
  • SpringApplications实例.run
  • 获取并启动监听器
  • 实例化EventPublishingRunListener
  • 把所有通过spring.factories实例化的listener添加到SimpleApplicationEventMulticaster中。
  • 发布ApplicationStartingEvent 执行注册了这个事件的listener
  • 构造容器环境
  • 创建容器
  • 实例化Failure Analyzers 处理启动的报错信息
  • 准备容器
  • 刷新容器
  • 刷新容器的后扩展接口
  • 今天会分析
  • 构造容器环境
  • 创建容器
  • 实例化Failure Analyzers 处理启动的报错信息

构造容器环境

  • 瞅一眼代码

private ConfigurableEnvironment prepareEnvironment(

  SpringApplicationRunListeners listeners,

  ApplicationArguments applicationArguments) {

// Create and configure the environment

ConfigurableEnvironment environment = getOrCreateEnvironment();

configureEnvironment(environment, applicationArguments.getSourceArgs());

listeners.environmentPrepared(environment);

bindToSpringApplication(environment);

if (this.webApplicationType == WebApplicationType.NONE) {

  environment = new EnvironmentConverter(getClassLoader())

    .convertToStandardEnvironmentIfNecessary(environment);

}

ConfigurationPropertySources.attach(environment);

return environment;

}

  • 如果指定了spring.profile.active 会在这里被加载到 否则为空

listeners.environmentPrepared(environment)

  • 这里会把ApplicationEnvironmentPreparedEvent时间发送给注册这个事件的listener
  • 比较重要的是ConfigFileApplicationListener
  • 里面有一个内部类Loader会对代码注册变量文件进行解析, 比如我们新增了classpath:application.properties classpath:application-default.properties
  • 最终我们可以看到我们的Environment中加载了这些数据
  • 至此,springBoot中的资源文件加载完毕,解析顺序从上到下,所以前面的配置文件会覆盖后面的配置文件。可以看到application.properties的优先级最低,系统变量和环境变量的优先级相对较高。

创建容器

  • 惯例瞅一眼源码

context = createApplicationContext();

  • 跟进进去会发现代码异常简单

protected ConfigurableApplicationContext createApplicationContext() {

Class<?> contextClass = this.applicationContextClass;

if (contextClass == null) {

  try {

  switch (this.webApplicationType) {

  case SERVLET:

    contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);

    break;

  case REACTIVE:

    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);

    break;

  default:

    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);

  }

  }

  catch (ClassNotFoundException ex) {

  throw new IllegalStateException(

    "Unable create a default ApplicationContext, "

      + "please specify an ApplicationContextClass",

    ex);

  }

}

return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);

}

  • 其实就是做了两件事
  • 根据当前的容器环境获取应该进行实例化的类, SERVLET类型会得到org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
  • 实例化这个类
  • 7.27注: 这里有坑,没有想象的那么简单 此处对reader、scanner、beanFactory进行了实例化;reader中实例化了属性conditionEvaluator;scanner中添加了两个AnnotationTypeFilter:一个针对@Component,一个针对@ManagedBean;beanFactory中注册了8个注解配置处理器  但是我太懒了 还没有对这里进行补充。
  • 我们debug进AnnotationConfigServletWebServerApplicationContext这个类发现对Reader和Scanner做了初始化
  • AnnotatedBeanDefinitionReader
    + 往ApplicationContext里面塞了一个conditionEvaluator  这个Evaluator是用来处理 @conditional注解的
    + 注册了多个Processors 执行后我们发现 我们的ApplicationContext里的BeanFactory里面多了6个Processors
  • ClassPathBeanDefinitionScanner 一看名字就知道是一个bean定义扫描器,用来扫描类路径下的bean侯选者。
  • debug进去 核心代码是注册了两个核心的AnnotationTypeFilter 一个针对@Component,一个针对@ManagedBean

实例化Failure Analyzers 处理启动的报错信息

  • 扫一下源码

<pre>

  exceptionReporters = getSpringFactoriesInstances(

    SpringBootExceptionReporter.class,

    new Class[] { ConfigurableApplicationContext.class }, context);

</pre>

  • 我们debug进去看一下

<pre>

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,

  Class<?>[] parameterTypes, Object... args) {

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

// 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;

}

</pre>

  • 其实如果前面几步都进行过单步调试的话 对这个方法一定不会陌生, 他的核心就是根据入参type的定义 去spring.factories里面找到对应的实现类通过反射进行实例化。
  • 我们去看一下org.springframework.boot.diagnostics.FailureAnalyzers的构造方法
  • 这个构造方法的核心就是loadFailureAnalyzers

<pre>

private List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) {

List<String> analyzerNames = SpringFactoriesLoader

  .loadFactoryNames(FailureAnalyzer.class, classLoader);

List<FailureAnalyzer> analyzers = new ArrayList<>();

for (String analyzerName : analyzerNames) {

  try {

  Constructor<?> constructor = ClassUtils.forName(analyzerName, classLoader)

    .getDeclaredConstructor();

  ReflectionUtils.makeAccessible(constructor);

  analyzers.add((FailureAnalyzer) constructor.newInstance());

  }

  catch (Throwable ex) {

  logger.trace("Failed to load " + analyzerName, ex);

  }

}

AnnotationAwareOrderComparator.sort(analyzers);

return analyzers;

}

  • 其实也简单, 就是把spring.factories里面的FailureAnalyzer.class对应的类全部加载回来并实例化
  • 那么这些Analyzers是怎么被使用的呢?  我们发现启动流程本身会有一个大的try-catch
  • 最终handleRunFailure 这个方法会执行到reportFailure 这个方法里
  • 核心是这个for循环 他会遍历 实例化的Analyzers  如果Analyzer发现 当前的ex自己处理不了 就会返回null  这样就会流转到下一个Analyzers   通过这种方案 就可以通过这些Analyzers  对错误进行更详细更专业的透出。
目录
相关文章
|
Java TensorFlow 算法框架/工具
Android 中集成 TensorFlow Lite图片识别
Android 中集成 TensorFlow Lite图片识别
240 0
|
Arthas 缓存 Java
在 Windows 下的 Arthas 快速安装 | 学习笔记
快速学习在 Windows 下的 Arthas 快速安装
在 Windows 下的 Arthas 快速安装 | 学习笔记
|
2月前
|
Ubuntu Linux
如何在 Ubuntu 服务器上安装桌面环境(GUI)
如果你有任何问题,请在评论区留言。你会在服务器上使用 GUI 吗?参照本文后你遇到了什么问题吗?
296 0
|
7月前
|
缓存 NoSQL Java
Redis应用—8.相关的缓存框架
本文介绍了Ehcache和Guava Cache两个缓存框架及其使用方法,以及如何自定义缓存。主要内容包括:Ehcache缓存框架、Guava Cache缓存框架、自定义缓存。总结:Ehcache适合用作本地缓存或与Redis结合使用,Guava Cache则提供了更灵活的缓存管理和更高的并发性能。自定义缓存可以根据具体需求选择不同的数据结构和引用类型来实现特定的缓存策略。
454 16
Redis应用—8.相关的缓存框架
|
测试技术 API 数据库
性能测试概念
**性能测试评估系统在负载下的表现,关注响应时间、吞吐量、并发用户数和资源利用率。关键指标包括RT、HPS、TPS、QPS、RPS等。并发数可由QPS和平均响应时间计算。提升压力的技术手段涉及多进程、多线程,各有优劣。P90、P95、P99等分位数用于描述数据分布,揭示不同负载场景下的系统性能。**
|
Java 数据库连接 Spring
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could
这个错误通常出现在使用Spring Boot进行数据库连接时。错误信息表明Spring Boot未能配置一个DataSource,因为没有指定'url'属性,并且没有发现默认的数据库连接。
4926 0
|
Java
Thread 类中的start() 和 run() 方法有什么区别
【8月更文挑战第9天】Thread 类中的start() 和 run() 方法有什么区别
697 0
【IDEA】IDEA 快速实现override方法的快捷键
【IDEA】IDEA 快速实现override方法的快捷键
|
前端开发 Java Spring
SpringBoot2 | SpringBoot Environment源码分析(四)
SpringBoot2 | SpringBoot Environment源码分析(四)
196 0
|
并行计算 算法 安全
Java面试题:解释Java内存模型的内存屏障,并讨论其对多线程并发的影响,解释Java中的线程局部变量(ThreadLocal)的工作原理,解释Java中的ForkJoinPool的工作原理
Java面试题:解释Java内存模型的内存屏障,并讨论其对多线程并发的影响,解释Java中的线程局部变量(ThreadLocal)的工作原理,解释Java中的ForkJoinPool的工作原理
155 0