观察者模式在spring中的应用

news2024/12/24 9:12:50

作者:王子源

1 观察者模式简介

1.1 定义

指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

1.2 角色介绍

在观察者模式中,有以下几个角色。

主题也叫被观察者(Subject):

  1. 定义被观察者必须实现的职责,
  2. 它能动态的增加取消观察者,它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:
  3. 管理观察者并通知观察者。

观察者(Observer):
观察者接受到消息后,即进行更新操作,对接收到的信息进行处理。

具体的被观察者(ConcreteSubject):
定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。

具体的观察者(ConcreteObserver):
具体的观察者,每个观察者接收到消息后的处理反应是不同的,每个观察者都有自己的处理逻辑。

1.3 观察者模式的适用场景

  1. 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
  2. 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
  3. 实现类似广播机制的功能,不需要知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播。
  4. 多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。

2 观察者模式在Spring中的应用

2.1 spring的事件监听机制

Spring事件机制是观察者模式的实现。ApplicationContext中事件处理是由ApplicationEvent类和ApplicationListener接口来提供的。如果一个Bean实现了ApplicationListener接口,并且已经发布到容器中去,每次ApplicationContext发布一个ApplicationEvent事件,这个Bean就会接到通知。ApplicationEvent 事件的发布需要显示触发,要么 Spring 触发,要么我们编码触发。spring内置事件由spring触发。我们先来看一下,如何自定义spring事件,并使其被监听和发布。

2.1.1 事件

事件,ApplicationEvent,该抽象类继承了EventObject,EventObject是JDK中的类,并建议所有的事件都应该继承自EventObject。

public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp = System.currentTimeMillis();

public ApplicationEvent(Object source) {
super(source);
}

public final long getTimestamp() {
return this.timestamp;
}
}

2.1.2 监听器

ApplicationListener,是一个接口,该接口继承了EventListener接口。EventListener接口是JDK中的,建议所有的事件监听器都应该继承EventListener。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E var1);
}

2.1.3 事件发布器

ApplicationEventPublisher,ApplicationContext继承了该接口,在ApplicationContext的抽象实现类AbstractApplicationContext中做了实现下面我们来看一下

org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");

ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
//获取当前注入的发布器,执行发布方法
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}

我们可以看到,AbstractApplicationContext中publishEvent方法最终执行发布事件的是ApplicationEventMulticaster#multicastEvent方法,下面我们再来一起看一下multicastEvent方法

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
//拿到所有的监听器,如果异步执行器不为空,异步执行
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
//执行监听方法
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}


private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}

上面介绍了非spring内置的事件发布和监听执行流程。总结一下

  1. 定义一个事件,该事件继承ApplicationEvent
  2. 定义一个监听器,实现ApplicationListener接口
  3. 定义一个事件发布器,实现ApplicationEventPublisherAware接口
  4. 调用ApplicationEventPublisher#publishEvent方法.

2.2 spring事件实现原理

上面我们讲到了spring事件的发布,那么spring事件发布之后,spring是如何根据事件找到事件对应的监听器呢?我们一起来探究一下。

spring的容器初始化过程想必大家都已十分了解,这里就不过多赘述,我们直接看refresh方法在refresh方法中,有这样两个方法,initApplicationEventMulticaster()和registerListeners()

我们先来看initApplicationEventMulticaster()方法

2.2.1 initApplicationEventMulticaster()

org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster

public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
protected void initApplicationEventMulticaster() {
//获得beanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//BeanFactory中是否有ApplicationEventMulticaster
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
//如果BeanFactory中不存在,就创建一个SimpleApplicationEventMulticaster
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}

上述代码我们可以看出,spring先从BeanFactory中获取applicationEventMulticaster如果为空,则直接创建SimpleApplicationEventMulticaster

2.2.2 registerListeners()

org.springframework.context.support.AbstractApplicationContext#registerListeners

registerListeners 是将各种实现了 ApplicationListener 的监听器注册到 ApplicationEventMulticaster 事件广播器中


protected void registerListeners() {
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}

String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
//把监听器注册到事件发布器上
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}

Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
//如果内置监听事件集合不为空
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
//执行spring内置的监听方法
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}

这里解释一下earlyApplicationListeners

earlyApplicationListeners的本质还是ApplicationListener。Spring单例Ban的实例化是在Refresh阶段实例化的,那么用户自定义的一些ApplicationListener组件自然也是在这个阶段才初始化,但是Spring容器启动过程中,在Refresh完成之前还有很多事件:如Spring上下文环境准备等事件,这些事件又是Spring容器启动必须要监听的。所以Spring定义了一个earlyApplicationListeners集合,这个集合中的Listener在factories文件中定义好,在容器Refresh之前预先实例化好,然后就可以监听Spring容器启动过程中的所有事件。

当registerListeners方法执行完成,我们的监听器已经添加到多播器SimpleApplicationEventMulticaster中了,并且earlyEvent 早期事件也已经执行完毕。但是我们发现,如果自定义了一个监听器去监听spring内置的事件,此时并没有被执行,那我们注册的监听器是如何被执行的呢?答案在finishRefresh方法中。

2.2.3 finishRefresh

org.springframework.context.support.AbstractApplicationContext#finishRefresh

protected void finishRefresh() {
clearResourceCaches();

initLifecycleProcessor();

getLifecycleProcessor().onRefresh();
//容器中的类全部初始化完毕后,触发刷新事件
publishEvent(new ContextRefreshedEvent(this));

LiveBeansView.registerApplicationContext(this);
}

如果我们想要实现在spring容器中所有bean创建完成后做一些扩展功能,我们就可以实现ApplicationListener这样我们就可以实现其功能了。至此,Spring中同步的事件监听发布模式我们就讲解完了,当然Spring还支持异步的消息监听执行机制。

2.2.4 spring中异步的监听执行机制

我们回过头来看一下ApplicationEventMulticaster#pushEvent方法

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");


ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
//获取当前注入的发布器,执行发布方法
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}

最终执行发布事件的是ApplicationEventMulticaster#multicastEvent方法,下面我们再来一起看一下multicastEvent方法

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();
//拿到所有的监听器,如果异步执行器不为空,异步执行
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
//执行监听方法
         invokeListener(listener, event);
      }
   }
}

可以看到,异步事件通知主要依靠SimpleApplicationEventMulticaster 类中的Executor去实现的,如果这个变量不配置的话默认事件通知是同步的, 否则就是异步通知了,要实现同时支持同步通知和异步通知就得从这里下手;我们上文已经分析过了在initApplicationEventMulticaster方法中有这样一段代码

if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}

如果BeanFactory中已经有了SimpleApplicationEventMulticaster则不会重新创建,那么我们可以再spring中注册一个SimpleApplicationEventMulticaster并且向其中注入对应的Executor这样我们就可以得到一个异步执行监听的SimpleApplicationEventMulticaster了,我们的通知就会通过Executor异步执行。这样可以大大提高事件发布的效率。

在springboot项目中我们可以增加一个配置类来实现

@Configuration
@EnableAsync
public class Config {
@Bean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster(){
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor());
return simpleApplicationEventMulticaster;
}

@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setKeepAliveSeconds(300);
executor.setThreadNamePrefix("thread-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}

spring项目中我们也可以增加如下xml配置

<!--定义事件异步处理-->
<bean id="commonTaskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 线程池维持处于Keep-alive状态的线程数量。如果设置了allowCoreThreadTimeOut为true,该值可能为0。
并发线程数,想达到真正的并发效果,最好对应CPU的线程数及核心数 -->
<property name="corePoolSize" value="5" />
<!-- 最大线程池容量 -->
<property name="maxPoolSize" value="20" />
<!-- 超过最大线程池容量后,允许的线程队列数 -->
<property name="queueCapacity" value="100" />
<!-- 线程池维护线程所允许的空闲时间 .单位毫秒,默认为60s,超过这个时间后会将大于corePoolSize的线程关闭,保持corePoolSize的个数 -->
<property name="keepAliveSeconds" value="300" />
<!-- 允许核心线程超时: false(默认值)不允许超时,哪怕空闲;true则使用keepAliveSeconds来控制等待超时时间,最终corePoolSize的个数可能为0 -->
<property name="allowCoreThreadTimeOut" value="true" />

<!-- 线程池对拒绝任务(无线程可用)的处理策略 -->
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
<!-- java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中 -->
<!-- java.util.concurrent.ThreadPoolExecutor$AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
</property>
</bean>

<!--名字必须是applicationEventMulticaster,因为AbstractApplicationContext默认找个-->
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<!--注入任务执行器 这样就实现了异步调用-->
<property name="taskExecutor" ref="commonTaskExecutor"></property>
</bean>

3 小结

本文主要讲解了观察者模式在spring中的应用及事件监听机制,JDK 也有实现提供事件监听机制Spring 的事件机制也是基于JDK 来扩展的。Spring 的事件机制默认是同步阻塞的,想要提升对应的效率要考虑异步事件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/43491.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

离散卡尔曼滤波实现

离散卡尔曼滤波基本理论 卡尔曼预报器、平滑器可以参考之前的博客&#xff1a;(2条消息) 卡尔曼滤波器_KPer_Yang的博客-CSDN博客 下面贴上一张图1&#xff0c;很直观&#xff1a;分成时间更新和测量更新两步&#xff0c;其中的KKK和PPP有可能随着时间推移变成常数&#xff0…

STM32实战总结:HAL之RTC

RTC基础知识参考&#xff1a; 51单片机内部外设&#xff1a;实时时钟(SPI)_路溪非溪的博客-CSDN博客 STM32中的RTC 51单片机通常是外置的RTC芯片如DS1302&#xff0c;那么STM32的RTC是什么情况呢&#xff1f; STM32芯片自带RTC&#xff0c;因此不须像其他MCU需外接RTC模块。 先…

年末盘点Android 过去一年与未来的一个走势~

随着Android的发展&#xff0c;有些人对Android未来感到茫然&#xff0c;不少人可能会产生这样的疑惑&#xff1a;“从事Android是不是没有前途&#xff0c;Android开发还有什么值得学&#xff1f;“这类话题一直让大家争论不休&#xff0c;它并没有一个确切、唯一的标准答案&a…

中介者模式

思考中介者模式 当多个类&#xff08;对象&#xff09;耦合严重时&#xff0c;通过中介者模式创建一个中介者&#xff0c;多个类不直接交互了&#xff0c;变成和中介者进行交互&#xff0c;松散耦合 1.中介者模式的本质 中介者模式的本质:封装交互。 中介者模式的目的&#xff…

关于无感刷新Token,我是这样子做的

本文正在参加「金石计划 . 瓜分6万现金大奖」 什么是JWT JWT是全称是JSON WEB TOKEN&#xff0c;是一个开放标准&#xff0c;用于将各方数据信息作为JSON格式进行对象传递&#xff0c;可以对数据进行可选的数字加密&#xff0c;可使用RSA或ECDSA进行公钥/私钥签名。 使用场景…

WPSpell将拼写检查添加到VCL应用程序

WPSpell将拼写检查添加到VCL应用程序 WPSpell包括键入功能时的拼写。拼写错误的单词带有下划线&#xff0c;可以使用上下文菜单进行更正。它还包括一个传统的拼写检查对话框&#xff0c;并支持多个词典。WPSpell特别适合与WPTools一起使用。 WPSpell功能 键入时进行拼写检查。 …

1-FreeRTOS入门指南

本专栏是根据官方提供的文档进行FreeRTOS的各个功能函数的说明&#xff0c;以及函数的使用 本专栏不涉及动手操作&#xff0c;只是对原理进行说明&#xff0c;FreeRTOS基础知识篇更新完成会对如何在开发板上进行上手实战操作。 这里不会对比其他RTOS的优缺点&#xff0c;因为每…

2、Redis中简单动态字符串的简介,也就是Redis中的键和值的字符串底层表达

简介 首先在Redis中&#xff0c;没有直接使用C语言传统字符串表示(以空字符结尾的字符数组,以下简称C字符串)&#xff0c;而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型(可以简单的理解为Java中的String 类)&#xff0c;并且将SDS用作Redis的默…

动态规划算法(1)

认识动态规划 动态规划的求解思路&#xff1a; 1. 把一个问题分解成若干个子问题 2. 将中间结果保存以避免重复计算 基本步骤&#xff1a; 1. 找出最优解的性质&#xff0c;然后刻画结构特征 &#xff08;找规律&#xff09; 2. 最优解(最好的解决方案 定义) 循环(递归) 3. 以…

我与梅西粉丝们的世界杯观球日常

世界杯 ⚽️ 期间&#xff0c;我与其他的梅西粉丝在某 APP 里建了个梅粉聊天群&#xff0c;群内人数上万人&#xff0c;大家一起讨论赛事热点&#xff0c;可谓热火朝天&#xff0c;此起彼伏&#xff0c;这是四年一度的狂欢&#xff0c;虽值冬季&#xff0c;但热情不减。 “阿根…

配置设备远程管理—eNSP

案例&#xff1a;给路由器配置远程管理&#xff0c;使一台路由器远程管理另一台。 所需设备&#xff1a;两台路由器&#xff0c;一根网线 图示 一、给两台设备配置IP地址 AR1&#xff08;以下命令&#xff09; a. sy b. int g0/0/0 c. ip add 1.1.1.1 24AR2 a. sy b. int g0/0…

十分钟学完简单工厂,普通工厂,抽象工厂

快速学习简单工厂&#xff0c;普通工厂&#xff0c;抽象工厂前言&#xff1a;产品等级和产品族工厂模式作用简单工厂模式uml代码优缺点普通工厂模式uml代码优缺点抽象工厂模式uml代码优缺点前言&#xff1a;产品等级和产品族 在学习工厂模式之前&#xff0c;先得了解一下产品等…

Redis实践

一、持久化 Redis 的数据 全部存储 在 内存 中&#xff0c;如果 突然宕机&#xff0c;数据就会全部丢失&#xff0c;因此必须有一套机制来保证 Redis 的数据不会因为故障而丢失&#xff0c;这种机制就是 Redis 的 持久化机制&#xff0c;它会将内存中的数据库状态 保存到磁盘 …

Spring——AOP原理及流程详解

AOP原理及流程详解一、AOP结构介绍Pointcut通知原理连接点拦截器二、Bean介入点EnableAspectJAutoProxyAspectJAutoProxyRegistrarAnnotationAwareAspectJAutoProxyCreatorAbstractAutoProxyCreator实例前执行初始化后执行循环依赖会调用总结三、处理切面获取所有切面其下通知方…

国内饮料行业数据浅析

大家好&#xff0c;这里是小安说网控。 饮料一直深得年轻人的宠爱&#xff0c;主要消费品类为饮用水、碳酸饮料、奶制品、气泡水等。刚刚过去的十月份&#xff0c;我国饮料产量当期值1199.6万吨&#xff0c;同比下降6.1%&#xff1b;今年1-10月份&#xff0c;饮料产量累计值157…

这几个点让我买了Watch Ultra

01.凑够Apple 全家桶 MacBook ProiPhoneAirPodsiPad 02.可以解锁iPhone手机&#xff0c;MacBook,iPad 03.当iPhone 来电话&#xff0c;不方便接听&#xff0c;可以使用Watch接听(虽然这种情况挺少) 04.可以连接AirPods 听音乐 05.花10元钱开卡&#xff0c;iPhone和Watch 可以…

前端ES6-ES11新特性

ES6新特性 变量声明 let a; let b,c,d; let e 100; let f 521, g iloveyou, h [];块级作用域 {let girl 周扬青; }console.log(girl); //这里会报错&#xff0c;变量不在作用域内,用var声明就可以常量声明 const NAME tom; //必须赋予初始值&#xff0c;变量名大写&am…

3-7数据链路层-设备

文章目录一.网桥1.基本原理2.透明网桥&#xff08;1&#xff09;工作原理&#xff08;2&#xff09;自学习算法3.源路由网桥二.局域网交换机1.局域网交换机2.原理3.特点4.两种交换模式5.交换机的自学习算法一.网桥 网桥根据MAC帧的目的地址对帧进行转发和过滤。当网桥收到一个…

Java中数组、集合初始化及遍历方式

一、数组 1. 一维数组 一维数组两种初始化方式 静态初始化 int[] array {1,2,3};int[] array new int[]{1,2,3};动态初始化 int[] array new int[3]; array[0]1; array[1]2; array[2]3;一维数组两种遍历方式 普通for循环for (int i 0; i < array.length; i) {System.ou…

全家桶Spring、HikariCP、Mybatis和Oracle配置,你想要的都在这里

目录1、HikariCP配置说明2、spring配置文件里&#xff0c;配置HikariCP数据库连接池3、注意连接池大小设置&#xff0c;重点推荐官方说明文档4、HikariCP配置5、数据库配置文件1、HikariCP配置说明 HikariCP: https://github.com/brettwooldridge/HikariCP 2、spring配置文件…