前言
- 第一篇文章我们大概了解了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 对错误进行更详细更专业的透出。