程序的开始
java
SpringApplication.run(Start.class, args);
走进run方法可以发现该方法支持Class数组
java
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
再次进入发现执行了SpringApplicaton构造函数
java
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
那么这个构造函数做了什么事情呢?接着往下看
java
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
java
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// resourceLoader赋值null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// primarySources赋值为[Start.class]
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 设置程序的应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置初始化器(赋值给initializers)
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置Spring上下文监听器(赋值给listeners)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 设置主应用程序类(通过当前线程的栈跟踪信息StackTrace)
this.mainApplicationClass = deduceMainApplicationClass();
}
问题一
Spring如何确认程序当前应用类型的?
java
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();
}
注:ClassUtils.isPresent方法判断是否存在该类
(1)当项目中存在 DispatcherHandler 这个类,且不存在 DispatcherServlet 类和ServletContainer时,程序的应用类型就是 REACTIVE,也就是他会加载嵌入一个反应式的 web 服务器。
(2)当项目中 Servlet 和 ConfigurableWebApplicationContext 其中一个不存在时,则程序的应用类型为 NONE,它并不会加载内嵌任何 web 服务器。
(3)除了上面两种情况外,其余的都按 SERVLET 类型处理,会内嵌一个 servlet 类型的 web 服务器。
而上面的这些类的判定,都来源于 Spring 的相关依赖包,而这依赖包是否需要导入,也是开发者所决定的,所以说开发者可以决定程序的应用类型,并不是 Srpingboot 本身决定的
问题二
如何设置初始化器和监听器?
通过Springboot SPI机制实现,根据当前类加载器找到所有META-INF/spring.factories下设置的类路径,最终实例化对象并返回。
至此构造方法讲完了,接下来我们看下run方法。
走近run方法
启用计时监控
java
StopWatch stopWatch = new StopWatch();
stopWatch.start();
配置Headless模式
java
this.configureHeadlessProperty();
Headless模式是系统的一种配置模式。在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式。
注册并启用监听器
ini
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
同理:也采用了Spring SPI机制
问题一
使用了什么设计模式?
在Spring Boot框架中,SpringApplicationRunListeners
的使用体现了观察者模式(Observer Pattern)的设计思想。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生改变时,它的所有依赖者(观察者)都会自动收到通知并更新。
在Spring Boot的上下文中:
- 主题(Subject) :可以看作是
SpringApplication
及其启动过程。这个主题是被观察的对象,它的状态变化(比如启动的不同阶段)会引发通知。 - 观察者(Observer) :即
SpringApplicationRunListeners
的实现类。这些观察者注册到主题上,以便在主题状态改变时得到通知。 - 注册(Register) :当
SpringApplication
开始运行时,它会通过this.getRunListeners(args)
获取并初始化所有的监听器,这实际上是一个注册过程,将监听器(观察者)注册到SpringApplication
(主题)上。 - 通知(Notify) :在
SpringApplication
的启动过程中,不同的阶段会触发相应的方法调用,这实际上是对观察者发出通知。每个监听器(观察者)都会根据自己的实现响应这些通知,执行相应的操作。
观察者模式的好处在于它降低了组件之间的耦合度,使得系统更加灵活和可扩展。在Spring Boot中,这种设计模式使得开发者能够方便地添加自定义的启动监听器,来扩展或修改应用的启动行为,而无需修改框架本身的代码。
问题二
ApplicationListener和SpringApplicationRunListener区别?
-
作用范围与监听对象:
- ApplicationListener是Spring上下文(org.springframework.context)中的监听器,它的作用范围是整个Spring上下文,因此它的监听对象更为广泛。
- SpringApplicationRunListener则是特定于Spring Boot的监听器,位于org.springframework.boot包下,其主要关注的是SpringBoot程序的启动和运行过程。
-
加载与初始化时机:
- ApplicationListener在SpringApplication类实例化的时候就加载了。Spring会从所有的spring.factories文件中加载所有的SpringApplication类的实现类,并实例化它们。
- SpringApplicationRunListener则是属于应用程序启动层面的监听器,它在SpringBoot启动的时候通过调用run方法进行反射加载初始化。此时上下文还没有加载,因此通过@Component注解是无法起作用的。
-
功能与角色:
- SpringApplicationRunListener是一个接口,其主要实现类是EventPublishingRunListener。这个接口的主要作用是监听SpringApplication启动的各个阶段,并将这些阶段封装成对应的ApplicationEvent,并发布出去。这实际上运用到了观察者模式。
- ApplicationListener则用于监听由Spring容器发布的事件,响应特定的生命周期事件,如应用启动、环境准备等。
综上所述,ApplicationListener和SpringApplicationRunListener的主要区别在于它们的作用范围、加载与初始化时机以及所扮演的功能角色。简单来说,ApplicationListener关注于整个Spring上下文的事件监听,而SpringApplicationRunListener则专注于SpringBoot程序的启动和运行过程。