Spring Boot的初始化过程
Spring Boot是开发微服务的不二利器。它遵从Convention over Configuration原则,看上去非常简约,但实际底下却做了很多工作。本文从Spring Boot应用程序的初始化过程入手,讲解一下其实现细节。
一般Spring Boot的应用都是从main函数开始,调用SpringApplication.run函数。
SpringApplication.run(SomeApp.class, args);
这是一个静态函数,实际上它构造了一个SpringApplicaiton的实例,并调用另外一个run方法,如下所示:
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
public SpringApplication(Object... sources) {
// 一系列的初始化
initialize(sources);
}
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 判断是否为Web应用, 依据是此时是否已加载Servlet或ConfigurableWebApplicationContext类
this.webEnvironment = deduceWebEnvironment();
// 设置初始化对象,后期在prepareContext时调用
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 设置应用监听器,注意与后面的应用运行情况监听器区别
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 获取当前应用的主类,及带有入口函数main的类
this.mainApplicationClass = deduceMainApplicationClass();
}
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch(); ----- (1)
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args); ----- (2)
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args); ----- (3)
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments); ----- (4)
// 打印spring标志
Banner printedBanner = printBanner(environment);
// 创建应用上下文对象
context = createApplicationContext();
analyzers = new FailureAnalyzers(context); ----- (5)
prepareContext(context, environment, listeners, applicationArguments,
printedBanner); ----- (6)
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
1. StopWatch
StopWatch是Spring Framework的一个辅助类,它的作用是记录曾经执行的一系列任务,并在这些任务结束时统计其运行的时间。StopWatch有一个函数名叫prettyPrint,一听就知道它会做一些漂亮的输出工作,例如:
-----------------------------------------
ms % Task name
-----------------------------------------
02000 020% initializing
05000 050% processing
03000 030% finalizing
一般Spring Boot程序启动完成后都会输出整个启动过程花了多长时间,这个任务就是由StopWatch来完成的。
2. SpringApplicationRunListeners
这是一个Spring应用运行情况监听类的集合类。这句话比较拗口,实际上它就是将一些事件监听对象集合在一起的类,这些对象都继承自SpringApplicationRunListener接口。从名字上就可以判断出它们是用于监听应用的运行情况的。具体监听哪些事件,查看一下接口的定义就一目了然了。
public interface SpringApplicationRunListener {
// 应用开始启动
void starting();
// 环境变量准备完毕
void environmentPrepared(ConfigurableEnvironment environment);
// 上下文对象准备完毕
void contextPrepared(ConfigurableApplicationContext context);
// 上下文对象加载完毕
void contextLoaded(ConfigurableApplicationContext context);
// 应用启动结束
void finished(ConfigurableApplicationContext context, Throwable exception);
}
Spring Boot调用getRunListeners(args),从ClassPath中搜寻该接口的实现类,实例化,并将结果保存在这个监听对象集合类里。不过并不是所有实现类都会被实例化,只有在ClassPath中所有“META-INF/spring.factories”文件中声明的实现类才会被实例化。默认情况下,这个接口只在spring-boot-.jar包里的“META-INF/spring.factories”声明,内容如下:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
这个文件里还有很多其他配置。可见,虽然Spring Boot的架构原则是Convenience Over Configuration, 但是并非绝对,仍然还有一些配置。
获取监听器之后,调用listeners.starting( ),即可执行这些监听器的starting方法。
3. ApplicationArguments
这是Spring Boot提供的处理应用参数的接口,同时Spring Boot还提供了一个实现类DefaultApplicationArguments。使用起来非常直白,只要在构造时代入参数args,使用时只要getOptionNames,getOptionValues即可。
4. ConfigurableEnvironment
读取各种环境变量,包括系统环境变量,JVM的属性,以及web servlet容器的属性等等。
5. FailureAnalyzers
失败分析器。这跟ApplicationRunListeners类似,也是根据spring.factories里的配置生成相应的对象。
6. prepareContext
这里主要是对应用的上下文对象进行初始化,应用初始化对象(SpringApplication构造时获取的那些初始化对象,定义在spring.factories里),加载bean等等。