当前位置: 首页 > news >正文

Spring Security 6.x 系列(7)—— 源码分析之Builder设计模式

一、Builder设计模式

WebSecurityHttpSecurityAuthenticationManagerBuilder 都是框架中的构建者,把他们放到一起看看他们的共同特点:

查看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;}

AbstractSecurityBuilderSecurityBuilder的一个基础实现抽象类,提供构造器的基础流程和控制,能够确保对象O只被创建一次。

这个类很简单:

  • 定义了一个原子操作的对象,用来标记当前对象是否处于构造中

    private AtomicBoolean building = new AtomicBoolean();
    
  • 实现SecurityBuilder接口的 build() 方法。

    调用 doBuild() 方法完成构造,并且在调用 doBuild() 之前需要原子修改 buildingtrue,只有修改成功才能执行 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 方法第一次被调用,到所有 SecurityConfigurerinit 方法都被调用完这期间都是 INITIALIZING 状态

  • CONFIGURING(2): 配置中

    表示从所有的 SecurityConfigurerinit 方法都被调用完,直到所有 configure 方法都被调用

    意思就是所有配置器都初始化了,直到配置都被调用这段时间都时 CONFIGURING 状态

  • BUILDING(3) :对象构造中

    表示已经执行完所有的 SecurityConfigurerconfigure 方法,到刚刚执行完 AbstractConfiguredSecurityBuilderperformBuild 方法这期间

    意思就是从将所有配置器的配置都配置完成开始,到构造完这个对象这段时间都是 BUILDING 状态

  • BUILT(4) :对象已经构造完成

    表示对象已经构造完成。

枚举类中还有两个方法:

  • isInitializingINITIALIZING状态时返回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:从源码可以看出,默认情况下 allowConfigurersOfSameTypefalse

    这个成员变量的含义:

    • 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);}
}

方法很简单功能很简单,就是遍历 configurersconfigurersAddedInInitializing ,对里面存储的配置器进行初始化。

配置器初始化的详细内容到看配置器源码时在了解。

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) 方法,将配置器添加到构建器,方便构建时使用(初始,配置)。

关于 SecurityConfigurerAdapterSecurityConfigurer 后面再详细了解。这里观察可以看出,他们实现了相同的接口,都可以作为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 其他方法

在这里插入图片描述

剩下的方法都是一些getsetremove 方法很好理解,不做多余追述。

相关文章:

Spring Security 6.x 系列(7)—— 源码分析之Builder设计模式

一、Builder设计模式 WebSecurity、HttpSecurity、AuthenticationManagerBuilder 都是框架中的构建者&#xff0c;把他们放到一起看看他们的共同特点&#xff1a; 查看AuthenticationManagerBuilder的继承结构图&#xff1a; 查看HttpSecurity的继承结构图&#xff1a; 查看W…...

PyQt6 中自定义浮点型滑块类

介绍&#xff1a; 在PyQt6中&#xff0c;滑块&#xff08;Slider&#xff09;是常用的用户界面元素之一&#xff0c;用于选择数值范围。然而&#xff0c;有时候我们可能需要使用浮点数值&#xff0c;而标准的滑块仅支持整数。为了解决这个问题&#xff0c;我们可以创建一个自定…...

笔记,B+树

B树面对的场景&#xff0c;是一个有10亿行的表&#xff0c;希望某一列是有序的。这么大的数据量&#xff0c;内存里放不下&#xff0c;需要放在硬盘里。结果&#xff0c;原本运行于内存的二叉树&#xff0c;就升级为B树了。 在二叉树中&#xff0c;每个节点存储着一个数字&…...

代码随想录刷题题Day2

刷题的第二天&#xff0c;希望自己能够不断坚持下去&#xff0c;迎来蜕变。&#x1f600;&#x1f600;&#x1f600; 刷题语言&#xff1a;C / Python Day2 任务 977.有序数组的平方 209.长度最小的子数组 59.螺旋矩阵 II 1 有序数组的平方&#xff08;重点&#xff1a;双指针…...

【JAVA面向对象编程】--- 探索子类如何继承父类

&#x1f308;个人主页: Aileen_0v0&#x1f525;学习专栏: Java学习系列专栏 &#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 继承 继承的普通成员方法调用 及 普通成员变量修改 构造方法的调用 子类构造方法 继承 package Inherit;class Animal …...

从浏览器控制台发送get,post请求

---------------------get请求--------------------------- fetch(url, { method: get, }) .then(response > response.json()) .then(data > { // 获取到响应的数据后的处理逻辑 console.log(data); }) .catch(error > { // 请求发生错误的处理逻…...

海外问卷调查怎么批量做?可以用指纹浏览器吗?

海外问卷调查通常是指产品与品牌上线时&#xff0c;基于消费者、市场调查而组织的问卷调查&#xff0c;包括个人、企业或其他组织&#xff0c;以获取关于市场、消费者行为、产品需求、社会趋势等方面的见解。 通常&#xff0c;研究人员或组织会设计一份问卷&#xff0c;通过在线…...

HarmonyOS 位置服务开发指南

位置服务开发概述 移动终端设备已经深入人们日常生活的方方面面&#xff0c;如查看所在城市的天气、新闻轶事、出行打车、旅行导航、运动记录。这些习以为常的活动&#xff0c;都离不开定位用户终端设备的位置。 当用户处于这些丰富的使用场景中时&#xff0c;系统的位置能力…...

ThinkPHP6学生选课管理系统

有需要请加文章底部Q哦 可远程调试 ThinkPHP6学生选课管理系统 一 介绍 此学生选课管理系统基于ThinkPHP6框架开发&#xff0c;数据库mysql8&#xff0c;前端bootstrap。系统角色分为学生&#xff0c;教师和管理员。学生登录后可进行选课&#xff0c;教师登录后可查看选课情况…...

uniapp如何与原生应用进行混合开发?

目录 前言 1.集成Uniapp 2.与原生应用进行通信 3.实现原生功能 4.使用原生UI组件 结论: 前言 随着移动应用市场的不断发展&#xff0c;使用原生开发的应用已经不能满足用户的需求&#xff0c;而混合开发成为了越来越流行的选择。其中&#xff0c;Uniapp作为一种跨平台的开…...

Csharp(C#)无标题栏窗体拖动代码

C#&#xff08;C Sharp&#xff09;是一种现代、通用的编程语言&#xff0c;由微软公司在2000年推出。C#是一种对象导向的编程语言&#xff0c;它兼具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,回到系统还原点

解决电脑蓝屏问题&#xff1a;SYSTEM_THREAD_EXCEPTION_NOT_HANDLED&#xff0c;回到系统还原点 1&#xff0c;蓝屏显示问题1.1&#xff0c;蓝屏1&#xff0c;清楚显示1.2&#xff0c;蓝屏2&#xff0c;模糊显示 2&#xff0c;排除故障问题3&#xff0c;解决蓝屏的有效方法 1&a…...

connectivity_plus 安卓build的时候报错

报错信息 当前版本&#xff1a;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&#xff1f; 2.NVIC寄存器 3.中断优先级 4.NVIC的配置 设置中断分组​编辑 配置某一个中断的优先级 5.什么是EXTI 6.EXTI和NVIC之间的关系 7.SYSCFG 的介绍 1.什么是 NVIC&#xff1f; NVIC是一种中断控制器&#xff0c;主要用于处理 ARM Cort…...

【Linux】快速上手自动化构建工具make/makefile

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.什么是make / makefile 2…...

HarmonyOS

基本概念 1、ARKTS是由ArkUI框架提供&#xff0c;它是声明式UI 2、声明式UI的思想&#xff1a;- 关心描述UI的呈现结果&#xff0c;而不关心过程&#xff1b;- 状态驱动视图更新自定义组件的组成 关键字说明举例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题 雪崩防范 原题再现&#xff1a; 雪崩是极其危险的现象。现在&#xff0c;我们对雪崩是如何形成的已经有了很好的理解&#xff0c;但是我们还不能详细地预测雪崩发生的原因、时间和地点。村庄和道路可以通过各种方式防止雪崩。避免在脆…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

消息队列系统设计与实践全解析

文章目录 &#x1f680; 消息队列系统设计与实践全解析&#x1f50d; 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡&#x1f4a1; 权衡决策框架 1.3 运维复杂度评估&#x1f527; 运维成本降低策略 &#x1f3d7;️ 二、典型架构设计2.1 分布式事务最终一致…...

恶补电源:1.电桥

一、元器件的选择 搜索并选择电桥&#xff0c;再multisim中选择FWB&#xff0c;就有各种型号的电桥: 电桥是用来干嘛的呢&#xff1f; 它是一个由四个二极管搭成的“桥梁”形状的电路&#xff0c;用来把交流电&#xff08;AC&#xff09;变成直流电&#xff08;DC&#xff09;。…...