run()方法
环境准备
执行ApplicationContextInitializer.initialize()
监听器SpringApplicationRunListener回调ContextPrepared
加载主配置类定义信息
监听器SpringApplicationRunListener回调contextLoaded
刷新启动IOC容器
扫描加载所有容器中的组件
包括从META-INF/spring-factories中获取所有的EnableAutoConfiguration组件
回调容器中所有的ApplicationRunner、CommandLineRunner的run方法
监听器SpringApplicationRunListener回调finished
1、创建SpringApplication对象
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
//保存主配置类
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//判断是否web应用
this.webEnvironment = deduceWebEnvironment();
//设置initializers属性,从类路径下的各个jar包中的META-INF/spring.facories中查找
//所有的ApplicationContextInitializer,并加载赋值给initializers
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//设置listeners属性, 从类路径下的各个jar包中的META-INF/spring.facories中查找
//所有的ApplicationListener,并加载赋值给listeners
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
2、执行run方法
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//获取SpringApplicationRunListener,从类路径下的各个jar包中的META-INF/spring.facories中查找
//所有的SpringApplicationRunListener,并加载赋值给listeners
SpringApplicationRunListeners listeners = getRunListeners(args);
//回调每一个SpringApplicationRunListeners 的starting方法
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//创建Banner,在控制台打印Spring的标识
Banner printedBanner = printBanner(environment);
//创建ApplicationContext,根据是否是web环境创建web的IOC还是普通的IOC
context = createApplicationContext();
//创建抛出异常所需的分析器
analyzers = new FailureAnalyzers(context);
//准备上下文,将environment保存到ioc中,且执行applyInitializers()方法,方法是中执行执行之前保存的
//所有ApplicationContextInitializer的initialize方法,回调完initialize方法之后还会回调所有的
//ApplicationListener的contextPrepared方法
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新容器,初始化IOC容器(如果是web应用,还会创建嵌入式Tomcat容器)
//扫描、创建、加载所有组件
refreshContext(context);
//从IOC容器中获取所有的ApplicationRunner和CommandLineRunner,ApplicationRunner先回调,CommandLineRunner后调用
afterRefresh(context, applicationArguments);
//所有的ApplicationListener执行finished方法
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//整个SpringBoot应用启动完成之后返回IOC容器
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
事件监听机制
配置在META-INF/spring.factories中的
ApplicationContextInitializer
SpringApplicationRunListener
只需要放在IOC容器中的
ApplicationRunner
CommandLineRunner
可以自定义监听器,分别实现上述接口,配置在META-INF/spring.factories中的需要自定义文件,在容器中的只需要加入容器即可