public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//SpringApplication的启动由两部分组成:
//1. 实例化SpringApplication对象
//2. run(args):调用run方法
return new SpringApplication(primarySources).run(args);
}
项目初始化启动
上一篇文章分析完SpringApplication的实例化对象的创建过程,接着查看run(args)的项目初始化源码分析
public ConfigurableApplicationContext run(String... args) {
// 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 初始化应用上下文和异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置 headless 属性
configureHeadlessProperty();
// (1)获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 创建 ApplicationArguments 对象 初始化默认应用参数类
// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//(2)项目运行环境Environment的预配置
// 创建并配置当前SpringBoot应用将要使用的Environment
// 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
Banner printedBanner = printBanner(environment);
// (3)创建Spring容器
context = createApplicationContext();
// 获得异常报告器 SpringBootExceptionReporter 数组
//这一步的逻辑和实例化初始化器和监听器的一样,
// 都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// (4)Spring容器前置处理
//这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// (5):刷新容器
refreshContext(context);
// (6):Spring容器后置处理
//扩展接口,设计模式中的模板方法,默认为空实现。
// 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
afterRefresh(context, applicationArguments);
// 停止 StopWatch 统计时长
stopWatch.stop();
// 打印 Spring Boot 启动的时长日志。
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// (7)发出结束执行的事件通知
listeners.started(context);
// (8):执行Runners
//用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
//Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
//Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
callRunners(context, applicationArguments);
} catch (Throwable ex) {
// 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
// (9)发布应用上下文就绪事件
//表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
// 这样整个Spring Boot项目就正式启动完成了。
try {
listeners.running(context);
} catch (Throwable ex) {
// 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//返回容器
return context;
}
根据源码分析,大致可以分为以下几个步骤:
第一步 获取并启动监听器
this.getRunListeners(args)listeners.starting()方法主要用于获取SpringApplication实例化过程中初始化的SpringApplicationRunListener监听器并运行
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 这里仍然利用了getSpringFactoriesInstances方法来获取实例,
// 从META-INF/spring.factories中读取Key为org.springframework.boot.SpringApplicationRunListener的Values
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
第二步 根据SpringApplicationRunListener以及参数来准备环境
this.prepareEnvironment(listeners, applicationArguments)主要用于对项目运行环境进行预设置,同时通过this.configureIgnoreBeanInfo(environment)方法排除一些不需要的环境
/*
加载外部化配置资源到environment,包括命令行参数、servletConfigInitParams、
servletContextInitParams、systemProperties、sytemEnvironment、random、
application.yml(.yaml/.xml/.properties)等;初始化日志系统。
*/
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
//获取或创建环境(存在就直接返回,不存在创建一个再返回)
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境:配置PropertySources和active Profiles
configureEnvironment(environment, applicationArguments.getSourceArgs());
//listeners环境准备(就是广播ApplicationEnvironmentPreparedEvent事件)。
listeners.environmentPrepared(environment);
//将环境绑定到SpringApplication
bindToSpringApplication(environment);
//如果非web环境,将环境转换成StandardEnvironment
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 配置PropertySources对它自己的递归依赖
// 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。
ConfigurationPropertySources.attach(environment);
return environment;
}
第三步 创建Spring容器
根据webApplicationType进行判断,确定容器类型,如果该类型为SERVLET,会通过反射装载对应的字节码,也就是,AnnotationConfigServletWebServerApplicationContext,接着使用之前初始化设置的context(应用上下文)environment(项目运行环境)listeners(监听器)applicationArguments(项目参数)printedBanner(项目图标信息)进行应用上线文的组装配置,并刷新
protected ConfigurableApplicationContext createApplicationContext() {
// 根据 webApplicationType 类型,获得 ApplicationContext 类型
// 这里创建容器的类型 还是根据webApplicationType进行判断的,
// 该类型为SERVLET类型,所以会通过反射装载对应的字节码,
// 也就是AnnotationConfigServletWebServerApplicationContext
// 先判断有没有指定的实现类
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_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);
}
}
// 创建 ApplicationContext 对象
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
第四步 Spring容器前置处理
这一步主要实在容器刷新之前的准备工作,设置容器环境,包括各种变量参数等,其中包含一个非常关键的操作,将启动类注入容器,为后续开启自动化配置奠定基础
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//设置容器环境,包括各种变量
context.setEnvironment(environment);
//设置上下文的 bean 生成器和资源加载器
postProcessApplicationContext(context);
//执行容器中的ApplicationContextInitializer(包括 spring.factories和自定义的实例)
applyInitializers(context);
//触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
listeners.contextPrepared(context);
//记录启动日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
// 加载所有资源
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加载我们的启动类,将启动类注入容器,为后续开启自动化配置奠定基础
load(context, sources.toArray(new Object[0]));
//触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
listeners.contextLoaded(context);
//这块会对整个上下文进行一个预处理,比如触发监听器的响应事件、加载资源、设置上下文环境等等
}
第五步 刷新容器
开启刷新Spring容器,通过refresh方法对整个IOC容器的初始化
private void refreshContext(ConfigurableApplicationContext context) {
// 开启(刷新)Spring 容器,通过refresh方法对整个IoC容器的初始化(包括Bean资源的定位、解析、注册等等)
refresh(context);
// 注册 ShutdownHook 钩子
if (this.registerShutdownHook) {
try {
//向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,除非它当时已经关闭
context.registerShutdownHook();
} catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
第六步 Spring容器的后置处理
扩展接口,设计模式中的模板方法,默认为空实现。
如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
第七步 发出结束执行的事件
获取EventPublishingRunListener监听器,并执行器started方法,并且将其创建的spring容器传进去,创建一个ApplicationStartedEvent事件,并执行ConfigurableApplicationContext的publishEvent方法也就是说在这里实在spring容器发布事件,并不是在SpringApplication中发布事件,和前面的starting是不同的,前面的staring是直接向SpringApplication中的监听器发布启动事件
public void started(ConfigurableApplicationContext context) {
//执行所有SpringApplicationRunListener实现的started方法。
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
第八步 执行Runners
用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
private void callRunners(ApplicationContext context, ApplicationArguments args) {
// 获得所有 Runner 们
List<Object> runners = new ArrayList<>();
// 获得所有 ApplicationRunner 实现类
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 获得所有 CommandLineRunner 实现类
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序 runners
AnnotationAwareOrderComparator.sort(runners);
// 遍历 Runner 数组,执行逻辑
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
第九步 发布应用上下文就绪事件
表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
这样整个Spring Boot项目就正式启动完成了。
public void running(ConfigurableApplicationContext context) {
//触发所有 SpringApplicationRunListener 监听器的 running 事件方法。
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}