Spring 应用上下文的刷新,是通过调用 SpringApplication 中的 refreshContext 方法来完成的。SpringApplication 中 refreshContext 方法相关代码如下。
private void refreshContext(ConfigurableApplicationContext context) {
//调用 refresh 方法
refresh(context);
if (this.registerShutdownHook) {
try
//注册 shutdownHook 线程, 实现销毁时的回调
context . registerShutdownHook();
} catch (AccessControlException ex) {
//在某些环境中不允许使用,会报出此异常,但此处并无处理操作
protected void refresh(ApplicationContext applicationContext) {
Assert. isInstanceOf(AbstractApplicationContext. class, applicationContex
t);
((AbstractAppl icationContext) applicat ionContext) .refresh();
}
}
其中 refresh 方法调用的是
AbstractApplicationContext 中的 refresh 方法,该类属于 spring-context 包。AbstractApplicationContext 的 refresh 方法更 多的是 Spring 相关的内容,这里我们通过 refresh 方法的顶层代码了解该方法都做了些什么,不过多深入 Spring 内部进行讲解。
blic void refresh() throws BeansException, IllegalStateException {
//整个过程同步处理
synchronized (this. startupShutdownMonitor) {
//准备刷新工作
prepareRefresh();
//通知子类刷新内部 beanI 厂
ConfigurablelistableBeanFactory beanFactory = obtainFreshBeanFactory();
//为当前 context 准备 beanI 厂
prepareBeanF actory(beanFactory);
/允许 context 的子类对 beanIJ 进行后置处理
postProcessBeanFactory(beanFactory);
//调用 context 中注册为 bean 的工厂处理器
invokeBeanFactoryPostProcessors (beanFactory);
// 注册 bean 处理器(beanPostProcessors)
registerBeanPostProcessors (beanFactory);
//初始化 context 的信息源,和国际化有关
initMessageSource();
// 初始化 context 的專件传播器
ini tApplicationEventMulticaster();
//初始化其他子类特殊的 bean
onRefresh();
//检查并炷册事件监昕器
registerListeners();
//实例化所有非巅加裁单例
finishBeanFactoryInitialization(beanFactory);
//最后一步:发布对应事件
finishRefresh();
} catch (BeansException ex) {
// .....
finally {
// .....
}}}
在上面的代码中,调用 finishRefresh 方法初始化容器的生 命周期处理器并发布容器的生命周期事件之后,Spring 应用 上下文正式开启,Spring Boot 核心特性也随之启动。完成 refreshContext 方法操作之后,调用 afterRefresh 方 法。最新版本的 Spring Boot 中refreshContext 方法的实现默认为空,可由开发人员自行扩展,相关代码如下。
但需要注意的是,该方法在以往的版本中方法定义和实现差距较大,如果对此方法进行扩展,在升级版本时需特别留意。
protected void afterRefresh(ConfigurableApplicationContext context,
Applicat ionArguments args) {
//默认实现为空
}
完成以上操作之后,调用 SpringApplicationRun isteners 的 started 方法,通知监听器容器启动完成,并调用 ApplicationRunner 和 CommandL ineRunner 的运行方法。
ApplicationRunner 和 CommandL ineRunner 是通过 SpringApplication 类的 callRunners方法来完成的,具体代码如下。
private void callRunners (ApplicationContext context, Applicat ionArguments a
rgs) {
List<0bject> runners = new ArrayList<>();
runners . addAll(context. getBeansOfType(ApplicationRunner. class). values());
runners . addAll(context . getBeansOfType(CommandL ineRunner . class).values());
AnnotationAwareOrderComparator. sort (runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof Commandl ineRunner)
callRunner( (CommandL ineRunner) runner, args);
private void callRunner(ApplicationRunner runner, ApplicationArguments arg
s)i
try {
(runner). run(args);
} catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner",
ex);
private void callRunner (CommandL ineRunner runner, ApplicationArguments arg
s) {
//内容与上面方法相同
}
}
以上代码,首先从 context 中获得类型为 ApplicationRunner 和 CommandLineRunner 的Bean, 将 它 们 放 入 List 列 表 中 并 进 行 排 序 。 然 后 再 遍 历 调 用 其 run 方 法 , 并 将ApplicationArguments 参数传入。
Spring Boot 提供这两个接口的目的,是为了我们在开发的过程中,通过它们来实现在容器启动时执行一-些操作,如果有多个实现类,可通过@Order 注解或实现 Ordered 接口来控制执行顺序。
这两个接口都提供了一个 run 方法,但不同之处在于: ApplicationRunner 中 run 方法的参数为 ApplicationArguments,而 CommandLineRunner 接口 中 run 方法的参数为 String 数组。
以上方法执行完成后,会通过
SpringApplicationRunListeners 的 running 方法通知监听器:
容器此刻已处于运行状态。至此,SpringApplication 的 run 方法执行完毕。
本章重点围绕 Spring Boot 启动过程中 SpringApplication 类的 run 方法的执行流程进行讲解,并做了一些功能和知识点的拓展,其中重点为在此过程中的事件监听、初始化环境、容器的创建及初始化操作。
本章也是 Spring Boot 的核心功能之一,读者如果感兴趣可以进一步查看 Spring 相关源代码。