Spring原理 SpringBoot
1 Boot
1.1 Boot 骨架项目
如果是 linux 环境,用以下命令即可获取 spring boot 的骨架 pom.xml
curl -G https://start.spring.io/pom.xml -d dependencies=web,mysql,mybatis -o pom.xml
也可以使用 Postman 等工具实现
若想获取更多用法,请参考
curl https://start.spring.io
1.2 Boot War项目
步骤1:创建模块,区别在于打包方式选择 war
接下来勾选 Spring Web 支持
步骤2:编写控制器
@Controller
public class MyController {
@RequestMapping("/hello")
public String abc() {
System.out.println("进入了控制器");
return "hello";
}
}
步骤3:编写 jsp 视图,新建 webapp 目录和一个 hello.jsp 文件,注意文件名与控制器方法返回的视图逻辑名一致
src
|- main
|- java
|- resources
|- webapp
|- hello.jsp
步骤4:配置视图路径,打开 application.properties 文件
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
将来 prefix + 控制器方法返回值 + suffix 即为视图完整路径
测试
如果用 mvn 插件 mvn spring-boot:run
或 main 方法测试
- 必须添加如下依赖,因为此时用的还是内嵌 tomcat,而内嵌 tomcat 默认不带 jasper(用来解析 jsp)
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
也可以使用 Idea 配置 tomcat 来测试,此时用的是外置 tomcat
- 骨架生成的代码中,多了一个 ServletInitializer,它的作用就是配置外置 Tomcat 使用的,在外置 Tomcat 启动后,去调用它创建和运行 SpringApplication
启示
对于 jar 项目,若要支持 jsp,也可以在加入 jasper 依赖的前提下,把 jsp 文件置入 META-INF/resources
1.3 Boot 启动过程
阶段一:SpringApplication 构造
演示获取 Bean Definition 源
@Configuration
public class A39_1 {
public static void main(String[] args) {
System.out.println("1. 演示获取 Bean Definition 源");
SpringApplication spring = new SpringApplication(A39_1.class);
spring.setSources(Set.of("classpath:b01.xml"));//新增来源xml
System.out.println("2. 演示推断应用类型");
System.out.println("3. 演示 ApplicationContext 初始化器");
System.out.println("4. 演示监听器与事件");
System.out.println("5. 演示主类推断");
//在run方法中创建并初始化spring容器
ConfigurableApplicationContext context = spring.run(args);
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name:" + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
context.close();
}
static class Bean1 {
}
static class Bean2 {
}
static class Bean3 {
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
@Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
演示推断应用类型
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
System.out.println("1. 演示获取 Bean Definition 源");
SpringApplication spring = new SpringApplication(A39_1.class);
spring.setSources(Set.of("classpath:b01.xml"));//新增来源xml
System.out.println("2. 演示推断应用类型");
Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
deduceFromClasspath.setAccessible(true);
//static WebApplicationType deduceFromClasspath() {} 静态方法,无需使用对象,所以传null,同时也不需要参数
System.out.println("\t应用类型为:" + deduceFromClasspath.invoke(null));
System.out.println("3. 演示 ApplicationContext 初始化器");
System.out.println("4. 演示监听器与事件");
System.out.println("5. 演示主类推断");
//在run方法中创建并初始化spring容器
ConfigurableApplicationContext context = spring.run(args);
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name:" + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
context.close();
}
演示 ApplicationContext 初始化器
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
System.out.println("1. 演示获取 Bean Definition 源");
SpringApplication spring = new SpringApplication(A39_1.class);
spring.setSources(Set.of("classpath:b01.xml"));//新增来源xml
System.out.println("2. 演示推断应用类型");
Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
deduceFromClasspath.setAccessible(true);
//static WebApplicationType deduceFromClasspath() {} 静态方法,无需使用对象,所以传null,同时也不需要参数
System.out.println("\t应用类型为:" + deduceFromClasspath.invoke(null));
System.out.println("3. 演示 ApplicationContext 初始化器");
spring.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {//刚刚创建,但是没有refresh的applicationContext对象
if (applicationContext instanceof GenericApplicationContext gac) { //转换类型
gac.registerBean("bean3",Bean3.class);//bean1,bean2之前注册了
}
}
});
System.out.println("4. 演示监听器与事件");
System.out.println("5. 演示主类推断");
//在run方法中创建并初始化spring容器
ConfigurableApplicationContext context = spring.run(args);
// 创建 ApplicationContext
// 调用初始化器 对 ApplicationContext 做扩展
// ApplicationContext.refresh()
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name:" + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
context.close();
}
演示监听器与事件
演示主类推断
阶段二:执行 run 方法
事件发布器 (run 1)
public class A39_2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//添加 app 监听器
SpringApplication app = new SpringApplication();
app.addListeners(e -> System.out.println(e.getClass()));
// 获取事件发送器实现类名
//参数1: 接口类型 , 参数2: ClassLoader 返回值:names:多个实现类的名字
List<String> names = SpringFactoriesLoader.loadFactoryNames(SpringApplicationRunListener.class, A39_2.class.getClassLoader());
for (String name : names) {
//org.springframework.boot.context.event.EventPublishingRunListener: 拿到了实现类的名字
System.out.println(name);
Class<?> clazz = Class.forName(name);//拿到类对象
Constructor<?> constructor = clazz.getConstructor(SpringApplication.class, String[].class);//拿到类构造器
//事件发布器对象创建完成
EventPublishingRunListener publisher = (EventPublishingRunListener) constructor.newInstance(app, args);//创建对象
//发布事件
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
publisher.starting(bootstrapContext); //spring boot 开始启动
publisher.environmentPrepared(bootstrapContext,new StandardEnvironment()); //环境信息准备完毕
GenericApplicationContext context = new GenericApplicationContext();
publisher.contextPrepared(context); //在spring 容器创建,并调用初始化器之后,发送此事件
publisher.contextLoaded(context); // 所有 bean definition 加载完毕
context.refresh();
publisher.started(context); //spring 容器初始化完成(refresh 方法调用完毕)
publisher.running(context); //spring boot 启动完毕
publisher.failed(context,new Exception("出错")); //spring boot 启动出错
}
}
}
run 方法流程( 8 -11 )
配置类获取bean 定义
public class A39_3 {
@SuppressWarnings("all")
public static void main(String[] args) throws Exception {
SpringApplication app = new SpringApplication();
//新增一个初始化器
app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override //需要提前准备容器,所以需要在第9.步骤回调这个初始化方法
public void initialize(ConfigurableApplicationContext applicationContext) {
}
});
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 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 定义");
//设置将来读取的bean definition 存储位置:getDefaultListableBeanFactory ,还没有开始读取bean definition
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(context.getDefaultListableBeanFactory());
//解析Config类中bean 定义,并加入到bean工厂
reader.register(Config.class);
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");
/*
学到了什么
a. 创建容器、加载 bean 定义、refresh, 对应的步骤
*/
}
private static GenericApplicationContext createApplicationContext(WebApplicationType type) {
GenericApplicationContext context = null;
switch (type) {
case SERVLET -> context = new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE -> context = new AnnotationConfigReactiveWebServerApplicationContext();
case NONE -> context = new AnnotationConfigApplicationContext();
}
return context;
}
static class Bean4 {
}
static class Bean5 {
}
static class Bean6 {
}
@Configuration
static class Config {
@Bean
public Bean5 bean5() {
return new Bean5();
}
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
}
xml文件获取类定义
//xml获取类定义
XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));
包扫描获取类定义
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("com/feng/a39boot/sub");//扫描包路径下所有类,并解析bean定义,加入到bean工厂
run (2,12)
@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());// 获取带--的key、
System.out.println(args.getOptionValues("server.port"));// 获取带--的key对应的值
System.out.println(args.getNonOptionArgs());// 获取不带--的参数
}
};
}
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 args");
//args: main()方法的参数
DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);
完整代码
public class A39_3 {
@SuppressWarnings("all")
public static void main(String[] args) throws Exception {
SpringApplication app = new SpringApplication();
//新增一个初始化器
app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override //需要提前准备容器,所以需要在第9.步骤回调这个初始化方法
public void initialize(ConfigurableApplicationContext applicationContext) {
}
});
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 args");
//args: main()方法的参数
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 定义");
//设置将来读取的bean definition 存储位置:getDefaultListableBeanFactory ,还没有开始读取bean definition
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
AnnotatedBeanDefinitionReader reader1 = new AnnotatedBeanDefinitionReader(beanFactory);
//解析Config类中bean 定义,并加入到bean工厂
reader1.register(Config.class);
//xml获取类定义
XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("com/feng/a39boot/sub");//扫描包路径下所有类,并解析bean定义,加入到bean工厂
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 11. refresh 容器");
context.refresh();//必须要先刷新,才能验证
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name:" + name + " 来源: " + beanFactory.getBeanDefinition(name).getResourceDescription());
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 12. 执行 runner");
/**
* springboot执行结束时会回调CommandLineRunner,ApplicationRunner
*/
//getBeansOfType: 根据类型去拿bean , K: bean的名字 V: bean的实例对象
Map<String, CommandLineRunner> beansOfType = context.getBeansOfType(CommandLineRunner.class);
for (CommandLineRunner runner : beansOfType.values()) {
runner.run(args);
}
for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class)
.values()) {
runner.run(arguments); //applicationArguments: 第二步封装好的参数
}
/*
学到了什么
a. 创建容器、加载 bean 定义、refresh, 对应的步骤
*/
}
private static GenericApplicationContext createApplicationContext(WebApplicationType type) {
GenericApplicationContext context = null;
switch (type) {
case SERVLET -> context = new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE -> context = new AnnotationConfigReactiveWebServerApplicationContext();
case NONE -> context = new AnnotationConfigApplicationContext();
}
return context;
}
static class Bean4 {
}
static class Bean5 {
}
static class Bean6 {
}
@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());// 获取带--的key、
System.out.println(args.getOptionValues("server.port"));// 获取带--的key对应的值
System.out.println(args.getNonOptionArgs());// 获取不带--的参数
}
};
}
}
}
run 3
public class Step3 {
public static void main(String[] args) throws IOException {
ApplicationEnvironment env = new ApplicationEnvironment(); // 系统环境变量, properties, yaml
for (PropertySource<?> ps : env.getPropertySources()) {//来源的集合
/** 打印结果
* PropertiesPropertySource {name='systemProperties'} 系统属性
* SystemEnvironmentPropertySource {name='systemEnvironment'} 系统环境变量
*
* 如果找到的同名的key, 系统属性优先级高
*/
System.out.println(ps);
}
System.out.println(env.getProperty("JAVA_HOME"));
}
}
添加配置文件来源
添加命令行来源
public class Step3 {
public static void main(String[] args) throws IOException {
ApplicationEnvironment env = new ApplicationEnvironment(); // 系统环境变量, properties, yaml
//优先级添加到最后
env.getPropertySources().addLast(new ResourcePropertySource(new ClassPathResource("application.properties")));
//命令行添加来源
env.getPropertySources().addFirst(new SimpleCommandLinePropertySource(args));
for (PropertySource<?> ps : env.getPropertySources()) {//来源的集合
/** 打印结果
* PropertiesPropertySource {name='systemProperties'} 系统属性
* SystemEnvironmentPropertySource {name='systemEnvironment'} 系统环境变量
*
* 如果找到的同名的key, 系统属性优先级高
*/
System.out.println(ps);
}
System.out.println(env.getProperty("JAVA_HOME"));
System.out.println(env.getProperty("server.port"));
}
}
run 4
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);//加入到evn中,优先级最高
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"));
}
}
run 5
后置处理器增强添加源
public class Step5 {
public static void main(String[] args) {
SpringApplication app = new SpringApplication();
ApplicationEnvironment env = new ApplicationEnvironment();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强前");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
//EnvironmentPostProcessor(后置处理器) 的实现类
ConfigDataEnvironmentPostProcessor postProcessor1 = new ConfigDataEnvironmentPostProcessor(new DeferredLogs(), new DefaultBootstrapContext());
postProcessor1.postProcessEnvironment(env, app);//添加一些propertySource
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后1");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
System.out.println(env.getProperty("server.port"));
RandomValuePropertySourceEnvironmentPostProcessor postProcessor2 = new RandomValuePropertySourceEnvironmentPostProcessor(new DeferredLog());
postProcessor2.postProcessEnvironment(env, app);//添加一些propertySource
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后2");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
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"));
}
}
之前都是使用硬编码方法,springboot都是使用配置文件
spring通过监听器实现上面步骤
public class Step5 {
public static void main(String[] args) {
SpringApplication app = new SpringApplication();
app.addListeners(new EnvironmentPostProcessorApplicationListener());
/*List<String> names = SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, Step5.class.getClassLoader());
for (String name : names) {
System.out.println(name);
}*/
//run 的第3,4步,创建envirment对象,初始化一些属性,第5步才可以添加监听器,所以第5步,添加一个事件发布器触发监听事件
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);
}
}
run 6
public class Step6 {
// 绑定 spring.main 前缀的 key value 至 SpringApplication, 请通过 debug 查看
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: 代表获取的源头 bind: 代表绑定,第一参数是properties中的key值前缀,第二个参数是实体类型 get:获取绑定赋值后的对象
User user = Binder.get(env).bind("user", User.class).get();
System.out.println(user);
}
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 + '\'' +
'}';
}
}
}
User user = new User();
Binder.get(env).bind("user", Bindable.ofInstance(user));//现有对象绑定
System.out.println(user);
// 绑定 spring.main 前缀的 key value 至 SpringApplication, 请通过 debug 查看
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: 代表获取的源头 bind: 代表绑定,第一参数是properties中的key值前缀,第二个参数是实体类型 get:获取绑定赋值后的对象
/*User user = Binder.get(env).bind("user", User.class).get();
System.out.println(user);*/
/*User user = new User();
Binder.get(env).bind("user", Bindable.ofInstance(user));//现有对象绑定
System.out.println(user);*/
env.getPropertySources().addLast(new ResourcePropertySource("step6", new ClassPathResource("step6.properties")));
Binder.get(env).bind("spring.main", Bindable.ofInstance(application));
System.out.println(application);
}
step6主要是把spring.main中的属性和配置文件中的值绑定
run 7
输出图片
public class Step7 {
public static void main(String[] args) {
ApplicationEnvironment env = new ApplicationEnvironment();
//如果没有设置图片,使用默认图片
SpringApplicationBannerPrinter printer = new SpringApplicationBannerPrinter(
new DefaultResourceLoader(),
new SpringBootBanner()
);
// 测试文字 banner
// env.getPropertySources().addLast(new MapPropertySource("custom", Map.of("spring.banner.location","banner1.txt")));
// 测试图片 banner
// env.getPropertySources().addLast(new MapPropertySource("custom", Map.of("spring.banner.image.location","banner2.png")));
// 版本号的获取
System.out.println(SpringBootVersion.getVersion());
printer.print(env, Step7.class, System.out);
}
}
run 方法小结
源码
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
//1步骤:根据spring.factory读取里面的事件发布器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//springboot开始启动了
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//2步骤:分装main参数,分成一个 --符号的选项参数 和一个非选项参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//查看下方源码方法
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
this.configureIgnoreBeanInfo(environment);
//7步骤:打印banner信息
Banner printedBanner = this.printBanner(environment);
//8步骤:创建spring容器,根据在构造方法中推断出来的容器类型,在三种类型中,选择一种容器实现
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//查看下方源码
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//调用容器refresh方法,调用各种bean工厂后处理器,准备各种bean的后处理器。然后初始化每个单例
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
}
//发布一个started事件
listeners.started(context, timeTakenToStartup);
//查看下方源码
this.callRunners(context, applicationArguments);
} catch (Throwable var12) {
this.handleRunFailure(context, var12, listeners);
throw new IllegalStateException(var12);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
//发布一个ready事件,表明springboot项目启动完成
listeners.ready(context, timeTakenToReady);
return context;
} catch (Throwable var11) {
//如果出现异常,发布一个失败事件
this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var11);
}
}
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
//3步骤:创建environment环境 如果非web环境,创建applicationContext对象
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
//把参数信息封装成properties源数据对象,添加到environment中(environment从参数中获取一些键值(只能是选项参数--符号)信息)
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
//4步骤:把参数中key中以_分隔或者驼峰分隔的,转成-分隔
ConfigurationPropertySources.attach((Environment)environment);
//5步骤:事件的发布与响应,为environmen对象添加源
listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
//6步骤:把environment中spring.main前缀和springapplication对象绑定
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = this.convertEnvironment((ConfigurableEnvironment)environment);
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
//9步骤:应用初始化器,对applicationContext做功能增强
this.applyInitializers(context);
//发布contextPrepared事件,容器创建好,并初始化容器后,发布这个事件
listeners.contextPrepared(context);
bootstrapContext.close(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 AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
//第10步骤,获取所有beanDefination源
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//把获取的源加载到容器,加载各种beanDefination
this.load(context, sources.toArray(new Object[0]));
//发布contextLoaded事件
listeners.contextLoaded(context);
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList();
//12步骤,调用所有实现了ApplicationRunner.class或者是CommandLineRunner.class的bean
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
Iterator var4 = (new LinkedHashSet(runners)).iterator();
while(var4.hasNext()) {
Object runner = var4.next();
if (runner instanceof ApplicationRunner) {
this.callRunner((ApplicationRunner)runner, args);
}
if (runner instanceof CommandLineRunner) {
this.callRunner((CommandLineRunner)runner, args);
}
}
}
boot启动过程总结
阶段一: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
-
发布 application ready 事件6️⃣
-
这其中有异常,发布 application failed 事件7️⃣
-
带 * 的有独立的示例
演示 - 启动过程
a39.A39_1 对应 SpringApplication 构造
a39.A39_2 对应第1步,并演示 7 个事件
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