Spring Security 6.x 系列(7)—— 源码分析之Builder设计模式
一、Builder设计模式
WebSecurity、HttpSecurity、AuthenticationManagerBuilder 都是框架中的构建者,把他们放到一起看看他们的共同特点:
查看AuthenticationManagerBuilder的继承结构图:

查看HttpSecurity的继承结构图:

查看WebSecurity的继承结构图:

可以看出他们都有这样一条继承树:
|- SecurityBuilder|- AbstractSecurityBuilder|- AbstractConfiguredSecurityBuilder
二、SecurityBuilder
/*** Interface for building an Object** @param <O> The type of the Object being built* @author Rob Winch* @since 3.2*/
public interface SecurityBuilder<O> {/*** Builds the object and returns it or null.* @return the Object to be built or null if the implementation allows it.* @throws Exception if an error occurred when building the Object*/O build() throws Exception;}
SecurityBuilder是一个接口,当调用它的 build() 方法时,会创建一个对象。将要创建的对象类型由泛型 O 限制。这个接口是所有构造器的顶级接口,也是Spring Security 框架中使用Builder设计模式的基础接口。
三、AbstractSecurityBuilder
/*** A base {@link SecurityBuilder} that ensures the object being built is only built one* time.** @param <O> the type of Object that is being built* @author Rob Winch**/
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {// 标记对象是否处于创建中private AtomicBoolean building = new AtomicBoolean();private O object;@Overridepublic final O build() throws Exception {if (this.building.compareAndSet(false, true)) {// 对象的实际底构建过程再 doBuild() 方法中实现this.object = doBuild();return this.object;}throw new AlreadyBuiltException("This object has already been built");}/*** 获取已生成的对象。如果尚未构建,则会引发异常。* @return the Object that was built*/public final O getObject() {if (!this.building.get()) {throw new IllegalStateException("This object has not been built");}return this.object;}/*** 子类需实现这个方法来执行对象构建。* @return the object that should be returned by {@link #build()}.* @throws Exception if an error occurs*/protected abstract O doBuild() throws Exception;}
AbstractSecurityBuilder是SecurityBuilder的一个基础实现抽象类,提供构造器的基础流程和控制,能够确保对象O只被创建一次。
这个类很简单:
-
定义了一个原子操作的对象,用来标记当前对象是否处于构造中
private AtomicBoolean building = new AtomicBoolean(); -
实现
SecurityBuilder接口的build()方法。调用
doBuild()方法完成构造,并且在调用doBuild()之前需要原子修改building为true,只有修改成功才能执行doBuild()方法,这间接的保证了:对象只构造一次,确保构造的唯一性和原子性。 -
定义一个
getObject()方法,方便获取构造的对象。 -
定义抽象方法
doBuild(),加入模板模式,交给子类自行实现。
四、AbstractConfiguredSecurityBuilder
源码注释:
A base SecurityBuilder that allows SecurityConfigurer to be applied to it. This makes modifying the SecurityBuilder a strategy that can be customized and broken up into a number of SecurityConfigurer objects that have more specific goals than that of the SecurityBuilder.一个基本的SecurityBuilder,允许将SecurityConfigurer应用于它。这使得修改SecurityBuilder的策略可以自定义并分解为许多SecurityConfigurer对象,这些对象具有比SecurityBuilder更具体的目标。
For example, a SecurityBuilder may build an DelegatingFilterProxy, but a SecurityConfigurer might populate the SecurityBuilder with the filters necessary for session management, form based login, authorization, etc.
请参阅:
WebSecurity
作者:
Rob Winch
类型形参:
<O>– The object that this builder returns 此构造器返回的对象
<B>– The type of this builder (that is returned by the base class) 此构造器的类型(由基类返回)
它继承自 AbstractSecurityBuilder ,在此之上又做了一些扩展。先来看看里面都有什么:

4.1 内部静态枚举类 BuildState
这个枚举类用来表示应用程序(构造器构造对象)的状态,代码相对简单,就不粘贴源码了。
枚举类中只有一个 int 类型的成员变量 order 表示状态编号:
-
UNBUILT(0):未构造构造器的
build方法被调用之前的状态 -
INITIALIZING(1): 初始化中构造器的
build方法第一次被调用,到所有SecurityConfigurer的init方法都被调用完这期间都是INITIALIZING状态 -
CONFIGURING(2): 配置中表示从所有的
SecurityConfigurer的init方法都被调用完,直到所有configure方法都被调用意思就是所有配置器都初始化了,直到配置都被调用这段时间都时
CONFIGURING状态 -
BUILDING(3):对象构造中表示已经执行完所有的
SecurityConfigurer的configure方法,到刚刚执行完AbstractConfiguredSecurityBuilder的performBuild方法这期间意思就是从将所有配置器的配置都配置完成开始,到构造完这个对象这段时间都是
BUILDING状态 -
BUILT(4):对象已经构造完成表示对象已经构造完成。
枚举类中还有两个方法:
-
isInitializing:INITIALIZING状态时返回true -
isConfigured:大于等于CONFIGURING的时候返回true也就是说配置器初始化完成时在构造器看来就算以配置状态了。
4.2 成员变量
private final Log logger = LogFactory.getLog(getClass());private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();private final Map<Class<?>, Object> sharedObjects = new HashMap<>();private final boolean allowConfigurersOfSameType;private BuildState buildState = BuildState.UNBUILT;private ObjectPostProcessor<Object> objectPostProcessor;
configurers:所要应用到当前SecurityBuilder上的所有的SecurityConfigurer。configurersAddedInInitializing:用于记录在初始化期间添加进来的SecurityConfigurer。sharedObjects:共享对象。ObjectPostProcessor:由外部调用者提供,这是一个后置处理对象,在创建完对象后会用到这个后置处理对象。
4.3 构造方法
/**** Creates a new instance with the provided {@link ObjectPostProcessor}. This post* processor must support Object since there are many types of objects that may be* post processed.* @param objectPostProcessor the {@link ObjectPostProcessor} to use*/
protected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor) {this(objectPostProcessor, false);
}/**** Creates a new instance with the provided {@link ObjectPostProcessor}. This post* processor must support Object since there are many types of objects that may be* post processed.* @param objectPostProcessor the {@link ObjectPostProcessor} to use* @param allowConfigurersOfSameType if true, will not override other* {@link SecurityConfigurer}'s when performing apply*/
protected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor,boolean allowConfigurersOfSameType) {Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");this.objectPostProcessor = objectPostProcessor;this.allowConfigurersOfSameType = allowConfigurersOfSameType;
}
构造函数只对两个成员变量进行了赋值:
-
objectPostProcessor:由外部调用者提供,这是一个后置处理对象,在创建完对象后会用到这个后置处理对象。 -
allowConfigurersOfSameType:从源码可以看出,默认情况下allowConfigurersOfSameType是false。这个成员变量的含义:
true表示允许相同类型的构造器,在应用配置器时不会覆盖相同类型的配。
4.4 方法
4.4.1 getOrBuild 方法
想要用构造器获取最终构造的对象时,需调用这个方法。
这个方法的逻辑很简单,调用 isUnbuilt() 方法判断对象创建状态是否未构建完成( return buildState == BuildState.UNBUILT ):
- 如果对象已经创建就直接返回已经构建好的对象,
- 否则调用构造器的
build()方法构建对象并返回已构建完成的对象。
从刚才看到的父类 AbstractSecurityBuilder 代码中可以知道真正的构建过程是调用子类 doBuild() 方法完成的。
isUnbuilt() 方法中,对configurers成员变量加了锁(synchronized),保证获取到的构建完成状态时,对象真的已经构建好了。
/*** Similar to {@link #build()} and {@link #getObject()} but checks the state to* determine if {@link #build()} needs to be called first.* @return the result of {@link #build()} or {@link #getObject()}. If an error occurs* while building, returns null.*/
public O getOrBuild() {if (!isUnbuilt()) {return getObject();}try {return build();}catch (Exception ex) {this.logger.debug("Failed to perform build. Returning null", ex);return null;}
}/*** Determines if the object is unbuilt.* @return true, if unbuilt else false*/
private boolean isUnbuilt() {synchronized (this.configurers) {return this.buildState == BuildState.UNBUILT;}
}
4.4.2 doBuild 方法
使用以下步骤对configurers执行生成:
/*** Executes the build using the {@link SecurityConfigurer}'s that have been applied* using the following steps:** <ul>* <li>Invokes {@link #beforeInit()} for any subclass to hook into</li>* <li>Invokes {@link SecurityConfigurer#init(SecurityBuilder)} for any* {@link SecurityConfigurer} that was applied to this builder.</li>* <li>Invokes {@link #beforeConfigure()} for any subclass to hook into</li>* <li>Invokes {@link #performBuild()} which actually builds the Object</li>* </ul>*/
@Override
protected final O doBuild() throws Exception {synchronized (this.configurers) {this.buildState = BuildState.INITIALIZING;beforeInit();init();this.buildState = BuildState.CONFIGURING;beforeConfigure();configure();this.buildState = BuildState.BUILDING;O result = performBuild();this.buildState = BuildState.BUILT;return result;}
}
- 构建过程对
configurers加锁。 - 方法体中时构建对象的整个流程,包括状态变化。
- 构建过程大致分为构建器初始化
beforeInit()、init(),构建器配置beforeConfigure()、configure(),构建对象performBuild()。
构建过程对
configurers加锁,也就意味着进入构建方法后configurers中的构建器应该都准备好了。这个时候如果再添加或者修改配置器都会失败。
4.4.3 beforeInit 方法 和 beforeConfigure 方法
这两个方法是抽象方法,由子类实现。子类通过覆盖这两个方法可以挂钩到对象构建的生命周期中,实现:在配置器(SecurityConfigurer)调用初始化方法或者配置方法之前做用户自定义的操作。
/*** Invoked prior to invoking each {@link SecurityConfigurer#init(SecurityBuilder)}* method. Subclasses may override this method to hook into the lifecycle without* using a {@link SecurityConfigurer}.*/
protected void beforeInit() throws Exception {
}/*** Invoked prior to invoking each* {@link SecurityConfigurer#configure(SecurityBuilder)} method. Subclasses may* override this method to hook into the lifecycle without using a* {@link SecurityConfigurer}.*/
protected void beforeConfigure() throws Exception {
}

4.4.4 init 方法
@SuppressWarnings("unchecked")
private void init() throws Exception {Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();for (SecurityConfigurer<O, B> configurer : configurers) {configurer.init((B) this);}for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {configurer.init((B) this);}
}
方法很简单功能很简单,就是遍历 configurers 和 configurersAddedInInitializing ,对里面存储的配置器进行初始化。
配置器初始化的详细内容到看配置器源码时在了解。
4.4.5 configure 方法
@SuppressWarnings("unchecked")
private void configure() throws Exception {Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();for (SecurityConfigurer<O, B> configurer : configurers) {configurer.configure((B) this);}
}private Collection<SecurityConfigurer<O, B>> getConfigurers() {List<SecurityConfigurer<O, B>> result = new ArrayList<>();for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {result.addAll(configs);}return result;
}
遍历 configurers ,调用所有配置器的 configure(SecurityBuilder b) 方法对当前的构建器(this)进行配置。
配置器配置详细内容到看配置器源码时在了解。
4.4.6 performBuild 方法
这也是一个抽象方法,需要子类实现,完成对象的创建并返回。

4.4.7 apply 方法
/*** Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and* invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.* @param configurer* @return the {@link SecurityConfigurerAdapter} for further customizations* @throws Exception*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {configurer.addObjectPostProcessor(this.objectPostProcessor);configurer.setBuilder((B) this);add(configurer);return configurer;
}/*** Applies a {@link SecurityConfigurer} to this {@link SecurityBuilder} overriding any* {@link SecurityConfigurer} of the exact same class. Note that object hierarchies* are not considered.* @param configurer* @return the {@link SecurityConfigurerAdapter} for further customizations* @throws Exception*/
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {add(configurer);return configurer;
}

这个方法的作用是将 SecurityConfigurerAdapter (配置器的适配器)或者 SecurityConfigurer (配置器)应用到当前的构建器。这两个方法是相互重载的,他们最后都调用了 add(configurer) 方法,将配置器添加到构建器,方便构建时使用(初始,配置)。
关于
SecurityConfigurerAdapter和SecurityConfigurer后面再详细了解。这里观察可以看出,他们实现了相同的接口,都可以作为add方法的参数。
而且
public <C extends SecurityConfigurerAdapter<O,B>> C apply(C configurer)throws Exception方法在6.2 版本标记为废弃。
4.4.8 add 方法
/*** Adds {@link SecurityConfigurer} ensuring that it is allowed and invoking* {@link SecurityConfigurer#init(SecurityBuilder)} immediately if necessary.* @param configurer the {@link SecurityConfigurer} to add*/
@SuppressWarnings("unchecked")
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {Assert.notNull(configurer, "configurer cannot be null");Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer.getClass();synchronized (this.configurers) {if (this.buildState.isConfigured()) {throw new IllegalStateException("Cannot apply " + configurer + " to already built object");}List<SecurityConfigurer<O, B>> configs = null;if (this.allowConfigurersOfSameType) {configs = this.configurers.get(clazz);}configs = (configs != null) ? configs : new ArrayList<>(1);configs.add(configurer);this.configurers.put(clazz, configs);if (this.buildState.isInitializing()) {this.configurersAddedInInitializing.add(configurer);}}
}
这个方法将配置器添加到一个map集合里面,这个map中以配置器的类名为 Key,以存放这个类型的配置器的 List 集合为 Value。
-
在执行添加操作时会对
configurers加锁(synchronized)。 -
通过构造方法中设置的
allowConfigurersOfSameType值判断是否允许添加相同类型的配置器,如果是true,那么在添加之前会根据类名先从map中获取该类型配置器链表(List),如果获取到了就把要添加的配置器追加到后面,然后把追加了新配置器的List再放回到map里面,如果获取到null,接创建一个新的List来存放配置器。 -
添加配置器时,如果该构建器已经处于以配置状态(大于等于
CONFIGURING.order),那么会抛出异常;如果该构建器已经处于INITIALIZING状态,那么久将这个适配器链表存放到configurersAddedInInitializing这个map中;否则将适配器链表存放到configurers这个map集合中。
遗留一个问题,没有看出来为什么要使用
configurersAddedInInitializing,如果没有configurersAddedInInitializing这个设计会出现什么并发问题吗?
4.4.9 其他方法

剩下的方法都是一些get,set,remove 方法很好理解,不做多余追述。
相关文章:
Spring Security 6.x 系列(7)—— 源码分析之Builder设计模式
一、Builder设计模式 WebSecurity、HttpSecurity、AuthenticationManagerBuilder 都是框架中的构建者,把他们放到一起看看他们的共同特点: 查看AuthenticationManagerBuilder的继承结构图: 查看HttpSecurity的继承结构图: 查看W…...
PyQt6 中自定义浮点型滑块类
介绍: 在PyQt6中,滑块(Slider)是常用的用户界面元素之一,用于选择数值范围。然而,有时候我们可能需要使用浮点数值,而标准的滑块仅支持整数。为了解决这个问题,我们可以创建一个自定…...
笔记,B+树
B树面对的场景,是一个有10亿行的表,希望某一列是有序的。这么大的数据量,内存里放不下,需要放在硬盘里。结果,原本运行于内存的二叉树,就升级为B树了。 在二叉树中,每个节点存储着一个数字&…...
代码随想录刷题题Day2
刷题的第二天,希望自己能够不断坚持下去,迎来蜕变。😀😀😀 刷题语言:C / Python Day2 任务 977.有序数组的平方 209.长度最小的子数组 59.螺旋矩阵 II 1 有序数组的平方(重点:双指针…...
【JAVA面向对象编程】--- 探索子类如何继承父类
🌈个人主页: Aileen_0v0🔥学习专栏: Java学习系列专栏 💫个人格言:"没有罗马,那就自己创造罗马~" 目录 继承 继承的普通成员方法调用 及 普通成员变量修改 构造方法的调用 子类构造方法 继承 package Inherit;class Animal …...
从浏览器控制台发送get,post请求
---------------------get请求--------------------------- fetch(url, { method: get, }) .then(response > response.json()) .then(data > { // 获取到响应的数据后的处理逻辑 console.log(data); }) .catch(error > { // 请求发生错误的处理逻…...
海外问卷调查怎么批量做?可以用指纹浏览器吗?
海外问卷调查通常是指产品与品牌上线时,基于消费者、市场调查而组织的问卷调查,包括个人、企业或其他组织,以获取关于市场、消费者行为、产品需求、社会趋势等方面的见解。 通常,研究人员或组织会设计一份问卷,通过在线…...
HarmonyOS 位置服务开发指南
位置服务开发概述 移动终端设备已经深入人们日常生活的方方面面,如查看所在城市的天气、新闻轶事、出行打车、旅行导航、运动记录。这些习以为常的活动,都离不开定位用户终端设备的位置。 当用户处于这些丰富的使用场景中时,系统的位置能力…...
ThinkPHP6学生选课管理系统
有需要请加文章底部Q哦 可远程调试 ThinkPHP6学生选课管理系统 一 介绍 此学生选课管理系统基于ThinkPHP6框架开发,数据库mysql8,前端bootstrap。系统角色分为学生,教师和管理员。学生登录后可进行选课,教师登录后可查看选课情况…...
uniapp如何与原生应用进行混合开发?
目录 前言 1.集成Uniapp 2.与原生应用进行通信 3.实现原生功能 4.使用原生UI组件 结论: 前言 随着移动应用市场的不断发展,使用原生开发的应用已经不能满足用户的需求,而混合开发成为了越来越流行的选择。其中,Uniapp作为一种跨平台的开…...
Csharp(C#)无标题栏窗体拖动代码
C#(C Sharp)是一种现代、通用的编程语言,由微软公司在2000年推出。C#是一种对象导向的编程语言,它兼具C语言的高效性和Visual Basic语言的易学性。C#主要应用于Windows桌面应用程序、Windows服务、Web应用程序、游戏开发等领域。C…...
李宏毅2020机器学习课程笔记(二)- 深度学习
相关专题: 李宏毅2020机器学习资料汇总 本系列笔记: 李宏毅2020机器学习课程笔记(一)- 分类与回归李宏毅2020机器学习课程笔记(二)- 深度学习李宏毅2020机器学习课程笔记(三)- CNN、半监督、RNN文章目录 3. Deep LearningBrief Introduction of Deep Learning(P12)Ba…...
解决电脑蓝屏问题:SYSTEM_THREAD_EXCEPTION_NOT_HANDLED,回到系统还原点
解决电脑蓝屏问题:SYSTEM_THREAD_EXCEPTION_NOT_HANDLED,回到系统还原点 1,蓝屏显示问题1.1,蓝屏1,清楚显示1.2,蓝屏2,模糊显示 2,排除故障问题3,解决蓝屏的有效方法 1&a…...
connectivity_plus 安卓build的时候报错
报错信息 当前版本:connectivity_plus 5.0.2 Flutter 3.13.6 Dart 3.1.3 A problem occurred configuring project :connectivity_plus. > Failed to create Jar file /Users/wangxiangyu/.gradle/caches/jars-8/fef84f4f98be9f93b0b593ccb1e3e207/lint-model-…...
系统部署安装-Centos7-Kafka
文章目录 安装离线安装下载安装 安装 离线安装 下载 可以前往kafka的官网进行下载 https://kafka.apache.org/downloads安装 1.创建安装目录 mdkir /opt/software/kafka mkdir /opt/kafka 2.解压 sudo tar -xzf kafka_2.12-3.6.0.tgz -C /opt/kafka --strip-components…...
94.STM32外部中断
目录 1.什么是 NVIC? 2.NVIC寄存器 3.中断优先级 4.NVIC的配置 设置中断分组编辑 配置某一个中断的优先级 5.什么是EXTI 6.EXTI和NVIC之间的关系 7.SYSCFG 的介绍 1.什么是 NVIC? NVIC是一种中断控制器,主要用于处理 ARM Cort…...
【Linux】快速上手自动化构建工具make/makefile
👀樊梓慕:个人主页 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》 🌝每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.什么是make / makefile 2…...
HarmonyOS
基本概念 1、ARKTS是由ArkUI框架提供,它是声明式UI 2、声明式UI的思想:- 关心描述UI的呈现结果,而不关心过程;- 状态驱动视图更新自定义组件的组成 关键字说明举例struct声明组件名struct ToDolist 代办组件EntryComponent装饰…...
Docker安装Oracle18c 坑已排完,放心食用
Docker安装Oracle18c 坑已排完,放心食用 0、有问题可邮件我1、拉取 oracle18c 镜像, 推荐使用 zhengqing版本的镜像2、启动容器3、等待容器启动完成, 这一步很慢很慢, 别着急4、进入容器5、修改管理员密码6、查看并设置环境变量7、设置监听模式支持以SID方式连接PDB数据库8、使…...
2023年第十二届数学建模国际赛小美赛C题雪崩防范求解分析
2023年第十二届数学建模国际赛小美赛 C题 雪崩防范 原题再现: 雪崩是极其危险的现象。现在,我们对雪崩是如何形成的已经有了很好的理解,但是我们还不能详细地预测雪崩发生的原因、时间和地点。村庄和道路可以通过各种方式防止雪崩。避免在脆…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...
解析两阶段提交与三阶段提交的核心差异及MySQL实现方案
引言 在分布式系统的事务处理中,如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议(2PC)通过准备阶段与提交阶段的协调机制,以同步决策模式确保事务原子性。其改进版本三阶段提交协议(3PC…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...
