Spring 生态与 I/O 综合

共 61 题
📑 题目列表 61 题
#
★★★

1. @Caching 组合注解在多缓存操作中的事务边界与回滚行为

请说明 @Caching 组合注解在多缓存操作中的事务边界与回滚行为?

  • @Caching 组合多个缓存注解
  • 多缓存操作顺序
  • 与事务的回滚行为

@Caching 用于在一个方法上组合多个缓存注解(@Cacheable/@CachePut/@CacheEvict),例如同时更新多个缓存或删除多个缓存。多缓存操作按缓存注解顺序执行。事务边界:Spring Cache 的缓存操作与事务(@Transactional)的协作——缓存操作默认在事务提交前/后执行,具体取决于缓存操作类型与事务阶段。若事务回滚,缓存操作可能已执行导致不一致(缓存与 DB 不一致)。@Caching 组合多缓存操作时,需注意缓存操作与事务的回滚一致性:缓存失效/更新应与事务提交对齐(如 @TransactionalEventListener 或缓存操作在事务提交后执行),避免"事务回滚但缓存已更新"。

@Caching 组合多缓存操作,事务边界是"缓存与 DB 一致性"。缓存操作应尽量与事务提交对齐,避免回滚后缓存不一致。

#
★★★

2. @Lock 与悲观锁/乐观锁

请说明 @Lock 与悲观锁/乐观锁的关系?

  • @Lock 指定 JPA 锁模式
  • 悲观锁(PESSIMISTIC_WRITE)
  • 乐观锁(@Version)

@Lock 是 Spring Data JPA 在 Repository 方法上指定锁模式的注解,通过 LockModeType 设置:PESSIMISTIC_WRITE(悲观写锁,SELECT FOR UPDATE)、PESSIMISTIC_READ(悲观读锁)、OPTIMISTIC(乐观锁,配合 @Version)、OPTIMISTIC_FORCE_INCREMENT 等。悲观锁在查询时加数据库锁,适合冲突频繁场景;乐观锁通过 @Version 版本字段在更新时校验,冲突抛 OptimisticLockException。@Lock 用于显式指定锁模式,悲观锁用 @Lock(PESSIMISTIC_WRITE),乐观锁用 @Version 字段 + @Lock(OPTIMISTIC)。二者是 JPA 中的锁策略实现。

@Lock 指定锁模式,悲观锁加数据库锁、乐观锁靠 @Version 版本校验。理解锁模式与 @Version 的配合是 JPA 并发控制核心。

#
★★★

3. CSRF Token 的 Cookie 双重提交模式与 SameSite Cookie 的协同防御

请说明 CSRF Token 的 Cookie 双重提交模式与 SameSite Cookie 的协同防御?

  • CSRF Token Cookie 双重提交
  • SameSite Cookie 属性
  • 协同防御

CSRF Token 的 Cookie 双重提交模式:把 CSRF Token 同时放在 Cookie 和请求参数/Header 中,服务器校验二者是否一致,防止跨站请求伪造(攻击者无法读取同源 Cookie 值来构造请求头)。SameSite Cookie 属性(Strict/Lax/None)限制 Cookie 在跨站请求中的发送,Lax 阻止跨站 POST 携带 Cookie,Strict 更严格,从 Cookie 层面阻断 CSRF。协同防御:Cookie 双重提交(Token 校验)+ SameSite(阻止跨站 Cookie 发送)叠加,形成纵深防御。两者互补,Token 校验防伪造请求,SameSite 减少跨站请求携带凭据。

Cookie 双重提交校验 Token 一致性,SameSite 限制跨站 Cookie 发送,二者叠加形成 CSRF 纵深防御。

#
★★★

4. CSRF 防护原理(同步器令牌模式)与前后端分离/纯 API 场景下的取舍(禁用 CSRF)

请说明 CSRF 防护原理(同步器令牌模式)与前后端分离/纯 API 场景下的取舍(禁用 CSRF)?

  • 同步器令牌模式(Synchronizer Token)
  • CSRF 防护原理
  • 纯 API 场景禁用 CSRF

CSRF 防护的同步器令牌模式:服务器生成随机 CSRF Token 存入 Session,并下发到客户端;客户端每次跨站请求携带 Token,服务器比对 Session 中的 Token,不一致则拒绝。因为攻击者无法读取受害者 Session 中的 Token,无法伪造带 Token 的请求,从而防 CSRF。纯 API 场景(前后端分离、无状态、JWT 认证、无 Cookie Session)的取舍:因无 Cookie 会话、认证靠 Authorization 头(Token),且 CSRF 主要针对"利用浏览器 Cookie 自动携带凭据"的攻击,无状态 API 不依赖 Cookie 会话,CSRF 风险低,常禁用 CSRF(csrf.disable())。但若 API 仍用 Cookie 会话,需保留 CSRF 防护。

同步器令牌通过"不可读 Token"防 CSRF。纯 API 无 Cookie 会话时 CSRF 风险低,可禁用;用 Cookie 会话则需保留。取舍关键在"是否依赖 Cookie 会话"。

#
★★★

5. Caffeine 缓存的 maximumSize/expireAfterWrite/refreshAfterWrite 调优

请说明 Caffeine 缓存的 maximumSize/expireAfterWrite/refreshAfterWrite 调优?

  • maximumSize 容量控制
  • expireAfterWrite 过期
  • refreshAfterWrite 刷新

Caffeine 缓存调优参数:maximumSize 设置最大容量(条目数),超限按 W-TinyLFU 淘汰策略淘汰,控制内存占用;expireAfterWrite 设置写入后过期时间(数据在指定时间后失效),适合数据变化频率已知的场景;refreshAfterWrite 设置写入后刷新时间(到期后异步重新加载,不阻塞读),但刷新需 CacheLoader 支持,且若刷新失败仍返回旧值。调优组合:expireAfterWrite 保证数据不过期过久,refreshAfterWrite 平滑刷新避免集中过期,maximumSize 控制内存。三者结合平衡"新鲜度、内存、可用性"。

maximumSize 控内存、expireAfterWrite 保证新鲜度、refreshAfterWrite 平滑刷新避免雪崩。三参数组合是 Caffeine 调优核心。

#
★★★

6. RememberMe 的实现(TokenBasedRememberMeServices)

请说明 RememberMe 的实现,特别是 TokenBasedRememberMeServices?

  • RememberMe 记住登录
  • TokenBasedRememberMeServices 令牌机制
  • 令牌生成与校验

RememberMe 用于"记住我"登录,Spring Security 通过 RememberMeServices 实现。TokenBasedRememberMeServices 是基于令牌的 RememberMe:登录成功后生成一个包含用户名、过期时间、签名的令牌(Base64),存到 Cookie;下次会话时凭 Cookie 令牌自动认证,服务器校验令牌(用户名、过期、签名)后恢复登录态。令牌基于用户名+过期时间+密钥签名,防篡改。另一种是 PersistentTokenBasedRememberMeServices(持久化令牌,数据库存令牌,可撤销)。TokenBased 简单无状态但无法撤销,适合低风险场景。

TokenBasedRememberMeServices 用签名令牌实现"记住登录",校验用户名校验签名。无状态但不可撤销,持久化令牌可撤销但需存储。

#
★★★

7. Token 存储(内存、Redis、数据库)的安全边界

请说明 Token 存储(内存、Redis、数据库)的安全边界?

  • 内存、Redis、数据库存储 Token
  • 各存储的安全边界
  • 存储选择

Token 存储方式的安全边界:内存存储(应用内,速度快但重启丢失、多实例需共享,仅适合单机临时);Redis 存储(分布式共享、可设置过期、支持分布式撤销,适合多实例,但 Redis 泄露或攻击是风险,需保护 Redis 与 Token 加密);数据库存储(持久化、可审计、可撤销,但访问慢、需考虑安全与加密)。安全边界:Token 本身应加密存储、设置过期、权限最小化;存储介质需防护(Redis 鉴权、数据库访问控制);配合 Token 加密、哈希、审计。选择依据:多实例共享用 Redis、需持久化审计用数据库、单机临时用内存。核心是"Token 加密 + 过期 + 存储介质防护"。

三种存储各有安全边界:内存快但单机、Redis 共享但需防护、数据库持久但慢。核心是 Token 加密、过期、介质防护与权限控制。

#
★★★

8. publishOn 与 subscribeOn 对线程切换的影响范围有何不同,多个 publishOn 串联时信号在哪些调度器间流转

请说明 publishOn 与 subscribeOn 对线程切换的影响范围差异,以及多个 publishOn 串联时信号在哪些调度器间流转?

  • subscribeOn 影响订阅/上游
  • publishOn 影响下游
  • 多个 publishOn 串联

subscribeOn 影响订阅过程(从下游到上游的订阅执行线程),通常作用于 source 的订阅;publishOn 影响其下游 operator 的执行线程。多个 publishOn 串联时,每个 publishOn 把它之后(下游)的 operator 切换到指定调度器,形成"分区":第一个 publishOn 之前的 operator 在订阅线程执行,第一个 publishOn 到第二个 publishOn 之间的 operator 在第一个调度器执行,第二个 publishOn 之后的 operator 在第二个调度器执行。信号在各调度器间流转,每个 publishOn 切换一次下游执行。subscribeOn 只影响订阅,不影响下游数据流执行。

多个 publishOn 把链切成多个"执行段",每段在对应调度器执行。subscribeOn 管订阅,publishOn 管下游数据流,理解可精确控制线程切换。

#
★★★

9. 两级缓存(Local + Remote)的实现策略

请说明两级缓存(Local + Remote)的实现策略?

  • 本地缓存(Caffeine)+ 远程缓存(Redis)
  • 两级缓存读写策略
  • 一致性处理

两级缓存(Local + Remote)实现策略:读时先查本地缓存(Caffeine,快),未命中查远程缓存(Redis,分布式共享),再未命中查 DB 并回填两级;写时更新 DB 并失效/更新两级缓存。策略要点:本地缓存提供极致读性能,远程缓存提供分布式共享与一致性;一致性是难点——本地缓存失效需广播(如 Redis pub/sub 或消息)通知其他节点同步失效,避免节点间不一致。可配置 TTL 分层、本地缓存只做热点数据(量小)、远程缓存做全量。两级缓存权衡"读性能 vs 一致性开销"。

两级缓存"本地快 + 远程共享",核心是本地一致性(失效广播)。策略上本地只存热点、远程存全量,用广播同步失效。

#
★★★

10. 伪异步 I/O(线程池)模型的问题

请说明伪异步 I/O(线程池)模型的问题?

  • 伪异步 I/O(线程池处理连接)
  • 线程池耗尽问题
  • 与真异步的对比

伪异步 I/O(线程池模型)用线程池处理连接,解决了"每连接一线程"的线程无界问题,但仍有本质缺陷:线程池大小有上限,当大量连接同时阻塞(如慢连接、长响应),线程被占满,新连接无法处理(线程池耗尽),表现为"连接能建立但无响应";且线程池中的线程在阻塞 IO 时仍占用线程资源,无法支撑超高并发。相比真异步(NIO/多路复用),伪异步只是"有限线程池",仍受阻塞与线程数限制,无法根本解决 C10K 高并发。问题本质是"线程池 + 阻塞 IO"的局限。

伪异步的缺陷是"有限线程池 + 阻塞 IO"——高并发时线程被阻塞占满,新连接得不到处理。真异步用多路复用解耦连接与线程。

#
★★

11. SelectionKey 被取消后何时才从各集合移除,处理已取消键时为何可能看到 CancelledKeyException

请说明 SelectionKey 被取消后何时才从各集合移除,以及处理已取消键时为何可能看到 CancelledKeyException?

  • SelectionKey 取消与集合移除
  • 已取消键的延迟移除
  • CancelledKeyException

SelectionKey 被取消(cancel())后,并不会立即从各集合(selectedKeys、keySet、cancelledKeys)移除,而是先放入 cancelledKeys 集合,在下次 select() 时才被清理。因此处理已取消键时,若键已被取消但仍在 selectedKeys 中,操作其 channel 可能抛 CancelledKeyException。处理已取消键应:在迭代 selectedKeys 时检查键是否有效(isValid),无效则跳过或移除;避免对已取消键做 channel 操作。CancelledKeyException 是"操作已取消键"的典型异常,需在循环中防御性检查。

SelectionKey 取消是"延迟移除"(select 时才清理),已取消键仍可能短暂出现在集合中,操作时抛 CancelledKeyException。需用 isValid 检查。

#
★★

12. 缓存穿透/击穿/雪崩的治理

请说明缓存穿透/击穿/雪崩的治理?

  • 穿透、击穿、雪崩定义
  • 各自治理方案
  • 综合策略

缓存穿透(查不存在的数据,每次穿透 DB):用空值缓存、布隆过滤器拦截;缓存击穿(热点 key 失效瞬间并发穿透):用 sync=true 互斥、分布式锁、缓存预热;缓存雪崩(大量 key 同时失效/缓存服务宕机):TTL 随机化、多级缓存、缓存降级、限流、集群高可用。治理综合策略:空值缓存 + 布隆过滤防穿透、互斥锁防击穿、TTL 随机 + 多级 + 降级防雪崩,并配合监控与限流。三者成因不同,需针对性治理。

穿透(查空)、击穿(热点失效)、雪崩(批量失效)成因不同,治理方案各异:空值/布隆、互斥锁、TTL 随机 + 多级降级。

#
★★

13. @AuthenticationPrincipal 的应用

请说明 @AuthenticationPrincipal 的应用?

  • @AuthenticationPrincipal 注入当前用户
  • 解析认证主体
  • 用法

@AuthenticationPrincipal 用于在 Spring MVC 控制器方法参数中注入当前已认证的用户(Authentication 的 principal),避免手动从 SecurityContext 获取。例如 @AuthenticationPrincipal UserDetails user 直接注入当前用户。可通过 expression 属性指定 SpEL 表达式提取 principal 的特定属性(如 @AuthenticationPrincipal(expression="username"))。@AuthenticationPrincipal 简化了"获取当前登录用户"的代码,是 Spring Security 提供的便捷注入。需配合 Spring Security 的认证上下文。

@AuthenticationPrincipal 把当前认证主体注入到方法参数,简化获取当前用户。expression 可提取主体属性,是常用便捷注解。

#
★★

14. @Caching 与复合注解

请说明 @Caching 与复合注解?

  • @Caching 组合多个缓存注解
  • 复合注解(组合注解)
  • 使用场景

@Caching 用于在单个方法上组合多个缓存注解(@Cacheable/@CachePut/@CacheEvict),当需要对多个缓存执行不同操作时使用(如同时更新多个缓存名、既有更新又有失效)。复合注解是"组合注解"(用一个注解组合多个注解),可自定义复合缓存注解(如用一个 @CacheUnion 组合 @Cacheable + @CacheEvict),提升可复用性。@Caching 是 Spring 内置的组合缓存注解,复合注解是自定义组合。使用场景:一次方法调用操作多个缓存、缓存操作需组合时。注意 @Caching 内注解的语义与顺序。

@Caching 内置组合多缓存操作,复合注解是自定义组合。用于多缓存操作复用,提升可维护性。

#
★★

15. @EntityGraph 与 N+1 查询问题

请说明 @EntityGraph 与 N+1 查询问题的解决?

  • @EntityGraph 定义实体图
  • N+1 问题
  • 实体图加载关联

@EntityGraph 用于声明实体图(Entity Graph),在 Repository 查询时指定要立即加载(eager)的关联,通过 JOIN FETCH 或批量加载一次取出关联数据,避免懒加载导致的 N+1 查询。@EntityGraph(attributePaths = {"orders"}) 指定加载的关联路径。相比 fetch join,@EntityGraph 声明式、可复用、不修改查询语句。解决 N+1:用 @EntityGraph 一次性加载关联,减少逐条查询。注意 EntityGraph 与查询的兼容性(fetch 与分页、复杂查询的限制)。

@EntityGraph 声明关联的 eager 加载,避免懒加载 N+1。声明式、可复用,是 JPA 防 N+1 的常用手段。

#
★★

16. @Modifying 与批量更新

请说明 @Modifying 与批量更新?

  • @Modifying 批量更新/删除
  • 与 @Query 配合
  • clearAutomatically/flushAutomatically

@Modifying 配合 @Query 用于批量更新/删除(执行 UPDATE/DELETE 的 DML,不通过 JPA 实体管理)。批量更新绕过持久化上下文,直接操作数据库,性能高。注意:批量操作后需配置 clearAutomatically=true 清空持久化上下文避免脏数据、flushAutomatically=true 执行前 flush 确保一致性。@Modifying 方法不能返回实体,通常返回 int(影响行数)。批量更新适合大批量数据更新,避免逐条 save。

@Modifying + @Query 实现批量 DML,绕过持久化上下文,需 clearAutomatically/flushAutomatically 保证一致,是批量更新的高效方式。

#
★★

17. @PreAuthorize/@PostAuthorize 与方法级安全

请说明 @PreAuthorize/@PostAuthorize 与方法级安全?

  • @PreAuthorize 执行前授权
  • @PostAuthorize 执行后授权
  • 方法级安全

@PreAuthorize 在方法执行前检查授权(SpEL 表达式),不满足抛 AccessDeniedException,用于"进入前鉴权";@PostAuthorize 在方法执行后检查授权(可访问 returnObject 基于返回值判断),用于"返回后校验"(如按数据归属校验)。二者构成方法级安全(Method Security),通过 @EnableMethodSecurity 开启,由 AOP 拦截实现。@PreAuthorize 是最常用方法级鉴权,@PostAuthorize 适合基于返回值/数据的细粒度授权。

方法级安全通过注解在方法上声明授权规则,@PreAuthorize 进入前、@PostAuthorize 返回后,基于返回值做细粒度授权。

#
★★

18. @Query 与 JPQL 的使用

请说明 @Query 与 JPQL 的使用?

  • @Query 声明查询
  • JPQL 面向实体查询
  • 参数与原生 SQL

@Query 用于在 Repository 方法上声明查询,可写 JPQL(面向实体对象,操作实体及其属性)或 nativeQuery(原生 SQL)。JPQL 通过实体名与属性查询,跨数据库(方言转换),如 @Query("SELECT u FROM User u WHERE u.status = :status")。参数用 :param 或 ?1 绑定,支持分页(Pageable)。nativeQuery=true 写原生 SQL,性能可控但依赖数据库。@Query 适合复杂查询与方法名无法派生的场景,优先用 JPQL 保跨库,特殊用原生 SQL。

@Query 声明查询,JPQL 面向实体(跨库、可移植),nativeQuery 用原生 SQL(依赖库)。按需选择,复杂查询用 @Query。

#
★★

19. @QueryHints 与 Hibernate Hint 传递

请说明 @QueryHints 与 Hibernate Hint 传递?

  • @QueryHints 传递查询提示
  • Hibernate Hint(fetch size、注释等)
  • 与查询优化

@QueryHints 用于在 Repository 查询方法上传递 JPA 查询提示(QueryHint)给 JPA 提供者(如 Hibernate)。例如 @QueryHints(@QueryHint(name = "org.hibernate.fetchSize", value = "100")) 设置抓取大小,或 org.hibernate.comment 添加 SQL 注释。QueryHint 用于查询优化与行为控制(fetch size、缓存、超时等)。Hibernate 的 hint 通过名称传递,供 Hibernate 在生成 SQL 时使用。@QueryHints 与 @Query 结合,为查询补充 Hibernate 级优化提示。

@QueryHints 传递 QueryHint 给 Hibernate,控制 fetch size、超时等查询优化。是 JPA 查询到 Hibernate 的优化通道。

#
★★

20. AccessDecisionManager 与投票者

请说明 AccessDecisionManager 与投票者(Voter)?

  • AccessDecisionManager 授权决策
  • 投票者(AccessDecisionVoter)
  • 决策策略

AccessDecisionManager 是 Spring Security 的授权决策管理器,负责根据安全元数据与投票者(AccessDecisionVoter)做出是否放行的决策。各投票者(如 RoleVoter、AuthenticatedVoter)对请求投票(赞成/反对/弃权),AccessDecisionManager 汇总投票结果。常见策略:AffirmativeBased(任一赞成即通过)、ConsensusBased(多数通过)、UnanimousBased(全员通过才放行)。Spring Security 6+ 用 AuthorizationManager 取代 AccessDecisionManager,但概念相通。投票者机制是"多维度投票决策"的授权模型。

AccessDecisionManager 汇总投票者的投票形成决策,策略(Affirmative/Consensus/Unanimous)决定放行条件。是经典授权决策模型。

#
★★

21. AccessDeniedHandler 在 RBAC/ABAC 权限模型中的自定义响应(避免泄露权限策略细节)

请说明 AccessDeniedHandler 在 RBAC/ABAC 权限模型中的自定义响应(避免泄露权限策略细节)?

  • AccessDeniedHandler 处理无权限
  • RBAC/ABAC 权限模型
  • 自定义响应避免泄露策略

AccessDeniedHandler 用于处理"已认证但无权限"的请求(403)。在 RBAC/ABAC 权限模型中,自定义 AccessDeniedHandler 可返回统一、安全的错误响应(如"无权访问"),避免返回具体权限细节(如"缺少 ROLE_X")防止泄露权限策略与内部结构。实现:自定义 AccessDeniedHandler 中写 JSON 错误响应(含统一错误码与提示),不暴露具体权限规则。ABAC 模型基于属性动态评估,响应更应通用化。自定义响应兼顾"用户友好"与"安全不泄露"。

自定义 AccessDeniedHandler 返回统一错误响应,避免泄露 RBAC/ABAC 具体策略细节,是安全响应设计。

#
★★

22. Aggregator/Splitter 的边界

请说明 Aggregator/Splitter 的边界?

  • Aggregator 聚合消息
  • Splitter 拆分消息
  • 边界与使用

Aggregator 与 Splitter 是 Spring Integration 的集成模式:Splitter 把一个消息拆分为多个消息(如集合拆分成多个元素分发),Aggregator 把多个相关消息聚合为一个消息(如收集多个子结果合并)。边界:Splitter 是"一对多"拆分,Aggregator 是"多对一"聚合,二者常配合使用(先拆分处理再聚合)。相关概念:Correlation Strategy(关联策略,聚合时按 key 关联)、Release Strategy(释放策略,决定何时聚合完成)。Aggregator 需处理聚合超时与不完整消息。二者是消息处理中"拆分-处理-聚合"的对称模式。

Splitter 一对多拆分、Aggregator 多对一聚合,配合关联与释放策略。是"并行处理后再合并"的集成模式。

#
★★

23. AuthenticationManager 与 ProviderManager 的委派

请说明 AuthenticationManager 与 ProviderManager 的委派?

  • AuthenticationManager 认证入口
  • ProviderManager 委派给 AuthenticationProvider
  • 认证流程

AuthenticationManager 是 Spring Security 的认证入口,定义 authenticate() 方法。ProviderManager 是 AuthenticationManager 的默认实现,它把认证请求委派给一组 AuthenticationProvider,逐个尝试认证,直到某个 Provider 成功认证或全部失败。ProviderManager 支持多个 Provider 按顺序尝试(如用户名密码、Token、OAuth 等),每个 Provider 负责特定认证方式。认证流程:authenticate 请求 → ProviderManager 遍历 Provider → 匹配的 Provider 验证凭据 → 返回已认证 Authentication 或抛异常。委派模式解耦"认证入口"与"具体认证方式"。

ProviderManager 委派给多个 AuthenticationProvider,每个 Provider 一种认证方式,实现认证的扩展与多方式支持。

#
★★

24. Bridge 与路由(Router)的工程应用

请说明 Bridge 与路由(Router)的工程应用?

  • Bridge 桥接模式
  • Router 路由
  • 工程应用

Bridge(桥接模式)用于把抽象与实现解耦,使二者可独立变化,工程上用于连接不同系统/抽象(如把业务逻辑与多种实现桥接)。Router(路由)用于根据规则把消息/请求分发到不同的处理目标,Spring Integration 的 Router 根据消息头/规则路由到不同通道,Spring MVC 的@RequestMapping 按 URL 路由到控制器。工程应用:Bridge 桥接异构系统(适配器组合),Router 分发请求(按条件路由到不同处理器)。二者结合:Router 决定去哪,Bridge 抽象不同目标的访问。都是"解耦与分发"的结构性模式。

Bridge 解耦抽象与实现,Router 按规则分发。工程上 Router 做分发、Bridge 做实现解耦,配合实现灵活的路由与适配。

#
★★

25. CompletionHandler 的回调语义

请说明 CompletionHandler 的回调语义?

  • CompletionHandler 异步 IO 回调
  • completed/failed 回调
  • 回调语义

CompletionHandler 是 NIO.2(AIO)的异步 IO 完成回调接口,含 completed(成功)与 failed(失败)两个回调方法。异步 IO 操作完成后,由底层线程在完成后调用相应回调:成功后回调 completed(带结果与附件),失败回调 failed(带异常与附件)。回调在 IO 完成时在某个线程执行,与发起线程不同。回调语义:completed 表示操作成功,failed 表示操作失败。用于异步 IO(异步读/写/连接/accept)的结果处理。注意回调线程与并发安全。

CompletionHandler 的 completed/failed 回调在异步 IO 完成时被调用,是 AIO 的结果处理机制。理解回调线程与语义是使用 AIO 的关键。

#
★★

26. FileStore 与磁盘空间查询

请说明 FileStore 与磁盘空间查询?

  • FileStore 表示文件系统存储
  • 磁盘空间查询
  • 用法

FileStore 表示文件系统存储(一个文件系统分区),通过 FileSystem.getFileStores() 获取所有 FileStore。通过 getTotalSpace(总空间)、getUsableSpace(可用空间)、getUnallocatedSpace(未分配空间)查询磁盘空间。用于监控磁盘容量、判断是否空间不足、管理存储。工程应用:磁盘告警、上传前检查空间、存储管理。FileStore 提供文件系统级别的空间信息,是磁盘空间查询的标准 API。

FileStore 封装文件系统存储,getTotalSpace/getUsableSpace 查询磁盘空间,用于存储监控与空间管理。

#
★★

27. FileVisitor 的递归遍历

请说明 FileVisitor 的递归遍历?

  • FileVisitor 接口
  • Files.walkFileTree
  • 递归遍历回调

FileVisitor 用于递归遍历文件树,配合 Files.walkFileTree 使用。FileVisitor 提供回调:preVisitDirectory(进入目录前)、visitFile(访问文件)、visitFileFailed(访问失败)、postVisitDirectory(退出目录后)。通过 walkFileTree 遍历目录树,在回调中处理文件/目录,实现递归遍历、目录操作、删除、复制等。相比 Files.walk 的流式,walkFileTree + FileVisitor 更精细(可控制遍历、处理失败)。工程应用:递归删除、统计、复制、目录树操作。

FileVisitor 提供遍历回调,walkFileTree 驱动递归,回调处理文件/目录,是文件树遍历的精细控制方式。

#
★★

28. Java ZipFileSystem 与 jar/zip 操作

请说明 Java ZipFileSystem 与 jar/zip 操作?

  • ZipFileSystem 提供 zip 作为文件系统
  • 通过 FileSystem API 操作 zip/jar
  • Created with FileSystems.newFileSystem

Java 的 ZipFileSystem 把 zip 文件(或 jar)作为文件系统访问,通过 FileSystems.newFileSystem(Paths.get("x.zip"), ...) 创建 ZipFileSystem,然后用标准 FileSystem API(Path、Files)操作 zip 内的文件(读取、写入、遍历、删除)。这统一了 jar/zip 的文件操作,无需第三方库。工程应用:读取 jar 资源、修改 zip 内容、打包。ZipFileSystem 是 zip/jar 操作的底层标准 API,配合 Files 操作提供便捷的文件系统视图。

ZipFileSystem 把 zip/jar 视为文件系统,用标准 Files API 操作内部文件,统一 jar/zip 处理。

#
★★

29. MessageHandler/MessageProducer 的自定义

请说明 MessageHandler/MessageProducer 的自定义?

  • MessageHandler 处理消息
  • MessageProducer 生成消息
  • 自定义实现

Spring Integration 中 MessageHandler 负责处理消息(消费消息,执行业务逻辑),MessageProducer 负责产生/发送消息(把外部数据接入消息流)。自定义 MessageHandler:实现 MessageHandler 接口(handleMessage 方法),处理收到的消息;自定义 MessageProducer:实现 MessageProducer 或继承 MessageProducerSupport,把外部数据源包装为消息发送到通道。通过自定义可接入私有协议/外部系统。二者是消息流的端点,自定义实现扩展集成能力。

MessageHandler 消费消息、MessageProducer 产生消息,自定义二者可把外部系统接入 Spring Integration 消息流。

#
★★

30. OP_CONNECT 就绪后为什么必须调用 finishConnect,连接失败时应清理哪些键和附件资源

请说明 OP_CONNECT 就绪后为什么必须调用 finishConnect,连接失败时应清理哪些键和附件资源?

  • OP_CONNECT 就绪后 finishConnect
  • 连接失败清理
  • 资源清理

非阻塞连接(SocketChannel.connect 返回 false 后,selector 监听 OP_CONNECT)就绪后,必须调用 finishConnect() 完成连接建立,确认连接是否成功(成功则继续,失败抛异常)。不调用 finishConnect 无法确认连接状态,可能影响后续读写。连接失败时需清理:取消 SelectionKey(cancel)、关闭通道(close)、移除 attachment(附件)、从 keySet 移除,释放连接资源,避免泄漏。清理要点是"取消键 + 关闭通道 + 释放附件",防止资源泄漏与后续错误操作。

finishConnect 完成非阻塞连接确认,连接失败需彻底清理(取消键、关通道、释放附件),防止资源泄漏。

#
★★

31. OpenID Connect(OIDC)的 id_token 与 nonce

请说明 OpenID Connect(OIDC)的 id_token 与 nonce?

  • id_token 身份令牌
  • nonce 防重放
  • OIDC 身份流程

OIDC 的 id_token 是携带用户身份信息的 JWT(含 sub、iss、aud、exp、nonce 等声明),由授权服务器签发,客户端用它建立用户身份认证。nonce 是客户端生成的一次性随机值,随授权请求发送,在 id_token 中回传;客户端校验 id_token 中的 nonce 与请求时发送的 nonce 一致,防止重放攻击(防令牌被拦截重放)。id_token 提供身份(谁),Access Token 提供访问授权(能做什么),nonce 防止 id_token 重放。它们构成 OIDC 身份认证的核心。

id_token 提供身份声明,nonce 防重放(校验随机值)。id_token 管"身份",Access Token 管"授权",nonce 保安全。

#
★★

32. Pageable 与 Page 的分页实现

请说明 Pageable 与 Page 的分页实现?

  • Pageable 分页参数
  • Page 分页结果
  • 分页查询实现

Spring Data 分页通过 Pageable(页码、每页大小、排序)与 Page (分页结果)实现。Repository 方法接收 Pageable 返回 Page,Page 包含当前页数据、总条数、总页数、页码等。实现:Spring Data 在查询时应用分页(limit/offset)并同时执行 count 查询获取总数,组装 Page。Pageable 用 PageRequest.of(page, size, sort) 创建。分页查询适合中小数据量,深分页(大 offset)性能差,需游标分页替代。Page 的 getTotalElements/getTotalPages 用于分页展示。

Pageable 传分页参数,Page 返回分页结果(含总数)。Spring Data 执行 limit/offset + count 组装 Page,深分页需用游标优化。

#
★★

33. Refresh Token 的轮换(Rotation)与重用检测

请说明 Refresh Token 的轮换(Rotation)与重用检测?

  • Refresh Token 轮换
  • 重用检测(Reuse Detection)
  • 安全机制

Refresh Token 轮换(Rotation):每次使用 Refresh Token 换取新 Access Token 时,同时签发新的 Refresh Token,并使旧 Refresh Token 失效,减少令牌长期有效带来的泄露风险。重用检测(Reuse Detection):检测 Refresh Token 是否被重复使用(重用意味着令牌可能被窃取),若检测到旧 Refresh Token 被重用,则撤销该令牌族的所有 Refresh Token,防止攻击者用已窃取令牌持续访问。轮换 + 重用检测是 OAuth 2.0 刷新令牌的安全最佳实践,需服务端存储令牌状态(已使用/撤销)。

Rotation 轮换刷新令牌减少长期暴露,Reuse Detection 检测复用并撤销令牌族,二者结合防令牌窃取与重放。

#
★★

34. SelectionKey 的兴趣集(interest set)与就绪集(ready set)的位运算操作细节

请说明 SelectionKey 的兴趣集(interest set)与就绪集(ready set)的位运算操作细节?

  • interestOps 兴趣集
  • readyOps 就绪集
  • 位运算操作

SelectionKey 的兴趣集(interest set)表示"关注的事件类型",通过 interestOps 设置(OP_ACCEPT/OP_CONNECT/OP_READ/OP_WRITE 的位);就绪集(ready set)表示"已就绪的事件",通过 readyOps 获取。位运算:用 | 组合多个事件(selectionKey.interestOps(OP_READ | OP_WRITE)),用 & 判断是否包含某事件((readyOps & OP_READ) != 0)。修改兴趣集用 interestOps(ops) 更新,查询就绪用 readyOps()。位运算基于各事件是 1<<n 的整型位,用于组合与检测。注意:修改 interestOps 会触发 selector 重新注册感兴趣的事件。

interestOps 用位组合关注事件,readyOps 用位检测就绪事件。位运算(| 组合、& 检测)是操作 SelectionKey 的基础。

#
★★

35. SelectionKey 的四种事件(OP_READ/OP_WRITE/OP_CONNECT/OP_ACCEPT)

请说明 SelectionKey 的四种事件 OP_READ/OP_WRITE/OP_CONNECT/OP_ACCEPT?

  • 四种事件语义
  • 各自适用场景
  • 触发条件

SelectionKey 的四种事件:OP_ACCEPT(服务端通道可接收新连接,ServerSocketChannel 监听)、OP_CONNECT(客户端通道连接完成,SocketChannel 非阻塞连接)、OP_READ(通道可读,有数据到达)、OP_WRITE(通道可写,缓冲可写入)。各自触发:OP_ACCEPT 在 listen 队列有连接时触发;OP_CONNECT 在 connect 完成时触发;OP_READ 在接收缓冲区有数据时触发;OP_WRITE 在发送缓冲区可写时触发(但 OP_WRITE 通常一直就绪,需按需注册)。四种事件对应各种通道操作,是 NIO 多路复用的核心。

四种事件对应"accept/connect/read/write"四类操作,各管一类通道事件。理解触发条件才能正确注册与处理。

#
★★

36. SelectionKey.attach 与 attachment

请说明 SelectionKey.attach 与 attachment?

  • attach 绑定附件对象
  • attachment 获取附件
  • 附件用途

SelectionKey.attach(attachment) 用于把附带对象(如会话状态、缓冲区、业务对象)绑定到 SelectionKey,attachment() 获取该对象。用途:在事件循环中,把每个连接的状态(如 Buffer、处理状态、用户上下文)附加到 SelectionKey,事件就绪时通过 attachment() 快速获取,避免外部 Map 查找。附件是"连接关联数据"的便捷存储,随 SelectionKey 生命周期管理。处理完需注意附件释放(连接关闭时清理),避免泄漏。

attach 把连接状态绑定到 SelectionKey,attachment 获取,事件就绪时快速关联数据。连接关闭需清理附件。

#
★★

37. SelectionKey.interestOps 与监听事件变更

请说明 SelectionKey.interestOps 与监听事件变更?

  • interestOps 变更关注事件
  • 动态监听事件
  • 注意事项

SelectionKey.interestOps() 用于读取/修改通道关注的事件集(对 OP_READ/OP_WRITE 等)。通过 interestOps(ops) 动态变更监听事件(如读完后改为监听写、写完后改回读)。注意:修改 interestOps 会使 selector 重新评估该键,需在 selector 线程中操作或醒来后处理;OP_WRITE 通常应只在需要写时注册,否则一直就绪造成忙循环。动态变更监听事件是 NIO 事件驱动的核心(按需求切换监听读/写)。变更后由 selector 重新监听新事件。

interestOps 动态切换监听事件(读/写切换),是事件驱动状态机。注意 OP_WRITE 按需注册与线程安全。

#
★★

38. SessionManagementFilter 的会话固定防护

请说明 SessionManagementFilter 的会话固定防护?

  • 会话固定攻击
  • SessionManagementFilter 防会话固定
  • 登录后更换会话

会话固定攻击(Session Fixation):攻击者先获取一个会话 ID,诱导受害者用该会话登录,攻击者用同一会话 ID 劫持认证后的会话。SessionManagementFilter 防会话固定:检测认证过程中的会话变化,策略包括 changeSessionId(登录后更换 Session ID)、newSession(新建会话)、migrateSession(迁移会话属性)。Spring Security 默认在登录后更换 Session ID(changeSessionId),使攻击者固定的会话 ID 失效,防会话固定。SessionManagementFilter 还负责会话超时与会话并发控制。

会话固定攻击利用"固定会话 ID + 用户登录"。登录后更换 Session ID 使攻击者会话失效,是防会话固定的核心手段。

#
★★

39. Specification 与动态查询构造

请说明 Specification 与动态查询构造?

  • Specification 动态查询
  • JpaSpecificationExecutor
  • 条件组合

Specification 用于构造动态查询(运行时根据条件组合查询),配合 JpaSpecificationExecutor 使用。实现基于 JPA Criteria API:通过 Specification.toPredicate 构建谓词(条件),用 Specification.where(...).and(...).or(...) 组合多个条件,实现动态的查询条件(如按可选参数过滤)。Repository 继承 JpaSpecificationExecutor 后,方法接收 Specification 执行动态查询。适用于多条件、动态筛选场景(如搜索/过滤)。Specification 是类型安全的动态查询构造方式。

Specification 基于 Criteria API 动态组合查询条件,配合 JpaSpecificationExecutor 实现多条件动态筛选。

#
★★

40. Spring Authorization Server 的多租户支持

请说明 Spring Authorization Server 的多租户支持?

  • Spring Authorization Server 多租户
  • 租户隔离
  • 配置

Spring Authorization Server 支持多租户(SaaS 场景),通过为不同租户配置不同的 RegisteredClient、授权服务器配置与用户信息源实现租户隔离。多租户支持方式:按租户区分客户端注册(每个租户的 client 配置)、租户级数据源/用户存储、按租户路由授权流程。配置时可通过租户上下文(如域名、子域)选择对应配置。多租户隔离的关键是"客户端、用户、授权数据按租户隔离",避免跨租户访问。Spring Authorization Server 提供基于 RegisteredClient 的隔离基础,租户级扩展需自定义。

多租户是通过"客户端注册 + 用户存储 + 授权配置"按租户隔离实现。核心是租户上下文路由与数据隔离。

#

41. StandardOpenOption 的语义与组合

请说明 StandardOpenOption 的语义与组合?

  • StandardOpenOption 打开选项
  • 各选项语义(READ/WRITE/CREATE/TRUNCATE_EXISTING 等)
  • 组合使用

StandardOpenOption 定义文件打开选项:READ(读)、WRITE(写)、CREATE(不存在则创建)、CREATE_NEW(必须新建,已存在则失败)、TRUNCATE_EXISTING(截断清空)、APPEND(追加)、DELETE_ON_CLOSE(关闭时删除)等。Files.newByteChannel/Files.newBufferedWriter 等通过 options 组合控制打开行为。组合注意:写文件需 WRITE + CREATE(+ TRUNCATE_EXISTING 清空或 APPEND 追加);CREATE_NEW 与 CREATE 互斥语义;DELETE_ON_CLOSE 用于临时文件。选项组合决定文件的打开方式与覆盖/追加行为。

StandardOpenOption 控制文件打开方式,组合(如 WRITE+CREATE+TRUNCATE)决定创建、覆盖、追加语义。

#

42. Token Introspection 与 RFC 7662

请说明 Token Introspection 与 RFC 7662?

  • Token Introspection 令牌校验
  • RFC 7662 规范
  • 资源服务器校验

Token Introspection(令牌自省)是 OAuth 2.0 规范(RFC 7662),定义资源服务器通过向授权服务器发送令牌自省请求(POST + token),由授权服务器校验令牌的有效性、作用域、客户端、过期时间等,返回响应(active、scope、exp 等)。用于资源服务器无法验证令牌签名时(如不透明令牌)依赖授权服务器校验。RFC 7662 定义自省端点(/introspect)与请求响应格式。相比 JWT 本地验签,自省是"远程校验",适合不透明令牌或需要撤销检查的场景。

RFC 7662 定义令牌自省端点,资源服务器远程校验不透明令牌的有效性。JWT 本地验签 vs 自省远程校验按令牌类型选择。

#

43. Token 撤销(Revocation)与登出端点

请说明 Token 撤销(Revocation)与登出端点?

  • RFC 7009 令牌撤销
  • 登出端点
  • 撤销与登出

Token 撤销(RFC 7009)定义授权服务器提供撤销端点,客户端可通过 POST 撤销 Access Token 或 Refresh Token,使令牌失效(用于登出、安全泄露场景)。登出端点:OIDC 的登出端点(Logout Endpoint)用于结束用户会话,可同时撤销相关令牌。撤销与登出的实现:撤销端点使令牌失效(服务端记录撤销状态),登出端点结束会话并触发撤销。配合 Refresh Token 撤销(使令牌族失效)实现完整登出。分布式需同步撤销状态(Redis/数据库)。

RFC 7009 撤销端点使令牌失效,OIDC 登出端点结束会话,结合令牌撤销实现完整登出与安全失效。

#

44. UserDetailsService 与 UserDetailsManager 的契约

请说明 UserDetailsService 与 UserDetailsManager 的契约?

  • UserDetailsService 加载用户
  • UserDetailsManager 管理用户
  • 契约差异

UserDetailsService 与 UserDetailsManager 是 Spring Security 的用户抽象:UserDetailsService 定义 loadUserByUsername(按用户名加载用户),是认证的用户来源契约,只读;UserDetailsManager 继承 UserDetailsService,增加用户管理方法(createUser、updateUser、deleteUser、changePassword),是"可管理用户"的契约。区别:UserDetailsService 只负责"读取用户用于认证",UserDetailsManager 提供"增删改用户"的管理能力。实现如 InMemoryUserDetailsManager、JdbcUserDetailsManager。自定义用户存储时,按需实现 UserDetailsService(认证)或 UserDetailsManager(管理)。

UserDetailsService 管"读取认证用户",UserDetailsManager 增加"用户管理"。契约区别是只读 vs 可管理。

#

45. WatchService 与目录监控

请说明 WatchService 与目录监控?

  • WatchService 监听目录
  • 事件(CREATE/MODIFY/DELETE)
  • 目录监控实现

WatchService 用于监听文件系统目录变化,通过 register 注册目录与监听事件(ENTRY_CREATE、ENTRY_MODIFY、ENTRY_DELETE),用 take/poll 获取 WatchKey 事件。工程应用:文件监控(文件上传目录、配置热更新)、日志采集、目录同步。实现:注册目录 → 循环 take 获取事件 → 处理事件(创建/修改/删除)→ 重置 key(reset)继续监听。注意:WatchService 只监听目录,不递归子目录(需递归注册);事件可能丢失(需结合轮询)。是文件系统监控的标准 API。

WatchService 监听目录创建/修改/删除事件,需递归注册子目录,事件处理需 reset 并考虑丢失。是文件监控标准解法。

#

46. WebClient 相比 RestTemplate 的非阻塞与流式能力体现在哪里,RestClient 又填补了怎样的同步场景空缺

请说明 WebClient 相比 RestTemplate 的非阻塞与流式能力,以及 RestClient 填补的同步场景空缺?

  • WebClient 非阻塞/流式
  • RestTemplate 阻塞
  • RestClient 同步便捷

WebClient 相比 RestTemplate:WebClient 是响应式非阻塞客户端(基于 Reactor,返回 Mono/Flux),支持流式(流式响应、SSE)、背压、异步组合,适合高并发非阻塞场景;RestTemplate 是传统阻塞客户端(同步,每请求阻塞线程),适合简单同步调用。RestClient 是 Spring 6.1+ 推出的同步客户端,填补 RestTemplate 的"同步但现代便捷"空缺:提供流畅的链式 API(类似 WebClient 的同步版),返回同步对象,兼顾简单同步与良好 API。边界:非阻塞/流式用 WebClient,简单同步用 RestClient(替代 RestTemplate)。

WebClient 非阻塞流式,RestTemplate 阻塞,RestClient 用现代 API 填补"同步便捷"空缺。三者按阻塞/非阻塞与 API 需求选择。

#

47. scope 与 authority 的映射

请说明 OAuth2 的 scope 与 authority 的映射?

  • scope 与 authority
  • 映射关系
  • 授权判断

OAuth 2.0 的 scope(作用域)表示客户端请求的权限范围(如 read、write),authority(权限)表示 Spring Security 中的授权(如 ROLE_ADMIN、权限)。映射:scope 可映射为 authority(Spring Security 把 scope 转换为 SCOPE_xxx 的 authority),用于授权判断。在 Spring Security 中,@PreAuthorize 可用 hasAuthority('SCOPE_read') 判断 scope,或通过网关把 scope 解析为权限。映射关系需设计:scope 是 OAuth 客户端的授权声明,authority 是资源服务器内部授权模型,二者通过"scope → authority"转换衔接。映射让 OAuth 授权与业务授权统一。

scope 是 OAuth 授权范围,authority 是 Spring Security 授权,映射(scope → SCOPE_xxx authority)衔接 OAuth 与业务授权。

#

48. selectedKeys() 与迭代器的处理(必须显式移除)

请说明 selectedKeys() 与迭代器的处理(必须显式移除)?

  • selectedKeys() 返回就绪键集合
  • 迭代器必须显式 remove
  • 重复处理问题

Selector.selectedKeys() 返回本次 select 就绪的 SelectionKey 集合。迭代处理时,必须显式调用迭代器 remove() 移除已处理的键,否则该键会残留在 selectedKeys 中,下次 select 时可能被重复处理(即使事件未重新就绪),导致重复处理与 CPU 空转。正确模式:遍历 selectedKeys,处理完每个键后调用 iterator.remove() 移除。若忘记移除,就绪键会累积,造成重复处理与性能问题。处理完需考虑键是否取消(isValid)与兴趣集更新。

selectedKeys 需迭代器显式 remove,否则就绪键残留导致重复处理与 CPU 空转。这是 NIO 事件循环的经典坑。

#

49. 函数式端点(RouterFunction/HandlerFunction)与注解式控制器在路由匹配、可组合性与测试方式上的取舍

请说明函数式端点(RouterFunction/HandlerFunction)与注解式控制器在路由匹配、可组合性与测试方式上的取舍?

  • RouterFunction 函数式路由
  • 注解式控制器
  • 路由匹配、可组合性、测试

函数式端点(RouterFunction/HandlerFunction)与注解式控制器(@Controller)是 Spring MVC/WebFlux 的两种编程模型。路由匹配:注解式用 @RequestMapping 声明式,函数式用 RouterFunction 编程式(route() 链式匹配路径与请求)。可组合性:函数式可组合(RouterFunction 可合并、嵌套),适合动态/可组合路由;注解式通过注解声明,路由静态、可读性高。测试方式:注解式用 MockMvc/WebTestClient,函数式直接测 RouterFunction(更单元化)。取舍:简单固定路由用注解式(直观),动态可组合路由或函数式偏好用 RouterFunction。函数式更灵活、注解式更直观。

注解式声明式直观、函数式编程式可组合。路由匹配、组合性与测试方式有别,按路由复杂度与偏好选择。

#

50. 原生查询(nativeQuery = true)的取舍

请说明原生查询(nativeQuery = true)的取舍?

  • nativeQuery 原生 SQL
  • 与 JPQL 对比
  • 取舍

@Query(nativeQuery = true) 使用原生 SQL 查询,直接操作数据库表与 SQL,性能可控、支持数据库特有功能(如特定函数、复杂 join),但依赖特定数据库(不可移植)、返回 Object[] 或需映射。取舍:性能与数据库特性优先、复杂 SQL 用原生查询;可移植性、类型安全优先用 JPQL。原生查询适合复杂报表、性能敏感、数据库特有功能场景;常规查询用 JPQL 保可移植。注意原生查询的 SQL 注入风险(参数需绑定)与结果映射。

nativeQuery 性能与数据库特性优先但不可移植,JPQL 可移植类型安全。按可移植性与性能需求取舍。

#

51. 同步/异步、阻塞/非阻塞的语义区分

请说明同步/异步、阻塞/非阻塞的语义区分?

  • 同步 vs 异步
  • 阻塞 vs 非阻塞
  • 语义区分

同步/异步与阻塞/非阻塞是两个维度:同步/异步关注"调用方是否等待结果"——同步调用等待结果返回,异步调用立即返回、结果通过回调/事件/未来获取;阻塞/非阻塞关注"调用方是否被挂起"——阻塞调用使线程挂起等待 IO 完成,非阻塞调用立即返回(即使未完成,可通过轮询/事件通知)。组合:同步阻塞(传统 IO)、同步非阻塞(NIO 轮询)、异步阻塞(少见)、异步非阻塞(AIO/Reactor,最理想)。区分这两个维度是把控 IO 模型与并发模型的关键。

同步/异步看"是否等待结果",阻塞/非阻塞看"是否挂起线程"。两个正交维度组合出四种 IO 语义,理解可准确选型。

#

52. 客户端凭证模式(Client Credentials)的应用

请说明 OAuth 2.0 客户端凭证模式(Client Credentials)的应用?

  • Client Credentials 模式
  • 无用户场景
  • 服务间通信

客户端凭证模式(Client Credentials)用于"无用户参与"的场景:客户端直接用 client_id + client_secret 向授权服务器换取 Access Token,无需用户授权。适用于服务间通信(M2M)、后台任务、定时任务等客户端自身代表自己的场景。流程:客户端用凭证请求令牌 → 授权服务器核验客户端并发放令牌 → 客户端用令牌访问资源。令牌授权范围是客户端本身(scope 定义)。适合系统间调用、不涉及用户身份的场景。安全性:client_secret 需妥善保管。

Client Credentials 是"客户端代替自己"的授权,无用户参与,适合服务间通信。用 client 凭证换取令牌。

#

53. 方法名查询(findByXxx)的命名规则

请说明 Spring Data 方法名查询(findByXxx)的命名规则?

  • 方法名解析规则
  • findBy 前缀与属性
  • 关键字

Spring Data 方法名派生查询(findByXxx)基于命名规则解析:findBy 前缀 + 属性路径 + 条件关键字。如 findByUsername(按属性名)、findByUsernameAndStatus(AND 连接多个条件)、findByAgeGreaterThan(比较关键字)、findByUsernameOrderByCreateTimeDesc(排序 OrderBy)。关键字包括 And、Or、Is、Between、GreaterThan、Like、In、Containing、OrderBy 等。属性名需与实体属性对应(支持嵌套属性用下划线)。方法名解析成查询由 Spring Data 在启动时完成,命名错误会启动报错。规则准确才能正确解析。

方法名派生查询按"findBy + 属性 + 关键字"规则解析,关键字(And/Or/Between 等)与属性名决定查询条件。

#

54. 远端半关闭连接时 read 返回负一,但本地仍可能写入,协议层应怎样定义半关闭策略

请说明远端半关闭连接时 read 返回 -1,但本地仍可能写入,协议层应怎样定义半关闭策略?

  • 半关闭(half-close)
  • read 返回 -1
  • 半关闭策略

半关闭(TCP half-close)指一端关闭发送(shutdownOutput)但另一端仍可发送。当远端关闭发送(半关闭),本地 read 返回 -1(EOF),表示"远端不再发送数据",但本地仍可向远端写入(write 未关闭)。协议层应对半关闭定义策略:若业务是"请求-响应"模式,收到 EOF 后本地可完成响应再关闭;若半关闭后不再需要交互,应立即关闭连接。策略需明确:何时关闭写(shutdownOutput)、何时关闭整个连接(close)、如何处理 EOF 后的读/写。避免在 EOF 后仍写入导致协议错误或资源泄漏。半关闭策略由应用协议定义,如 HTTP 的 Connection: close 语义。

半关闭时 read 返回 -1 表示"远端不再发",但本地可写。协议层需定义 EOF 后的响应与关闭策略,避免状态混乱。

#

55. 适配器(Adapter)模式与外部系统集成

请说明适配器(Adapter)模式与外部系统集成?

  • Adapter 适配器模式
  • 外部系统集成
  • 应用

适配器(Adapter)模式用于把目标接口与不兼容的已有接口桥接,使已有类可被统一的接口调用。在外部系统集成中,适配器模式广泛应用:把外部系统(不同 SDK、不同协议、不同数据格式)的接口适配为应用统一的接口,屏蔽外部差异。Spring Integration 的适配器(如 inbound adapter、outbound adapter)就把外部系统(消息队列、文件、HTTP)接入/送出消息流。工程应用:第三方 SDK 封装、旧系统适配、协议转换。适配器让"变化的外部系统"与"稳定的内部接口"解耦。

Adapter 适配不兼容接口,集成外部系统时屏蔽差异、统一接口。Spring Integration 适配器把外部系统接入消息流。

#

56. 遍历 selectedKeys 后忘记调用迭代器 remove,会导致哪些重复处理与 CPU 占用问题

请说明遍历 selectedKeys 后忘记调用迭代器 remove 会导致哪些重复处理与 CPU 占用问题?

  • 忘记 remove 的后果
  • 重复处理
  • CPU 空转

遍历 selectedKeys 后忘记调用迭代器 remove,已处理的键会残留在 selectedKeys 集合中。下次 select 时,即使通道没有新事件就绪,这些残留键也会被重复处理,导致:重复处理事件(如重复读、重复处理连接),造成业务错误;CPU 空转(每次都遍历处理残留键,忙循环),CPU 占用飙升,事件循环效率下降。这是 NIO 事件循环的经典 bug。正确做法:处理完每个键后立即 iterator.remove(),必要时用 isValid 检查后再处理。

忘记 remove 使就绪键残留,导致重复处理 + 每次 select 空转 CPU。移除已处理键是 NIO 事件循环的必须操作。

#

57. 非阻塞 accept 应循环到返回 null,连接洪峰下又如何设置每轮接收上限保证公平性

请说明非阻塞 accept 应循环到返回 null,以及连接洪峰下如何设置每轮接收上限保证公平性?

  • 非阻塞 accept 循环
  • 连接洪峰
  • 每轮接收上限

非阻塞 accept 应循环调用 accept() 直到返回 null(表示没有更多等待连接),一次性处理完当前就绪的所有连接,避免残留。但在连接洪峰下,若一直循环接受可能导致其他事件(读/写)被饿死,因此应设置每轮接收上限:循环接受一定数量(如 100 或根据时间预算)后停止,让出事件循环处理其他事件,保证公平性。上限策略:按数量或时间(如每轮最多接受 N 个或接受 X 毫秒),兼顾吞吐与公平。连接洪峰时避免单连接处理过长。

accept 循环到 null 处理完待连接,但需设上限防饿死其他事件。数量/时间预算平衡吞吐与公平。

#

58. JDK 25 Scoped Values 与 I/O 线程切换

请说明 JDK 25 Scoped Values 与 I/O 线程切换的关系?

  • Scoped Values 线程上下文
  • I/O 线程切换
  • 上下文传播

Scoped Values(JEP 429/464)是 JDK 的线程上下文值机制,用于在结构化作用域内绑定值,并在线程(含虚拟线程)间按作用域传播。与 I/O 线程切换的关系:在异步/虚拟线程场景,I/O 操作可能切换线程(阻塞挂起、调度到其他线程),Scoped Values 能在结构化并发作用域内传递上下文(如请求 ID、认证信息),即使 I/O 切换线程,作用域内的值仍可访问(ScopedValue 定义了线程切换时的传播语义)。相比 ThreadLocal(不跨线程、可变、易泄漏),Scoped Values 不可变、作用域明确、支持结构化线程传播,适合 I/O 线程切换场景的上下文传递。JDK 25 中 Scoped Values 稳定,提升异步/虚拟线程的上下文安全。

Scoped Values 在结构化作用域内传播上下文,支持 I/O 线程切换,比 ThreadLocal 更安全(不可变、作用域明确),适合异步/虚拟线程。

#

59. JDK 25 中 HttpClient(Java 11+)对 HTTP/2 与 WebSocket 的原生支持与连接复用

请说明 JDK 25 中 HttpClient(Java 11+)对 HTTP/2 与 WebSocket 的原生支持与连接复用?

  • HttpClient 原生 HTTP/2
  • WebSocket 支持
  • 连接复用

JDK 11+ 的 java.net.http.HttpClient 原生支持 HTTP/2(可通过 HTTP/2 多路复用、头部压缩、服务端推送),默认协商 HTTP/2。支持 WebSocket(WebSocket 客户端,sendAsync 建立连接、收发消息)。连接复用:HttpClient 维护连接池,对同一 host 复用连接(HTTP/1.1 keep-alive、HTTP/2 多路复用单连接承载多请求),减少连接建立开销。HttpClient 提供同步(send)与异步(sendAsync)API,支持虚拟线程下弹性。JDK 25 中 HttpClient 稳定,是标准 HTTP 客户端,原生支持 HTTP/2 与 WebSocket 及连接复用。

HttpClient 原生 HTTP/2 与 WebSocket,连接池复用连接,支持同步/异步。是标准 HTTP 客户端,与虚拟线程配合。

#

60. JDK 25 虚拟线程下 I/O 模型变化

请说明 JDK 25 虚拟线程下 I/O 模型的变化?

  • 虚拟线程 I/O 模型
  • 阻塞 I/O 的变化
  • 与传统 I/O 对比

JDK 25 虚拟线程下,I/O 模型的变化:虚拟线程使阻塞 I/O 的成本大幅降低——虚拟线程在阻塞 I/O 时自动挂起并让出载体线程,无需为每个 I/O 分配平台线程,因此"阻塞式 I/O + 虚拟线程"也能获得高并发,传统需用 NIO/异步 I/O 才能避免的"阻塞线程池"问题被虚拟线程缓解。变化核心:I/O 模型从"平台线程-per-请求"变为"虚拟线程-per-请求 + 阻塞挂起",编程模型保持同步简单,但并发能力提升。常见 I/O 代码(同步阻塞)在虚拟线程下可直接获得高并发,无需改写为异步 NIO。但注意 synchronized 阻塞(钉住)与 JNI 的边界。

虚拟线程让阻塞 I/O 变便宜,同步阻塞模型即可获得高并发,无需异步 NIO。I/O 模型从线程池变为虚拟线程挂起。

#

61. Pageable/Page 分页查询在大数据量下的 Slice 替代与性能取舍

请说明 Pageable/Page 分页查询在大数据量下的 Slice 替代与性能取舍?

  • Page 与 Slice
  • 大数据量分页
  • 性能取舍

Page 分页会执行 count 查询获取总数,在大数据量下 count 查询开销大;Slice 不查询总数,只返回当前页数据(不含总条数/总页数),性能更高。大数据量下用 Slice 替代 Page:避免 count 查询,适合"只需当前页数据、不需要总数"的场景(如无限滚动、加载更多)。取舍:Page 提供总数适合分页控件,但 count 开销大;Slice 轻量但无总数,适合流式加载。此外深分页(大 offset)可用游标分页优化。数据量大时优先 Slice 或游标分页,避免 count 与深 offset 开销。

Page 带 count 查询,Slice 无总数更轻量。大数据量用 Slice 或游标分页,避免 count 与深 offset 性能开销。