在spring boot 使用系列一中我们介绍了最简单使用spring boot的方法。我们看到Spring boot应用的核心启动代码如下:
@SpringBootApplication
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
从代码中,我们看到了两个新面孔:@SpringBootApplication 和 SpringApplication。这两个是spring boot的两个核心点,所以我们都会分析。
本文,我们从SpringApplication.run(BootApplication.class, args)入口分析spring boot运行过程中信息:
后续spring boot 使用系列三,我们来分析注解@SpringBootApplication。
启动流程
SpringApplication默认启动流程
- 根据classpath,创建相关的ApplicationContext
- 注册一个CommandLinePropertySource把命令行参数显示为Spring的properties
- 刷新ApplicationContext,加载所有的singleton bean
- 触发所有的CommandLineRunner实例
一般场景使用SpringApplication.run(Class,String[])就可以了。
SpringApplication能够加载多种不同的source
- AnnotatedBeanDefinitionReader加载全限定的类名。
- XmlBeanDefinitionReader加载的xml文件,或者GroovyBeanDefinitionReader加载groovy脚本
- ClassPathBeanDefinitionScanner扫描的包
SpringApplication的核心run流程:
1、构建SpringApplication对象:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
- 通过classpath推断web的类型。具体逻辑比较简单。
- 设置ApplicationContextInitializer: 扫描classpath下面 "META-INF/spring.factories"下的文件,加载其中ApplicationContextInitializer指定的类
- 设置ApplicationListener:扫描classpath下面 "META-INF/spring.factories"下的文件,加载其中ApplicationListener指定的类
- 通过堆栈信息推断出主应用程序类
2、SpringApplication的run方法:
去除掉无用的日志、watch代码、异常处理等代码
public ConfigurableApplicationContext run(String... args) {
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.started(context);
callRunners(context, applicationArguments);
listeners.running(context);
return context;
}
- 配置java.awt.headless属性,默认为true
- 加载配置的所有SpringApplicationRunListener实例。并聚合为 SpringApplicationRunListeners实例
- 准备好Environment。并通过SpringApplicationRunListeners调用所有SpringApplicationRunListener实例的environmentPrepared方法。
- 创建ConfigurableApplicationContext实例
- 加载配置的所有SpringBootExceptionReporter实例
- 配置好ConfigurableApplicationContext。
- 设置ConfigurableApplicationContext的environment属性
- 应用相关的post处理程序
- 调用所有的ApplicationContextInitializer实例的initialize方法
- 通过SpringApplicationRunListeners调用所有SpringApplicationRunListener实例的 contextPrepared方法。
- 增加一些spring boot特有的singleton beans,例如springApplicationArguments,springBootBanner,LazyInitializationBeanFactoryPostProcessor等
- 加载所有的source及导入相应的bean,并通过SpringApplicationRunListeners调用所有SpringApplicationRunListener实例的contextLoaded方法。
- 刷新context。调用给你context的refresh方法。这个过程后复杂,后续专门文章分析
- 通过SpringApplicationRunListeners调用所有SpringApplicationRunListener实例的started方法。
- 调用所有的ApplicationRunner和CommandLineRunner的实例的run方法。如果前面方法有任何异常,都通过SpringApplicationRunListeners调用所有SpringApplicationRunListener实例的failed方法进行通知。并调用所有SpringBootExceptionReporter实例的reportException方法。
- 通过SpringApplicationRunListeners调用所有SpringApplicationRunListener实例的running方法。
扩展点
1、SpringApplication的各种set方法,可以根据自己的需要设置
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
// ... customize application settings here. 各种设置可以配置SpringApplication的部分行为。
application.run(args)
}
2、通过SpringApplicationRunListener扩展需求
可以在SpringApplication的run过程中增加自定义的操作。当到达指定的流程,会触发我们自定义的操作。
3、通过扩展SpringApplication扩展一些新的功能或者重写启动流程
这个扩展需要特别小心,许多高级功能可以扩展。protected方法可以重写,再定义。