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  对错误进行更详细更专业的透出。
目录
相关文章
|
5月前
|
SQL 监控 Java
如何判断Springboot项目中的数据池启动成功
这篇文章介绍了多种方法来判断Spring Boot项目中的数据池(如HikariCP)是否启动成功,包括查看启动日志、验证数据库连接、配置测试查询、检查数据源Bean初始化、使用Spring Boot Actuator、检查数据库操作执行情况、捕获初始化错误和启用SQL监控等。
如何判断Springboot项目中的数据池启动成功
|
5月前
|
Java 数据安全/隐私保护
SpringBoot 自定义初始化任务 Runner
SpringBoot 自定义初始化任务 Runner
32 0
|
8月前
|
Java
SpringBoot 项目启动初始化一个Map对象到内存
SpringBoot 项目启动初始化一个Map对象到内存
162 1
|
8月前
|
缓存 前端开发 Java
SpringBoot启动后加载初始化数据
SpringBoot启动后加载初始化数据
188 0
|
Java 数据库连接 开发者
探讨spring的启动类run方法启动的时候底层会执行什么
Spring是一个开源的Java应用程序框架,它为开发者提供了一种简化企业级应用开发的方式。Spring框架的核心是一个轻量级的容器,它能够管理和协调应用程序中的各个组件。在Spring框架中,Spring Boot是一个用于快速构建独立的、生产级的Spring应用程序的工具。Spring Boot简化了Spring应用程序的配置和部署,使得开发者可以更专注于业务逻辑的实现。
95 0
|
Java
SpringBoot项目启动过程中做数据资源初始化的方式
SpringBoot项目启动过程中做数据资源初始化的方式
525 0
|
Java 容器 Spring
SpringBoot源码学习(二) 初始化环境,创建容器,初始化Failure Analyzers
## 前言 + 第一篇文章我们大概了解了springboot启动的时候主要做了这么几件事 + new了一个SpringApplication实例 + 判断当前spring运行的环境 + 加载META-INF/spring.factories 并初始化监听器 + SpringApplications实例.run + 获取并启动监听器 + 实例化Even
282 0
SpringBoot源码学习(二) 初始化环境,创建容器,初始化Failure Analyzers
|
SQL 存储 缓存
如何在SpringBoot启动时执行初始化操作,两个简单接口就可以实现
最近遇到一个功能点,数据库中一张很简单的表有一千多条数据,这里的数据主要做到了值域映射的作用,简单来讲就是我可以通过中文名拿到数据库中对应的code值。原本的实现方式是每次用到之后去查一次sql,虽然不会有什么问题,但是只要是走了网络io,都会消耗时间。所以这个方案需要想办法优化。 优化的方式其实很简单,数据量不多,一千多条数据放在内存里也占不了多少空间。因此完全可以把一次性把数据加载到内存中,后面只需要每次去内存里调用就可以了。
|
Java 容器
SpringBoot2.x基础篇:应用程序在启动时访问启动项参数
`SpringBoot`应用程序在启动时,我们可以传递自定义的参数来进行动态控制逻辑,比如我们使用`--debug`启动参数时就会使用`debug`启动应用程序,在控制台打印一些调试日志信息。