SpringBoot3核心原理
# 事件与监听器
# 生命周期监听
生命周期会监听SpringBoot运作过程是周期 , 监听需要重写 SpringApplicationRunListener
接口
SpringApplicationRunListener
接口重写的方法
方法 | 参数说明 | 说明 |
---|---|---|
starting() | ConfigurableBootstrapContext : 引导整个项目启动器 | 引用开始就调用(监听器管理一旦加载完运行) |
environmentPrepared() | ConfigurableBootstrapContext : 上下文引导 ConfigurableEnvironment : 环境 | 环境准备完成 (ico容器未创建) |
contextPrepared() | ConfigurableApplicationContext : 应用程序上下文 | ioc容器创建准备完成 , 并关闭引用启动器 (但 sources主配置类 未加载) |
contextLoaded() | ConfigurableApplicationContext : 应用程序上下文 | ioc容器加载 (主配置类加载) , 但 ico容器的Bean未加载 |
started() | ConfigurableApplicationContext : 应用程序上下文 Duration : 启动程序所需的时间 或 null | ioc容器刷新 (所有Bean初始化) , 但 runner 未调用 |
ready() | ConfigurableApplicationContext : 应用程序上下文 Duration : 启动程序所需的时间 或 null | ioc容器刷新 , runner 也调用完成 |
failed() | ConfigurableApplicationContext : 应用程序上下文 Throwable : 失败 | 捕捉starting()之后的步骤异常情况 |
SpringBoot生命周期运作流程 : (分三大部分)
引导部分
- staring : 启动
- environmentPrepared : 环境准备完成
启动部分
- contextPrepared : ioc创建准备完成 , 主程序未加载
- contextLoaded : ioc加载 , 并未刷新
- started : ioc刷新 runner未调用
- ready : ioc刷新 runner未调用完成
- failed : 启动失败 (失败情况下)
运行部分
contxt.isRunning() 运行中
应用步骤 :
创建 自定义监听类 MyApplistener实现 SpringApplicationRunListener接口
重写接口所有方法 (打印查看运行状况)
新建文件 加载文件
/resources/META-INF/spring.factories
# 后面写的是 自定义监听类全限定名路径 org.springframework.boot.SpringApplicationRunListener=com.sans.demo2.listener.MyAppListener
运行Application类 查看控制台打印即可
未重写默认是以下配置
org.springframework.boot.SpringApplicationRunListener=\org.springframework.boot.context.event.EventPublishingRunListener
在Application启动类中跟踪可以看到 , 该方法是根据指定路径配置文件进行加载的监听器
# 事件触发时机
SpringBoot在启动的时候会有很多事件触发 , 这些事件的触发可以自定义定义一些业务实现
可以通过上面应用的 生命周期应用的 SpringApplicationRunListener 作为简单的实例去理解!
各种监听器回调
类/接口 | 感知范围 | 引用 | 说明 |
---|---|---|---|
BootstrapRegistryInitializer | 引导初始化 | - META-INF/spring.factories | 用于在应用启动很早期进行一些初始化逻辑 |
ApplicationContextInitializer | Ioc容器初始化 | - META-INF/spring.factories | 用于在应用Ioc容器创建前进行一些定制化 |
ApplicationListener | 全局事件感知 | - META-INF/spring.factories - @EventListener - Ioc容器注册 | 用于感知SpringBoot应用全局事件 , 并执行对应逻辑 |
SpringApplicationRunListener | 生命周期感知 | - META-INF/spring.factories | 用于感知SpringBoot应用的启动阶段 |
ApplicationRunner | 应用就绪Ready | - Ioc容器注册 | 用于应用启动完成后执行一些初始化逻辑 , 只执行一次 |
CommandLineRunner | 应用就绪Ready | - Ioc容器注册 | 同ApplicationRunner , 但这是面向命令行参数的 , 用来解析命令行参数 |
实战应用
- 项目启动前 采用 :
BootstrapRegistryInitializer
/ApplicationContextInitializer
- 项目启动后 采用 :
ApplicationRunner
/CommandLineRunner
- 生命周期 采用 :
SpringApplicationRunListener
- 全局事件 采用 :
ApplicationListener
# 事件监听
# 前置概念
监听器机制是Spring框架提供的一种主体对象状态变化的机制 . 当被监听的对象发生变化时 , 相关的监听器就会被通知 , 进行一些相应的处理
比如我们可以用监听器监听Spring容器的启动和刷新事件 , 然后在事件发生后做一些初始化操作
监听器三大组件 :
- 事件(ApplicationEvent)
- 事件监听器(ApplicationListener)
- 事件发布者(ApplicationEventPublisher)
# ApplicationListener监听
ApplicationListener触发的事件 分别有9个 又称 9大事件 :
顺序 | 事件 | 时机 |
---|---|---|
1 | ApplicationStartingEvent | 应用启动但未做任何事情 , 除过注册listeners and initializers |
2 | ApplicationEnvironmentPreparedEvent | Environment准备完成 , 可以获取环境变量等信息 |
3 | ApplicationContextInitializedEvent | ApplicationContext初始化完成 |
4 | ApplicationPreparedEvent | ApplicationContext准备完成 , Bean定义加载完成 |
5 | ApplicationStartedEvent | 应用启动完成 |
6 | AvailabilityChangeEvent | 应用可用性变化 LivenessState.CORRECT 探针应用存活 |
7 | ApplicationReadyEvent | 应用启动完成 , ApplicationContext也加载完成 |
8 | AvailabilityChangeEvent | 应用可用性变化 ReadinessState.ACCEPTING_TRAFFIC 就绪探针 |
9 | ApplicationFailedEvent | 应用启动失败 |
示例 : 所有事件时机Demo (opens new window)
生命周期流程图 :
事件&监听器 时序图 :
# 事件驱动
事件驱动我们不难理解 , 前面前置的三大监听组件可以得知 , 事件发布者 广播发布 事件 , 监听器会接收与之对应的对象类型
- 发布事件 :
ApplicationEventPublisherAware
接口实现 , 重写自动回调获取API操作接口 - 监听事件 :
ApplicationListener<事件对象>
接口实现 , 重写触发事件方法 - 事件对象 :
ApplicationEvent
继承该类 , 标识事件监听对象
监听方式
ApplicationListener
接口 实现 , 接口的泛型为 事件对象 , 并重写onApplicationEvent()
方法 触发监听事件@EventListener
注解 实现 , 注解方法上 , 参数必须为监听的 事件对象
示例 : Demo案例 (opens new window)
假如我要实现登录事件 , 实现 登录后欢迎 和 随机发放优惠券 功能 , 通过事件进行触发这些服务
事件对象 点击展开
省略User对象实体
public class LoginSuccessEvent extends ApplicationEvent {
public LoginSuccessEvent(User user) {
super(user);
}
}
事件发布者 点击展开
@Component
public class EventPublisher implements ApplicationEventPublisherAware {
ApplicationEventPublisher applicationEventPublisher;
/**
* 底层自动回调传入API接口操作
* @param applicationEventPublisher 对象要使用的事件发布者
*/
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
/**
* 所有事件均可发送
* @param event
*/
public void sendEvent(ApplicationEvent event) {
// 调用底层API发送事件
applicationEventPublisher.publishEvent(event);
}
}
Contorller层登录触发 点击展开
@RestController
public class LoginController {
@Resource
private EventPublisher eventPublisher;
@GetMapping("/login")
public String login(String username, String password) {
// 创建事件
LoginSuccessEvent event = new LoginSuccessEvent(new User(username, password));
// 发布事件
eventPublisher.sendEvent(event);
return "[" + username + "]登录成功";
}
}
监听事件&登录欢迎 点击展开
通过 ApplicationListener接口 实现监听
@Service
public class AccountService implements ApplicationListener<LoginSuccessEvent> {
public void welcome(String username) {
System.out.println("欢迎 "+username+" 到来!");
}
@Override
public void onApplicationEvent(LoginSuccessEvent event) {
User user = (User) event.getSource();
String username = user.getUsername();
System.out.println( username + "监听收到事件!");
welcome(username);
}
}
监听事件&发送优惠券 点击展开
通过 @EventListener
注解 实现监听
@Service
public class CouponService {
public void sendCoupon(String username) {
System.out.println(username+" 随机获取一张优惠券!");
}
@EventListener
public void onEvent(LoginSuccessEvent event) {
User user = (User) event.getSource();
String username = user.getUsername();
System.out.println( username+ "监听收到事件!");
sendCoupon(username);
}
}
# 自动配置
SpringBoot 非常重要的一个特性 , 可以根据你添加的jar依赖自动配置 , 例如引入有关 starter相关的Spring应用 spring-boot-starter-web
, spring-boot-starter-redis
...
意图 : 通过外部jar包自动加载Bean , 搭配 @ConditionalOnClass
、@ConditionalOnBean
实现
自定义自动配置引用步骤 :
- 导入依赖
starter
和autoconfigure
- 创建类路径文件
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
它在运行后会寻找该类路径进行执行 - 创建 配置类
xxxAutoConfiguration
, 添加@AutoConfiguration
注解 - (可选) 配置与属性对应注入
@EnableConfigurationProperties
注解 (注解值指定 配置属性类) - (可选) 创建 配置属性类
xxxProperties
, 添加@ConfigurationProperties
注解 (注解值指定 配置文件前缀绑定) - (可选) 配置属性类 定义成员变量属性 , 后缀为成员属性名进行绑定
- 在 配置类上写个
@Bean
初始实例化 并且参数引用 配置属性类 - 测试引用
SPI机制示例 : 自动配置Demo (opens new window) , 日志案例Demo (opens new window)
SPI机制
该机制用于 动态发现和加载组件 , 在运行程序后加载
# @SpringBootApplication
@SpringBootApplication注解使启动类具有配置类、自动配置和组件扫描的功能 , 从而简化了Spring Boot应用的配置和开发
@SpringBootConfiguation . 标识配置类(装配Ioc容器)
@EnableAutoConfiguation . 开启自动配置
@AutoConfigurationPackage 扫描主程序包 , 加载自己的组件
- @Import({AutoConfigurationPackages.Registrar.class}) . 主程序所在的包路径下的所有其他包以及子包
@Import({AutoConfigurationImportSelector.class}) . 加载自动配置类 , 加载starter导入的组件
扫描SPI文件加载
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@ComponentScan . 排除组件 (排除已经扫描的配置类 和 自动配置类)
# 完整运作流程
断点理解技巧 :
- 根据启动类
@SpringBootApplication
注解 入手跟踪@Import
注解 - 断点跟踪 方法栈顺序跟踪
- 反向思维 : Bean构造方法 , ApplicationListener接口 事件全感知 跟踪
# 自定义Starter
虽说官方提供了很多Starter , 也并非能满足所有场景 , 假如项目有个特殊的需求更改原生Starter进行封装 , 那么其他模块也会引用该模块封装的API
大致步骤 :
抽离业务代码
配置价值模式
使用 @EnableXxx机制 (手动注解引用)
使用以上注解搭配 @Import 加载配置类 进行统一配置
使用SPI机制 (完全自动引入)
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件 自动加载配置类
实际业务场景
需求 : 通过导入此 stater 实现接口具有打招呼功能 , 问候语携带人名 (人名从配置信息中读取)