【第三十九讲】Boot 启动流程
- SpringApplication 构造分析
- SpringApplication run 分析
12大步骤、7大事件
阶段一:springApplication
1.来源演示
当作外部处理
添加
@Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
既可 
输出结果

来源于xml
public static void main(String[] args) {
System.out.println("1.演示获取 Bean Definition 源");
SpringApplication springApplication = new SpringApplication(A39_1.class);
Set<String> set = new HashSet<>();
set.add("classpath:b01.xml");
springApplication.setSources(set);
ConfigurableApplicationContext context = springApplication.run(args);
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
context.close();
}
 
2.推断应用类型

因为加入的为web依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
推断出类型为 应用类型SERVLET

3.初始化器
System.out.println("3.演示 ApplicationContext 初始化器");
springApplication.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
if(applicationContext instanceof GenericApplicationContext ){
GenericApplicationContext context = (GenericApplicationContext) applicationContext;
context.registerBean("bean3",Bean3.class);
}
}
});

4. 监听事件
System.out.println("4.演示监听器与事件");
springApplication.addListeners(new ApplicationListener<ApplicationEvent>() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("\t事件为" + event.getClass());
}
});
5.主类推断

System.out.println("5.演示主类推断");
Method deduceMainApplicationClass = SpringApplication.class.getDeclaredMethod("deduceMainApplicationClass");
deduceMainApplicationClass.setAccessible(true);
System.out.println("\t 5-------" + deduceMainApplicationClass.invoke(springApplication));

阶段二:执行 run 方法
public class A39_3 {
public static void main(String[] args) throws Exception{
SpringApplication app = new SpringApplication();
app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("执行初始化器增强...");
}
});
System.out.println("----------2.封装启动 args");
DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);
System.out.println("----------8.创建容器");
GenericApplicationContext context = createApplicationContext(WebApplicationType.SERVLET);
System.out.println("----------9.准备容器");
for (ApplicationContextInitializer initializer : app.getInitializers()) {
initializer.initialize(context);
}
System.out.println("----------10.加载 bean 定义");
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
AnnotatedBeanDefinitionReader reader1 = new AnnotatedBeanDefinitionReader(beanFactory);
reader1.register(Config.class);
XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("com.example.spring01.com.a39.sub");
System.out.println("----------11.refresh 容器");
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name:"+ name + "来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
System.out.println("----------12.执行 runner");
for (CommandLineRunner runner : context.getBeansOfType(CommandLineRunner.class).values()) {
runner.run(args);
}
for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class).values()) {
runner.run(arguments);
}
}
1.得到 SpringApplicationRunListeners
名字取得不好,实际是事件发布器
- 发布 application starting 事件
学到了什么 a. 如何读取 spring.factories 中的配置 b. run 方法内获取事件发布器 (得到 SpringApplicationRunListeners) 的过程, 对应步骤中 1.获取事件发布器 发布 application starting 事件1?? 发布 application environment 已准备事件2?? 发布 application context 已初始化事件3?? 发布 application prepared 事件4?? 发布 application started 事件5?? 发布 application ready 事件6?? 这其中有异常,发布 application failed 事件7??
public class A39_2 {
public static void main(String[] args) throws Exception {
SpringApplication app = new SpringApplication();
app.addListeners( event -> System.out.println(event.getClass()));
List<String> names = SpringFactoriesLoader.loadFactoryNames(SpringApplicationRunListener.class, A39_2.class.getClassLoader());
for (String name : names) {
System.out.println("name---" + name);
Class<?> aClass = Class.forName(name);
Constructor<?> constructor = aClass.getConstructor(SpringApplication.class, String[].class);
SpringApplicationRunListener publisher = (SpringApplicationRunListener) constructor.newInstance(app, args);
GenericApplicationContext context = new GenericApplicationContext();
publisher.starting();
publisher.environmentPrepared(new StandardEnvironment());
publisher.contextPrepared(context);
publisher.contextLoaded(context);
context.refresh();
publisher.started(context);
publisher.running(context);
publisher.failed(context,new Exception("出错了"));
}
}
}

2.封装启动 args
DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);
8.创建容器
- 发布 application context 已初始化事件
GenericApplicationContext context = createApplicationContext(WebApplicationType.SERVLET);
--------
private static GenericApplicationContext createApplicationContext(WebApplicationType type) {
GenericApplicationContext context = null;
switch (type) {
case SERVLET:
context = new AnnotationConfigServletWebServerApplicationContext();
return context;
case REACTIVE:
context = new AnnotationConfigReactiveWebServerApplicationContext();
return context;
case NONE:
context = new AnnotationConfigApplicationContext();
break;
}
return context;
}
9.准备容器
for (ApplicationContextInitializer initializer : app.getInitializers()) {
initializer.initialize(context);
}
10.加载 bean 定义
- 发布 application prepared 事件
三种方式
- Config配置类
- xml 文件
- 包扫描
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
AnnotatedBeanDefinitionReader reader1 = new AnnotatedBeanDefinitionReader(beanFactory);
reader1.register(Config.class);
XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("com.example.spring01.com.a39.sub");
@Configuration
static class Config {
@Bean
public Bean5 bean5() {
return new Bean5();
}
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public CommandLineRunner commandLineRunner() {
return new CommandLineRunner() {
@Override
public void run(String... args) throws Exception {
System.out.println("commandLineRunner()..." + Arrays.toString(args));
}
};
}
@Bean
public ApplicationRunner applicationRunner() {
return new ApplicationRunner() {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("applicationRunner()..." + Arrays.toString(args.getSourceArgs()));
System.out.println(args.getOptionNames());
System.out.println(args.getOptionValues("server.port"));
System.out.println(args.getNonOptionArgs());
}
};
}
}
b03.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bean4" class="com.example.spring01.com.a39.A39_3.Bean4"></bean>
</beans>

11. refresh 容器
* 发布 application started 事件
12执行 runner
* 发布 application ready 事件
* 这其中有异常,发布 application failed 事件
在config配置中加入
两个接口的参数不同
@Bean
public CommandLineRunner commandLineRunner() {
return new CommandLineRunner() {
@Override
public void run(String... args) throws Exception {
System.out.println("commandLineRunner()..." + Arrays.toString(args));
}
};
}
@Bean
public ApplicationRunner applicationRunner() {
return new ApplicationRunner() {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("applicationRunner()..." + Arrays.toString(args.getSourceArgs()));
System.out.println(args.getOptionNames());
System.out.println(args.getOptionValues("server.port"));
System.out.println(args.getNonOptionArgs());
}
};
}
参数
- –server.port=8080 debug

runner 执行
for (CommandLineRunner runner : context.getBeansOfType(CommandLineRunner.class).values()) {
runner.run(args);
}
for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class).values()) {
runner.run(arguments);
}
上面阶段
org.springframework.boot.Step3
public class Step3 {
public static void main(String[] args) throws IOException {
ApplicationEnvironment env = new ApplicationEnvironment();
env.getPropertySources().addLast(new ResourcePropertySource(new ClassPathResource("step3.properties")));
env.getPropertySources().addFirst(new SimpleCommandLinePropertySource(args));
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
System.out.println(env.getProperty("server.port"));
}
}
先从系统属性里面找

Step4
ConfigurationPropertySources——名字不一致进行统一 
public class Step4 {
public static void main(String[] args) throws IOException, NoSuchFieldException {
ApplicationEnvironment env = new ApplicationEnvironment();
env.getPropertySources().addLast(
new ResourcePropertySource("step4", new ClassPathResource("step4.properties"))
);
ConfigurationPropertySources.attach(env);
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
System.out.println(env.getProperty("user.first-name"));
System.out.println(env.getProperty("user.middle-name"));
System.out.println(env.getProperty("user.last-name"));
}
}
Step5
public class Step5 {
public static void main(String[] args) {
SpringApplication app = new SpringApplication();
app.addListeners(new EnvironmentPostProcessorApplicationListener());
EventPublishingRunListener publisher = new EventPublishingRunListener(app, args);
ApplicationEnvironment env = new ApplicationEnvironment();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强前");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
publisher.environmentPrepared(new DefaultBootstrapContext(), env);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
}
private static void test1() {
SpringApplication app = new SpringApplication();
ApplicationEnvironment env = new ApplicationEnvironment();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强前");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
ConfigDataEnvironmentPostProcessor postProcessor1 = new ConfigDataEnvironmentPostProcessor(new DeferredLogs(), new DefaultBootstrapContext());
postProcessor1.postProcessEnvironment(env, app);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
RandomValuePropertySourceEnvironmentPostProcessor postProcessor2 = new RandomValuePropertySourceEnvironmentPostProcessor(new DeferredLog());
postProcessor2.postProcessEnvironment(env, app);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
System.out.println(env.getProperty("server.port"));
System.out.println(env.getProperty("random.int"));
System.out.println(env.getProperty("random.int"));
System.out.println(env.getProperty("random.int"));
System.out.println(env.getProperty("random.uuid"));
System.out.println(env.getProperty("random.uuid"));
System.out.println(env.getProperty("random.uuid"));
}
}
Step6
public class Step6 {
public static void main(String[] args) throws IOException {
SpringApplication application = new SpringApplication();
ApplicationEnvironment env = new ApplicationEnvironment();
env.getPropertySources().addLast(new ResourcePropertySource("step4", new ClassPathResource("step4.properties")));
env.getPropertySources().addLast(new ResourcePropertySource("step6", new ClassPathResource("step6.properties")));
System.out.println(application);
Binder.get(env).bind("spring.main", Bindable.ofInstance(application));
System.out.println(application);
}
static class User {
private String firstName;
private String middleName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "User{" +
"firstName='" + firstName + '\'' +
", middleName='" + middleName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
}
Step7
public class Step7 {
public static void main(String[] args) {
ApplicationEnvironment env = new ApplicationEnvironment();
SpringApplicationBannerPrinter printer = new SpringApplicationBannerPrinter(
new DefaultResourceLoader(),
new SpringBootBanner()
);
System.out.println(SpringBootVersion.getVersion());
printer.print(env, Step7.class, System.out);
}
}
总结
SpringApplication.class
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach((Environment)environment);
listeners.environmentPrepared((ConfigurableEnvironment)environment);
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
this.applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
阶段一:SpringApplication 构造
- 记录 BeanDefinition 源
- 推断应用类型
- 记录 ApplicationContext 初始化器
- 记录监听器
- 推断主启动类
阶段二:执行 run 方法
-
得到 SpringApplicationRunListeners,名字取得不好,实际是事件发布器
- 发布 application starting 事件1??
-
封装启动 args -
准备 Environment 添加命令行参数(*) -
ConfigurationPropertySources 处理(*)
- 发布 application environment 已准备事件2??
-
通过 EnvironmentPostProcessorApplicationListener 进行 env 后处理(*)
- application.properties,由 StandardConfigDataLocationResolver 解析
- spring.application.json
-
绑定 spring.main 到 SpringApplication 对象(*) -
打印 banner(*) -
创建容器 -
准备容器
- 发布 application context 已初始化事件3??
-
加载 bean 定义
- 发布 application prepared 事件4??
-
refresh 容器
- 发布 application started 事件5??
-
执行 runner
带 * 的有独立的示例
演示 - 启动过程
com.itheima.a39.A39_1 对应 SpringApplication 构造
com.itheima.a39.A39_2 对应第1步,并演示 7 个事件
com.itheima.a39.A39_3 对应第2、8到12步
org.springframework.boot.Step3
org.springframework.boot.Step4
org.springframework.boot.Step5
org.springframework.boot.Step6
org.springframework.boot.Step7
收获💡
- SpringApplication 构造方法中所做的操作
- 可以有多种源用来加载 bean 定义
- 应用类型推断
- 添加容器初始化器
- 添加监听器
- 演示主类推断
- 如何读取 spring.factories 中的配置
- 从配置中获取重要的事件发布器:SpringApplicationRunListeners
- 容器的创建、初始化器增强、加载 bean 定义等
- CommandLineRunner、ApplicationRunner 的作用
- 环境对象
- 命令行 PropertySource
- ConfigurationPropertySources 规范环境键名称
- EnvironmentPostProcessor 后处理增强
- 由 EventPublishingRunListener 通过监听事件2??来调用
- 绑定 spring.main 前缀的 key value 至 SpringApplication
- Banner
|