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

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 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  对错误进行更详细更专业的透出。
目录
相关文章
|
3月前
|
Java 数据安全/隐私保护
SpringBoot 自定义初始化任务 Runner
SpringBoot 自定义初始化任务 Runner
16 0
|
6月前
|
Java
SpringBoot 项目启动初始化一个Map对象到内存
SpringBoot 项目启动初始化一个Map对象到内存
142 1
|
6月前
|
缓存 前端开发 Java
SpringBoot启动后加载初始化数据
SpringBoot启动后加载初始化数据
146 0
|
6月前
|
Java Spring
SPRINGBOOT启动原理(基于3.x版本)(三)- SpringApplication里有啥
SPRINGBOOT启动原理(基于3.x版本)(三)- SpringApplication里有啥
55 0
|
6月前
|
Java
SpringBoot集成swagger后出现: Failed to start bean ‘documentationPluginsBootstrapper‘的解决方法
SpringBoot集成swagger后出现: Failed to start bean ‘documentationPluginsBootstrapper‘的解决方法
185 0
|
Java API 调度
[Java Framework] SpringBoot几种启动后自动初始化的几种方式
业务需求需要在项目启动之后自动把执行一次方法 (数据初始化或者创建一些调度任务),但是有时候可能不太明确他们的执行顺序,本文就带你梳理一下它们的执行顺序
381 0
[Java Framework] SpringBoot几种启动后自动初始化的几种方式
|
Java Spring 容器
SpringBoot中是如何创建WebServer的?
SpringBoot中是如何创建WebServer的?
165 0
|
Java
SpringBoot项目启动过程中做数据资源初始化的方式
SpringBoot项目启动过程中做数据资源初始化的方式
501 0
|
Java 容器 Spring
SpringBoot源码学习(二) 初始化环境,创建容器,初始化Failure Analyzers
## 前言 + 第一篇文章我们大概了解了springboot启动的时候主要做了这么几件事 + new了一个SpringApplication实例 + 判断当前spring运行的环境 + 加载META-INF/spring.factories 并初始化监听器 + SpringApplications实例.run + 获取并启动监听器 + 实例化Even
279 0
SpringBoot源码学习(二) 初始化环境,创建容器,初始化Failure Analyzers
|
SQL 存储 缓存
如何在SpringBoot启动时执行初始化操作,两个简单接口就可以实现
最近遇到一个功能点,数据库中一张很简单的表有一千多条数据,这里的数据主要做到了值域映射的作用,简单来讲就是我可以通过中文名拿到数据库中对应的code值。原本的实现方式是每次用到之后去查一次sql,虽然不会有什么问题,但是只要是走了网络io,都会消耗时间。所以这个方案需要想办法优化。 优化的方式其实很简单,数据量不多,一千多条数据放在内存里也占不了多少空间。因此完全可以把一次性把数据加载到内存中,后面只需要每次去内存里调用就可以了。
下一篇
无影云桌面