Spring Boot 源码分析(一)

程序的开始

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区别?

  1. 作用范围与监听对象

    • ApplicationListener是Spring上下文(org.springframework.context)中的监听器,它的作用范围是整个Spring上下文,因此它的监听对象更为广泛。
    • SpringApplicationRunListener则是特定于Spring Boot的监听器,位于org.springframework.boot包下,其主要关注的是SpringBoot程序的启动和运行过程。
  2. 加载与初始化时机

    • ApplicationListener在SpringApplication类实例化的时候就加载了。Spring会从所有的spring.factories文件中加载所有的SpringApplication类的实现类,并实例化它们。
    • SpringApplicationRunListener则是属于应用程序启动层面的监听器,它在SpringBoot启动的时候通过调用run方法进行反射加载初始化。此时上下文还没有加载,因此通过@Component注解是无法起作用的。
  3. 功能与角色

    • SpringApplicationRunListener是一个接口,其主要实现类是EventPublishingRunListener。这个接口的主要作用是监听SpringApplication启动的各个阶段,并将这些阶段封装成对应的ApplicationEvent,并发布出去。这实际上运用到了观察者模式。
    • ApplicationListener则用于监听由Spring容器发布的事件,响应特定的生命周期事件,如应用启动、环境准备等。

综上所述,ApplicationListener和SpringApplicationRunListener的主要区别在于它们的作用范围、加载与初始化时机以及所扮演的功能角色。简单来说,ApplicationListener关注于整个Spring上下文的事件监听,而SpringApplicationRunListener则专注于SpringBoot程序的启动和运行过程。

相关推荐
中草药z5 分钟前
【Spring】深入解析 Spring 原理:Bean 的多方面剖析(源码阅读)
java·数据库·spring boot·spring·bean·源码阅读
信徒_13 分钟前
常用设计模式
java·单例模式·设计模式
神仙别闹18 分钟前
基于C#实现的(WinForm)模拟操作系统文件管理系统
java·git·ffmpeg
小爬虫程序猿19 分钟前
利用Java爬虫速卖通按关键字搜索AliExpress商品
java·开发语言·爬虫
m0_7482567824 分钟前
SpringBoot 依赖之Spring Web
前端·spring boot·spring
组合缺一24 分钟前
Solon v3.0.5 发布!(Spring 可以退休了吗?)
java·后端·spring·solon
程序猿零零漆27 分钟前
SpringCloud 系列教程:微服务的未来(二)Mybatis-Plus的条件构造器、自定义SQL、Service接口基本用法
java·spring cloud·mybatis-plus
猿来入此小猿29 分钟前
基于SpringBoot在线音乐系统平台功能实现十二
java·spring boot·后端·毕业设计·音乐系统·音乐平台·毕业源码
愤怒的代码42 分钟前
Spring Boot对访问密钥加解密——HMAC-SHA256
java·spring boot·后端
带多刺的玫瑰43 分钟前
Leecode刷题C语言之切蛋糕的最小总开销①
java·数据结构·算法