Spring Security中的认证实现
Spring Security认证架构概述
Spring Security的认证流程建立在精心设计的组件协作体系之上。图3.1展示了该框架实现认证过程的核心架构,这个架构由多个关键组件构成,理解这些组件的交互关系对于任何Spring Security实现都至关重要。
认证流程核心组件
认证流程始于AuthenticationFilter,它负责拦截传入请求并将认证任务委托给AuthenticationManager。AuthenticationManager作为中央调度器,并不直接处理认证逻辑,而是通过AuthenticationProvider来执行具体的认证操作。这种分层设计体现了职责分离原则,使得系统各组件能够专注于单一功能。
在验证用户名和密码时,AuthenticationProvider依赖于两个核心组件:
- UserDetailsService:负责按用户名检索用户信息
- PasswordEncoder:处理密码的编码与验证
// 典型认证流程伪代码示例
Authentication authentication = authenticationFilter.attemptAuthentication(request);
AuthenticationManager.authenticate(authentication);
authenticationProvider.authenticate(authentication);
用户管理组件
用户管理部分主要涉及以下接口:
UserDetailsService接口
该接口仅定义了一个核心方法:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
这种单一职责设计符合接口隔离原则,当应用仅需认证功能时,只需实现此基础接口。
UserDetailsManager接口
作为UserDetailsService的扩展,增加了用户管理功能:
void createUser(UserDetails user);
void updateUser(UserDetails user);
void deleteUser(String username);
这种设计允许应用根据需要选择实现层级,避免强制实现不需要的功能。
用户表示与权限控制
Spring Security通过UserDetails契约来理解用户模型,该接口要求实现以下关键要素:
- 用户凭证(用户名/密码)
- 权限集合(GrantedAuthority)
- 账户状态控制方法
public interface UserDetails {String getUsername();String getPassword();Collection getAuthorities();boolean isAccountNonExpired(); // 其他状态方法...
}
GrantedAuthority接口定义了权限的抽象表示:
public interface GrantedAuthority {String getAuthority();
}
实际应用中,可以通过两种方式创建权限实例:
// 使用lambda表达式
GrantedAuthority readAuthority = () -> "READ";// 使用SimpleGrantedAuthority类
GrantedAuthority writeAuthority = new SimpleGrantedAuthority("WRITE");
架构优势与实践建议
这种架构设计提供了显著的灵活性优势:
- 可扩展性:可以单独替换任何组件实现
- 职责明确:各组件专注单一功能
- 渐进式复杂:从简单实现开始,逐步增加功能
建议在实践中:
- 对简单应用使用Spring Security提供的User构建器
- 复杂系统应采用适配器模式分离领域模型与安全模型
- 权限设计应遵循最小权限原则
// 使用User构建器创建用户实例
UserDetails admin = User.withUsername("admin").password("{bcrypt}$2a$10$...").authorities("ROLE_ADMIN", "WRITE").accountLocked(false).build();
理解这些核心组件的协作关系,将帮助开发者在实际项目中做出更合理的技术选型与实现决策。后续章节将深入探讨各组件的具体实现方式和应用场景。
用户详情表示与实现
UserDetails接口规范解析
Spring Security通过UserDetails接口定义了用户模型的标准化表示方式,该接口包含七个核心方法:
public interface UserDetails extends Serializable {String getUsername(); // 获取用户名String getPassword(); // 获取加密后的密码Collection getAuthorities(); // 获取权限集合boolean isAccountNonExpired(); // 账户是否未过期boolean isAccountNonLocked(); // 账户是否未锁定boolean isCredentialsNonExpired(); // 凭证是否未过期boolean isEnabled(); // 账户是否启用
}
接口设计特点:
- 认证相关:仅
getUsername()和getPassword()直接参与认证过程 - 授权控制:
getAuthorities()返回权限集合,用于后续授权决策 - 状态管理:四个
is...()方法构成账户状态检查机制
权限表示与GrantedAuthority实现
权限通过GrantedAuthority接口表示,该接口采用极简设计:
public interface GrantedAuthority extends Serializable {String getAuthority(); // 返回权限标识字符串
}
实际开发中创建权限的两种典型方式:
// 方式1:使用lambda表达式
GrantedAuthority readAuth = () -> "ARTICLE_READ";// 方式2:使用SimpleGrantedAuthority工具类
GrantedAuthority writeAuth = new SimpleGrantedAuthority("ARTICLE_WRITE");
权限命名规范建议:
- 使用大写字母和下划线组合(如
INVENTORY_MANAGE) - 业务相关前缀避免冲突(如
ORDER_、REPORT_等) - 与Spring Security角色前缀
ROLE_区分使用
基础实现方案
静态用户实现示例
最简单的UserDetails实现类,适用于固定用户场景:
public class StaticUser implements UserDetails {@Overridepublic String getUsername() {return "system";}@Overridepublic String getPassword() {return "{bcrypt}$2a$10$N9qo8uLOickgx2ZMRZoMy...";}@Overridepublic Collection getAuthorities() {return List.of(new SimpleGrantedAuthority("ADMIN"));}// 其他状态方法默认返回true@Overridepublic boolean isAccountNonExpired() { return true; }// ...省略其他方法实现
}
动态用户实现方案
更符合实际业务的实现方式,支持创建不同用户实例:
public class DomainUser implements UserDetails {private final String username;private final String encodedPassword;private final List authorities;public DomainUser(String username, String encodedPassword, String... authorities) {this.username = username;this.encodedPassword = encodedPassword;this.authorities = Arrays.stream(authorities).map(SimpleGrantedAuthority::new).collect(Collectors.toList());}// 实现各接口方法返回对应字段@Overridepublic Collection getAuthorities() {return Collections.unmodifiableList(authorities);}// ...其他方法实现
}
User构建器实践
Spring Security提供的User构建器可快速创建用户实例:
// 基础构建方式
UserDetails user = User.withUsername("developer").password("{bcrypt}$2a$10$N9qo8uLOickgx2ZMRZoMy...").authorities("CODE_READ", "CODE_WRITE").build();// 带账户状态的构建
UserDetails admin = User.withUsername("admin").passwordEncoder(plain -> BCrypt.hashpw(plain, BCrypt.gensalt())).password("secret123").accountExpired(false).credentialsExpired(false).disabled(false).authorities("USER_MANAGE", "SYSTEM_CONFIG").build();
构建器核心特性:
- 支持链式调用
- 提供密码编码器插槽
- 生成不可变用户实例
- 内置参数校验(如用户名非空)
职责分离实践方案
推荐采用适配器模式分离业务用户与安全用户:
// JPA实体类(纯领域模型)
@Entity
public class Account {@Id private Long id;private String loginId;private String passHash;private boolean active;// 其他业务字段...
}// 安全适配器类
public class AccountUserDetails implements UserDetails {private final Account account;public AccountUserDetails(Account account) {this.account = account;}@Overridepublic String getUsername() {return account.getLoginId();}@Overridepublic boolean isEnabled() {return account.isActive();}// 其他方法实现...
}
这种实现方式:
- 保持领域模型纯洁性
- 避免安全逻辑污染业务代码
- 支持灵活的安全策略变更
- 便于单元测试隔离
实现选择建议
根据应用场景选择合适方案:
| 场景特点 | 推荐方案 | 优势说明 |
|---|---|---|
| 固定测试用户 | 静态实现类 | 简单直接,零依赖 |
| 简单动态用户 | User构建器 | 快速实现,内置验证逻辑 |
| 复杂业务系统 | 适配器模式 | 职责分离,易于维护扩展 |
| 需要特殊密码处理 | 自定义UserDetails | 完全控制密码处理流程 |
用户管理接口设计
核心接口职责划分
Spring Security通过UserDetailsService与UserDetailsManager两个接口实现了用户管理功能的模块化设计。这种分离体现了接口隔离原则(Interface Segregation Principle)的精髓:
// 基础检索接口
public interface UserDetailsService {UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}// 扩展管理接口
public interface UserDetailsManager extends UserDetailsService {void createUser(UserDetails user);void updateUser(UserDetails user);void deleteUser(String username);void changePassword(String oldPassword, String newPassword);boolean userExists(String username);
}
关键设计考量:
- 最小接口:UserDetailsService仅要求实现用户检索功能,满足基础认证需求
- 渐进式复杂:UserDetailsManager在基础接口上扩展CRUD操作
- 可选实现:应用可根据需求选择实现层级,避免强制实现不需要的功能
实际应用场景分析
只读用户系统
当用户数据来自外部系统(如LDAP)时,只需实现UserDetailsService:
@Service
public class LdapUserService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) {// 调用LDAP查询接口LdapUser ldapUser = ldapClient.search(username);return new LdapUserDetailsAdapter(ldapUser);}
}
完整用户管理系统
需要本地用户管理的系统应实现UserDetailsManager:
@Repository
public class JpaUserManager implements UserDetailsManager {private final UserRepository userRepo;@Overridepublic void createUser(UserDetails user) {UserEntity entity = new UserEntity(user.getUsername(),user.getPassword(),convertAuthorities(user.getAuthorities()));userRepo.save(entity);}// 其他接口方法实现...
}
设计模式应用
推荐采用适配器模式解决多系统用户模型差异问题:
public class ExternalSystemUserAdapter implements UserDetails {private final ExternalUser externalUser;public ExternalSystemUserAdapter(ExternalUser externalUser) {this.externalUser = externalUser;}@Overridepublic String getUsername() {return externalUser.getLoginId();}@Overridepublic Collection getAuthorities() {return externalUser.getPrivileges().stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());}
}
权限管理实现策略
权限存储建议采用三种典型方案:
- 静态配置(适合简单系统)
@Override
public Collection getAuthorities() {return List.of(new SimpleGrantedAuthority("ROLE_USER"),new SimpleGrantedAuthority("FILE_READ"));
}
- 数据库存储(推荐方案)
@Entity
public class UserRole {@Id private Long id;private String username;private String role; // 如 "DEPARTMENT_ADMIN"
}// 在UserDetailsService中动态加载
List auths = roleRepo.findByUsername(username).stream().map(ur -> new SimpleGrantedAuthority(ur.getRole())).collect(Collectors.toList());
- 混合模式(基础权限+动态权限)
@Override
public Collection getAuthorities() {List auths = new ArrayList<>();// 静态基础权限auths.add(new SimpleGrantedAuthority("BASIC_ACCESS"));// 动态业务权限auths.addAll(dynamicPermissionService.getCurrentPermissions());return auths;
}
最佳实践建议
- 接口实现原则
- 保持UserDetailsService实现无状态
- 密码处理应委托给PasswordEncoder
- 用户检索应添加缓存机制
- 性能优化
@Cacheable(value = "users", key = "#username")
@Override
public UserDetails loadUserByUsername(String username) {// 数据库查询操作
}
- 安全规范
- 永远不返回null(应抛出UsernameNotFoundException)
- 敏感操作需添加@Transactional注解
- 实现用户变更审计日志
通过这种清晰的接口分离设计,开发者可以灵活选择适合当前应用阶段的实现方案,并在业务发展过程中平滑升级用户管理系统。
企业级实现模式
JPA实体与SecurityUser的职责分离
在实际企业应用中,推荐采用职责分离模式处理用户实体与安全认证的映射关系。典型实现包含两个独立类:
// 纯JPA实体(仅关注数据持久化)
@Entity
public class SystemUser {@Id @GeneratedValueprivate Long userId;@Column(unique=true)private String loginName;private String passwordHash;private String department;// 其他业务字段...
}// 安全适配器(实现UserDetails)
public class SecurityUser implements UserDetails {private final SystemUser systemUser;public SecurityUser(SystemUser systemUser) {this.systemUser = systemUser;}@Overridepublic String getUsername() {return systemUser.getLoginName();}@Overridepublic Collection getAuthorities() {return loadDynamicAuthorities(systemUser.getUserId());}// 其他方法实现...
}
这种模式的优势在于:
- 领域模型纯净:SystemUser不包含任何安全框架依赖
- 安全隔离:认证逻辑变更不会影响核心业务模型
- 灵活扩展:可针对不同安全需求创建多个适配器
构建器模式的高级应用
Spring Security提供的User构建器支持多种高级配置方式:
// 带密码编码的构建
UserDetails admin = User.withUsername("admin").passwordEncoder(plain -> SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8().encode(plain)).password("s3cr3t").roles("ADMIN", "AUDITOR").accountLocked(false).build();// 基于现有用户克隆构建
UserDetails tempUser = User.withUserDetails(existingUser).passwordResetRequired(true).disabled(false).build();
构建器支持的关键特性:
- 密码编码器可插拔设计
- 角色自动前缀处理(自动添加ROLE_前缀)
- 细粒度的账户状态控制
- 线程安全的不可变实例生成
密码编码器的解耦设计
推荐采用策略模式将密码编码器与用户详情服务解耦:
@Configuration
public class SecurityConfig {@Beanpublic PasswordEncoder passwordEncoder() {return new DelegatingPasswordEncoder("bcrypt", Map.of("bcrypt", new BCryptPasswordEncoder(),"scrypt", new SCryptPasswordEncoder()));}@Beanpublic UserDetailsService userDetailsService(UserRepository repo, PasswordEncoder encoder) {return username -> repo.findByUsername(username).map(user -> new SecurityUser(user, encoder)).orElseThrow(() -> new UsernameNotFoundException(username));}
}
这种设计的优势体现在:
- 算法可配置:支持运行时动态选择加密算法
- 平滑迁移:兼容多种历史密码格式
- 集中管理:密码策略变更只需修改单一配置点
企业级实现建议
对于复杂系统,建议采用以下模式组合:
- 仓库模式处理用户数据访问
public interface UserRepository extends JpaRepository {Optional findByUsername(String username);@Query("SELECT u FROM SystemUser u JOIN FETCH u.roles WHERE u.username = :username")Optional findWithRolesByUsername(@Param("username") String username);
}
- DTO模式处理外部系统集成
public record UserAuthDTO(String loginId,String credential,List privileges,boolean active) {public UserDetails toUserDetails(PasswordEncoder encoder) {return User.builder().username(loginId).password(encoder.encode(credential)).authorities(privileges).disabled(!active).build();}
}
- 缓存层优化性能
@CacheConfig(cacheNames = "users")
public class CachedUserService implements UserDetailsService {@Cacheable(sync = true)public UserDetails loadUserByUsername(String username) {// 数据库查询操作}
}
这种架构既保持了各层的职责单一性,又通过清晰的接口定义实现了组件间的松耦合,适合大型企业应用的长期演进。
总结
Spring Security认证体系的核心在于其组件化的架构设计,通过UserDetails和GrantedAuthority两大基础契约构建了灵活的用户权限模型。这种设计体现了三个关键架构原则:
- 接口隔离原则
通过分离UserDetailsService(基础检索)与UserDetailsManager(扩展管理)的职责,开发者可以按需实现功能。例如仅需认证时实现基础接口:
public class BasicUserService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) {// 实现基础检索逻辑}
}
- 构建器模式
User构建器提供了声明式的用户实例创建方式,支持链式调用和不可变实例生成:
UserDetails user = User.builder().username("dev").password("{bcrypt}$2a$10$...").roles("DEV", "TESTER").build();
- 单一职责原则
推荐采用适配器模式分离业务实体与安全实体,例如JPA用户实体与安全适配器的组合:
@Entity
public class AppUser { /* 业务字段 */ }public class SecurityUser implements UserDetails {private final AppUser appUser;// 实现接口方法委托给appUser
}
实际开发中应注意:
- 权限设计采用
SimpleGrantedAuthority实现最小权限控制 - 密码编码器通过策略模式实现算法可插拔
- 复杂系统建议增加缓存层优化用户查询性能
这种架构既保证了核心认证流程的稳定性,又为不同复杂度的应用提供了可扩展的实现路径。
相关文章:
Spring Security中的认证实现
Spring Security认证架构概述 Spring Security的认证流程建立在精心设计的组件协作体系之上。图3.1展示了该框架实现认证过程的核心架构,这个架构由多个关键组件构成,理解这些组件的交互关系对于任何Spring Security实现都至关重要。 认证流程核心组件…...
MacOS解决局域网“没有到达主机的路由 no route to host“
可能原因:MacOS 15新增了"本地网络"访问权限,在 APP 第一次尝试访问本地网络的时候会请求权限,可能顺手选择了关闭。 解决办法:给想要访问本地网络的 APP (例如 terminal、Navicat、Ftp)添加访问…...
找到每一个单词+模拟的思路和算法
如大家所知,我们可以对给定的字符串 sentence 进行一次遍历,找出其中的每一个单词,并根据题目的要求进行操作。 在寻找单词时,我们可以使用语言自带的 split() 函数,将空格作为分割字符,得到所有的单词。为…...
澄清 STM32 NVIC 中断优先级
我们来澄清一下 STM32 NVIC 中断优先级的行为,特别是在抢占优先级和响应优先级(子优先级)都相同的情况下: 核心规则回顾: 抢占优先级 (Preemption Priority): 决定了中断是否可以打断另一个正在执行的中断。 高抢占优…...
2025东南亚跨境选择:Lazada VS. Shopee深度对比
东南亚电商市场持续爆发,2025年预计规模突破2000亿美元。对跨境卖家而言,Lazada与Shopee仍是两大核心战场,但平台生态与竞争格局已悄然变化。深入对比,方能制胜未来。 一、平台基因与核心优势对比 维度 Lazada (阿里系) Shopee …...
如何做好一份技术文档?(上篇)
如何做好一份技术文档?(上篇) 上篇:技术文档的基石设计 ——构建可持续迭代的文档体系 文档金字塔模型 [概念层] 为什么 —— 设计理念/适用场景 ▲ [指南层] 怎么做 —— 任务教程/最佳实践 ▲ [参考层] 是什么 ——…...
StarRocks
StarRocks 是一款由中国公司 北京快立方科技有限公司(Fenruilab)开发的 高性能分析型数据库,专注于解决大规模数据分析和实时查询场景的需求。它基于 MPP(大规模并行处理)架构设计,具备高并发、低延迟、易扩…...
Java-39 深入浅出 Spring - AOP切面增强 核心概念 通知类型 XML+注解方式 附代码
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
.NET 8集成阿里云短信服务完全指南【短信接口】
文章目录 前言一、准备工作1.1 阿里云账号准备1.2 .NET 8项目创建 二、集成阿里云短信SDK2.1 安装NuGet包2.2 配置阿里云短信参数2.3 创建配置类 三、实现短信发送服务3.1 创建短信服务接口3.2 实现短信服务3.3 注册服务 四、创建控制器五、测试与优化5.1 单元测试5.2 性能优化…...
实现仿中国婚博会微信小程序
主要功能: 1、完成底部标签导航设计、首页海报轮播效果设计和宫格导航设计,如图1所示 2、在首页里,单击全部分类宫格导航的时候,会进入到全部分类导航界面,把婚博会相关内容的导航集成到一个界面里,如图2…...
互联网大厂Java面试:从Spring Cloud到Kafka的技术考察
场景:互联网大厂Java求职者面试 面试官与谢飞机的对话 面试官:我们先从基础开始,谢飞机,你能简单介绍一下Java SE和Java EE的区别吗? 谢飞机:哦,这个简单。Java SE是标准版,适合桌…...
策略梯度核心:Advantage 与 GAE 原理详解
一.Advantage(优势函数)详解 什么是 Advantage? Advantage 表示当前动作比平均水平好多少。 其定义公式为: A ( s , a ) Q ( s , a ) − V ( s ) A(s, a) Q(s, a) - V(s) A(s,a)Q(s,a)−V(s) 其中: Q ( s , a ) …...
Python 使用总结之:Python 文本转语音引擎 - pyttsx3 完全指南
文本转语音(TTS,Text-to-Speech)技术已经广泛应用于语音助手、智能硬件、教育软件等多个领域。Python 提供了多个库来实现 TTS,其中 pyttsx3 是一个非常常用的跨平台 TTS 引擎。本文将详细介绍如何使用 pyttsx3 实现文本转语音的功…...
星闪开发之Server-Client 指令交互控制红灯亮灭案例解析(SLE_LED详解)
系列文章目录 星闪开发之Server-Client 指令交互控制红灯亮灭的全流程解析(SLE_LED详解) 文章目录 系列文章目录前言一、项目地址二、客户端1.SLE_LED_Client\inc\SLE_LED_Client.h2.SLE_LED_Client\src\SLE_LED_Client.c头文件与依赖管理宏定义与全局变…...
day25-计算机网络-3
1. DNS解析流程 windows host文件是否配置域名对应的ip查询本地DNS缓存是否有这个域名对应的ip询问本地DNS(网卡配置的)是否知晓域名对应的ip本地DNS访问根域名解析服务器,但是根DNS只有顶级域名的记录,根告诉我们.cn顶级域名的D…...
【ArcGIS应用】ArcGIS应用如何进行影像分类?
ArcGIS应用如何进行影像分类?...
RunnablePassthrough介绍和透传参数实战
导读:在构建复杂的LangChain应用时,你是否遇到过需要在处理链中既保留原始输入又动态扩展上下文的场景?RunnablePassthrough正是为解决这类数据流处理问题而设计的核心组件。 本文通过深入剖析RunnablePassthrough的工作机制和实际应用&#…...
JavaSec-XSS
反射型XSS 简介 XSS(跨站脚本攻击)利用浏览器对服务器内容的信任,攻击者通过在网页中注入恶意脚本,使这些脚本在用户的浏览器上执行,从而实现攻击。常见的XSS攻击危害包括窃取用户会话信息、篡改网页内容、将用户重定向到恶意网站,…...
AtCoder-abc408_b 解析
题目链接 让我们一步一步详细分析这个问题: 题目要求我们实现去重并排序的功能,这在C中可以直接使用set容器来实现。set是STL中的一个关联容器,具有以下特性: 自动去重:所有元素都是唯一的自动排序:默认…...
echarts在uniapp中使用安卓真机运行时无法显示的问题
文章目录 一、实现效果二、话不多说,上源码 前言:我们在uniapp中开发的时候,开发的时候很正常,echarts的图形在h5页面上也是很正常的,但是当我们打包成安卓app或者使用安卓真机运行的时候,图形根本就没有渲…...
STM32----IAP远程升级
一、概述: IAP,全称是“In-Application Programming”,中文解释为“在程序中编程”。IAP是一种对通过微控制器的对外接口(如USART,IIC,CAN,USB,以太网接口甚至是无线射频通道&#…...
C++优选算法 904. 水果成篮
文章目录 1.题目描述2.算法思路 3.完整代码容器做法数组做法 1.题目描述 看到这种题目,总觉得自己在做阅读理解,晕了,题目要求我们在一个数组里分别找出两种数字,并统计这两种数字分别出现一共是多少。 2.算法思路 采用哈希表滑…...
Python6.5打卡(day37)
DAY 37 早停策略和模型权重的保存 知识点回顾: 过拟合的判断:测试集和训练集同步打印指标模型的保存和加载 仅保存权重保存权重和模型保存全部信息checkpoint,还包含训练状态 早停策略 作业:对信贷数据集训练后保存权重…...
大中型水闸安全监测管理系统建设方案
一、背景介绍 我国现已建成流量5m/s及以上的水闸共计100321座。其中,大型水闸923座,中型水闸6,697座。按功能类型划分,分洪闸8193座,排(退)水闸17808座,挡潮闸4955座,引水闸13796座&…...
Compose Multiplatform 实现自定义的系统托盘,解决托盘乱码问题
Compose Multiplatform是 JetBrains 开发的声明式 UI 框架,可让您为 Android、iOS、桌面和 Web 开发共享 UI。将 Compose Multiplatform 集成到您的 Kotlin Multiplatform 项目中,即可更快地交付您的应用和功能,而无需维护多个 UI 实现。 在…...
风控研发大数据学习路线
在如今信息爆炸时代,风控系统离不开大数据技术的支撑,大数据技术可以帮助风控系统跑的更快,算的更准。因此,风控技术研发需要掌握大数据相关技术。然而大数据技术栈内容庞大丰富,风控研发同学很可能会面临以下这些痛点…...
【设计模式】门面/外观模式
MySQL ,MyTomcat 的启动 现在有 MySQL ,MyTomcat 类,需要依次启动。 public class Application {public static void main(String[] args) {MySQL mySQL new MySQL();mySQL.initDate();mySQL.checkLog();mySQL.unlock();mySQL.listenPort(…...
spring的webclient与vertx的webclient的比较
Spring WebClient 和 Vert.x WebClient 都是基于响应式编程模型的非阻塞 HTTP 客户端,但在设计理念、生态整合和适用场景上存在显著差异。以下是两者的核心比较: 🔄 1. 技术背景与架构 • Spring WebClient ◦ 生态定位:属于 Sp…...
贪心算法应用:埃及分数问题详解
贪心算法与埃及分数问题详解 埃及分数(Egyptian Fractions)问题是数论中的经典问题,要求将一个真分数表示为互不相同的单位分数之和。本文将用2万字全面解析贪心算法在埃及分数问题中的应用,涵盖数学原理、算法设计、Java实现、优…...
高效集成AI能力:使用开放API打造问答系统,不用训练模型,也能做出懂知识的AI
本文为分享体验感受,非广告。 一、蓝耘平台核心功能与优势 丰富的模型资源库 蓝耘平台提供涵盖自然语言处理、计算机视觉、多模态交互等领域的预训练模型,支持用户直接调用或微调,无需从零开始训练,显著缩短开发周期。 高性能…...
