Spring Data 与 Spring Security

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

1. @PreAuthorize/@PostAuthorize 方法级安全(Method Security)的 SpEL 表达式与 AOP 实现

请说明 @PreAuthorize/@PostAuthorize 方法级安全(Method Security)的 SpEL 表达式与 AOP 实现机制?

  • @PreAuthorize 方法执行前校验、@PostAuthorize 方法执行后校验
  • SpEL 表达式(hasRole、hasAuthority、#参数)
  • 基于 AOP 的 MethodSecurityInterceptor

@PreAuthorize 在方法执行前校验权限,@PostAuthorize 在方法执行后校验(可访问返回值,如 returnObject)。二者通过 SpEL 表达式表达授权规则,如 hasRole('ADMIN')、hasAuthority('READ')、#userId == authentication.principal.id、@bean.method() 等。实现基于 AOP:@EnableMethodSecurity 开启方法级安全,MethodSecurityInterceptor 作为 AOP 环绕通知拦截方法,通过 AuthorizationManager 评估 SpEL 表达式,不满足则抛出 AccessDeniedException。@PreAuthorize 用于"进入前授权",@PostAuthorize 用于"返回后授权(基于返回值)"。

方法级安全把"授权规则"以 SpEL 声明在方法上,由 AOP 拦截执行。相比 URL 级安全,方法级精确到方法,@PostAuthorize 可基于返回值决策。

@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {}

@PostAuthorize("returnObject.ownerId == authentication.principal.id")
public Order getOrder(Long id) { return order; }
#
★★★

2. JWT(Access Token)的生成与校验

请说明 JWT(Access Token)的生成与校验机制?

  • JWT 结构(Header、Payload、Signature)
  • 签名(HS256/RS256)与校验
  • 生成与校验流程

JWT(JSON Web Token)由 Header(算法与类型)、Payload(声明,如 sub、exp、claims)、Signature(签名)三段组成。生成时使用密钥(对称 HS256)或私钥(非对称 RS256)对 Header+Payload 签名;校验时用密钥/公钥验证签名完整性,并检查 exp(过期时间)、iss(签发者)、aud(受众)等声明。RS256 用公钥校验适合分布式(签名在授权服务器,校验在资源服务器)。JWT 是自包含的,无需服务端存储状态,但无法主动撤销,需配合短过期 + Refresh Token。

JWT 的核心是"签名防篡改 + 自包含声明"。生成用密钥签名,校验用密钥/公钥验签并检查声明,是无状态认证的基础。

#
★★★

3. MethodSecurityInterceptor 与 AOP 的协作

请说明 MethodSecurityInterceptor 与 AOP 的协作,以及它如何实现方法级安全?

  • MethodSecurityInterceptor 是 AOP 环绕通知
  • 拦截方法调用并评估授权
  • 与 AuthorizationManager/投票器协作

MethodSecurityInterceptor 是 Spring Security 方法级安全的 AOP 拦截器,它作为环绕通知(Around advice)拦截被保护的方法调用。执行流程:先获取方法的 SecurityMetadataSource(授权规则,如 @PreAuthorize 的 SpEL),再调用 AuthorizationManager(或 AccessDecisionManager)评估是否授权,未授权则抛出 AccessDeniedException,授权则放行。通过 @EnableMethodSecurity 开启后,Spring 为标注了安全注解的方法生成 AOP 代理,MethodSecurityInterceptor 作为通知织入。它与 AOP 的协作实现了"方法级授权"。

MethodSecurityInterceptor 把"方法调用"作为授权点,通过 AOP 代理拦截,用 AuthorizationManager 评估规则。这是方法级安全与 AOP 结合的核心。

#
★★★

4. SecurityContextHolder 与 ThreadLocal 的策略

请说明 SecurityContextHolder 与 ThreadLocal 的策略及其在异步场景下的处理?

  • SecurityContextHolder 持有当前认证上下文
  • ThreadLocal 策略(MODE_THREADLOCAL/MODE_INHERITABLETHREADLOCAL)
  • 异步线程上下文传播

SecurityContextHolder 用于保存当前线程的 SecurityContext(认证信息),默认使用 ThreadLocal 策略(MODE_THREADLOCAL),即每个线程持有自己的安全上下文。通过 SecurityContextHolder.setContext/getContext 存取。在异步场景(如 @Async、新线程),ThreadLocal 不会自动传播,需显式处理:用 MODE_INHERITABLETHREADLOCAL 让子线程继承,或用 DelegatingSecurityContextExecutor 包装线程池,将 SecurityContext 从父线程传递到子线程。虚拟线程下同样需注意上下文传播。策略选择决定线程间安全上下文是否共享。

SecurityContextHolder 的 ThreadLocal 策略使安全上下文与线程绑定,异步时必须显式传播。理解模式与传播手段是异步安全的关键。

#
★★★

5. Spring Data JPA 在 Spring Boot 3.5+ 对 Hibernate 7 与 Jakarta Persistence 3.2(JPA 3.2)的兼容性要点

请说明 Spring Data JPA 在 Spring Boot 3.5+ 中对 Hibernate 7 与 Jakarta Persistence 3.2(JPA 3.2)的兼容性要点?

  • Hibernate 7 与 Jakarta Persistence 3.2
  • 兼容性要点(命名空间、API、行为)
  • 升级注意点

Spring Boot 3.5+ 的 Spring Data JPA 基于 Hibernate 7 与 Jakarta Persistence 3.2(JPA 3.2)。兼容性要点:全面使用 jakarta.persistence 命名空间;Hibernate 7 对核心 API 有调整(如某些被废弃的 API 移除、Session 行为变化);JPA 3.2 引入一些新特性(如部分查询功能增强)。升级时需注意:依赖版本对齐、Hibernate 配置属性的迁移(hibernate.* 命名规范)、对遗留 API 的替换、以及方言与脏检查行为变化。Spring Boot 的依赖管理已对齐版本,重点是应用代码兼容。

兼容性要点是"命名空间 + API 调整 + 配置迁移"。Hibernate 7 与 JPA 3.2 的升级聚焦版本对齐与遗留 API 替换。

#
★★★

6. Spring Data Redis 在 Spring Boot 3.5+ 中 Lettuce/Jedis 客户端的默认切换

请说明 Spring Data Redis 在 Spring Boot 3.5+ 中 Lettuce 与 Jedis 客户端的默认切换?

  • Lettuce 是默认客户端
  • 切换为 Jedis 的条件
  • 客户端差异与配置

Spring Data Redis 默认使用 Lettuce 作为 Redis 客户端(基于 Netty,异步/响应式,连接复用)。Spring Boot 3.x 中默认客户端为 Lettuce,若需切换为 Jedis,需引入 spring-boot-starter-data-redis 并排除 Lettuce 依赖,加入 jedis 依赖,Spring Boot 的自动配置会按 classpath 中的客户端自动选择。Lettuce 支持异步、响应式、连接池可选;Jedis 是传统同步客户端,直连简单。版本演进中默认策略稳定为 Lettuce,切换需显式配置依赖。

默认客户端是 Lettuce(连接复用、异步能力),切换 Jedis 通过"排除 Lettuce + 引入 Jedis"实现。理解默认与切换机制可灵活选型。

#
★★★

7. Spring Data Redis 的 RedisTemplate/StringRedisTemplate

请说明 Spring Data Redis 的 RedisTemplate 与 StringRedisTemplate 的区别与使用?

  • RedisTemplate 泛型、默认 JDK 序列化
  • StringRedisTemplate 字符串序列化
  • 序列化差异与使用场景

RedisTemplate 是 Spring Data Redis 的操作模板,泛型支持任意类型(K,V),默认使用 JDK 序列化(JdkSerializationRedisSerializer)存储对象,适合复杂对象但序列化不可读、跨语言差;StringRedisTemplate 是 RedisTemplate<String,String> 的特例,key/value 都用 StringRedisSerializer 字符串序列化,可读、跨语言兼容。使用时需注意:用 RedisTemplate 写、StringRedisTemplate 读会因序列化不一致导致数据错乱。一般建议统一序列化策略(如 JSON + StringRedisSerializer key)。

核心差异是"序列化策略"。RedisTemplate 默认 JDK 序列化(对象),StringRedisTemplate 用字符串序列化。混合使用会造成数据不可读。

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory f) {
    RedisTemplate<String, Object> t = new RedisTemplate<>();
    t.setConnectionFactory(f);
    t.setKeySerializer(new StringRedisSerializer());
    t.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    return t;
}
#
★★★

8. Spring Data 多数据源(@EnableJpaRepositories 多配置)在 Spring Boot 3.5+ 的配置细节

请说明 Spring Data 多数据源(@EnableJpaRepositories 多配置)在 Spring Boot 3.5+ 的配置细节?

  • 多个 DataSource 与多个 EntityManagerFactory
  • @EnableJpaRepositories 指定对应 Repository 包
  • 事务管理器与数据源归属

多数据源配置需为每个数据源创建独立的 DataSource、EntityManagerFactory、TransactionManager,并通过 @EnableJpaRepositories 的 basePackages 或 entityManagerFactoryRef/transactionManagerRef 指定各 Repository 归属的数据源。例如主库用 @Primary 数据源,从库用独立配置,@EnableJpaRepositories(basePackages="com.x.primary", entityManagerFactoryRef="primaryEmf")。事务管理器也要按数据源区分(@Transactional 指定 transactionManager)。Spring Boot 3.5+ 沿用此机制,需注意实体类包与 Repository 包对应关系,避免自动配置歧义。

多数据源的核心是"每个数据源自成一套(DataSource + EntityManagerFactory + TransactionManager),并通过 @EnableJpaRepositories 绑定 Repository 包"。明确归属即可避免配置混乱。

#
★★★

9. Spring Security 7.0(Spring Boot 4.x)

请说明 Spring Security 7.0(Spring Boot 4.x)的主要变化?

  • Spring Security 7 的基线提升
  • API 重构与废弃项移除
  • Lambda DSL 与 SecurityFilterChain

Spring Security 7.0(对应 Spring Boot 4.x)的主要变化:移除已废弃的 API(如 WebSecurityConfigurerAdapter、antMatchers 等旧的配置方法),全面采用 Lambda DSL 与 SecurityFilterChain 声明式配置;提升基线(基于 Spring Framework 7 与 Jakarta EE 11);增强 OAuth2/OIDC 支持;默认启用更安全的配置(如默认启用 CSRF、严格路径匹配)。SecurityFilterChain 是 7.x 的核心配置模型,通过 http.requestMatchers(...).authorizeHttpRequests(...) 链式配置。升级需迁移旧 API 到新 DSL。

Spring Security 7 的核心是"去旧 API + 全面 Lambda DSL + 基线提升"。SecurityFilterChain 与 Lambda DSL 是配置主流,升级重点是迁移旧配置。

#
★★★

10. Spring Security OAuth2 Resource Server 的 JWT 校验

请说明 Spring Security OAuth2 Resource Server 的 JWT 校验机制?

  • Resource Server 用 JWT 校验访问令牌
  • 配置 JwtDecoder(密钥/公钥)
  • 校验签名、过期与声明

OAuth2 Resource Server 负责校验客户端携带的 JWT Access Token。Spring Security 通过配置资源服务器(http.oauth2ResourceServer().jwt())并指定 JwtDecoder 完成校验。JwtDecoder 使用对称密钥(NimbusJwtDecoder.withSecretKey)或非对称公钥(withPublicKey,或通过 JWK Set 远程获取)验证签名,并检查 exp(过期)、iss(签发者)、aud(受众)等声明。校验通过后把 JWT 解析为 Authentication 存入 SecurityContext,供后续授权使用。资源服务器不参与签发,只校验。

Resource Server 的 JWT 校验 = 验签 + 声明检查。配置 JwtDecoder(密钥来源)是核心,校验通过后建立认证上下文。

#
★★★

11. Spring Data JPA 的 N+1 查询问题与解决(fetch join、@EntityGraph、批量抓取 batch_fetch_size)

请说明 Spring Data JPA 的 N+1 查询问题,以及 fetch join、@EntityGraph、批量抓取 batch_fetch_size 等解决方案?

  • N+1 问题的产生
  • fetch join、@EntityGraph、batch_fetch_size
  • 各方案适用场景

N+1 问题指查询主实体 N 条时,关联实体又触发 N 次查询(1 次主查询 + N 次关联查询)。解决方案:fetch join(JPQL 中 JOIN FETCH 一次性加载关联,避免逐条查询);@EntityGraph(通过注解声明实体图,定义要 eager 加载的关联,重用查询);批量抓取 batch_fetch_size(设置批量大小,用 IN 一次批量加载多条关联,减少查询次数)。fetch join 适合单次查询精确控制,@EntityGraph 声明式灵活,batch_fetch_size 对不可控的懒加载兜底。三者结合消除 N+1。

N+1 是 ORM 懒加载的经典问题。fetch join 精确、@EntityGraph 声明式、batch_fetch_size 批量兜底,是三大主流解法。

#
★★★

12. OAuth 2.0 与 Passkey 的共存策略

请说明 OAuth 2.0 与 Passkey(无口令认证)的共存策略?

  • OAuth 2.0 授权框架与 Passkey 认证
  • Passkey 作为认证因子
  • 共存与集成

OAuth 2.0 是授权框架(授权码、令牌、资源服务器),Passkey(基于 WebAuthn 的无口令认证)是认证方式。二者可共存:Passkey 用于"认证"(确认用户身份),OAuth 2.0 用于"授权"(发放访问令牌)。共存策略:Passkey 作为登录阶段的主要认证因子,认证成功后通过 OAuth 2.0 授权流程签发 Access Token / Refresh Token;授权服务器可集成 Passkey 作为认证方式(替代密码),资源服务器仍按 OAuth 2.0 校验令牌。二者分层协作,Passkey 解决"谁",OAuth 解决"能做什么"。

Passkey 与 OAuth 2.0 是"认证"与"授权"两个层次,可无缝共存——Passkey 认证身份,OAuth 发放令牌。理解分层即可设计共存。

#
★★

13. @Transactional 在 Repository 方法的默认语义

请说明 @Transactional 在 Spring Data Repository 方法的默认语义?

  • Repository 方法默认事务
  • 查询方法只读、修改方法读写
  • 覆盖默认行为

Spring Data JPA 的 Repository 方法默认有事务语义:查询方法(find、get、read 开头)默认只读事务(readOnly=true),修改方法(save、delete 等)默认读写事务。这些默认事务由 Spring Data 的 CRUD 实现自动配置(SimpleJpaRepository 上的 @Transactional)。若需定制,可在自定义方法上显式标注 @Transactional 覆盖默认。注意:默认事务粒度为单个方法,跨方法业务需在 Service 层标注 @Transactional 组织事务边界。

Repository 默认事务是"查询只读、修改读写"的约定。业务事务边界应在 Service 层统一管理,Repository 默认事务是单方法粒度。

#
★★

14. FilterInvocationSecurityMetadataSource 的动态 URL 权限

请说明 FilterInvocationSecurityMetadataSource 如何实现动态 URL 权限?

  • SecurityMetadataSource 提供 URL 的授权属性
  • 动态从数据库加载权限配置
  • 与授权管理器协作

FilterInvocationSecurityMetadataSource 用于为每个请求 URL 提供对应的授权属性(如所需角色),Spring Security 通过它实现 URL 级授权。实现动态 URL 权限:自定义 FilterInvocationSecurityMetadataSource,从数据库或配置中心动态加载 URL 与权限的映射,在 getAttributes 中根据请求 URL 返回所需权限,再由 AccessDecisionManager 或 AuthorizationManager 决策。常用实现为 DefaultFilterInvocationSecurityMetadataSource,可自定义替换以支持动态权限(如基于数据库的权限表)。

动态 URL 权限的本质是"把 URL-权限映射从静态配置改为动态数据源"。实现自定义 SecurityMetadataSource + 授权管理器即可支持动态权限。

#
★★

15. JpaRepository 的方法命名派生查询(findByXxxAndYxx)与 @Query 的优先级

请说明 JpaRepository 方法命名派生查询(findByXxxAndYxx)与 @Query 的优先级?

  • 方法命名派生查询
  • @Query 显式查询
  • 优先级与使用场景

Spring Data JPA 支持两种查询方式:方法命名派生查询(如 findByUsernameAndStatus,根据方法名自动解析为查询)与 @Query 注解显式声明 JPQL/SQL 查询。当二者同时存在时,@Query 优先(方法名不再解析,直接使用 @Query 内容)。@Query 适合复杂查询、性能优化、原生 SQL;方法命名查询适合简单查询、可读性好。优先级规则:@Query 显式查询优先于方法命名派生查询。

优先级是"显式 @Query > 方法名派生"。方法名派生适合简单场景,@Query 应对复杂查询,二者按需选择、@Query 优先。

#
★★

16. OAuth 2.0 的 FAPI(Financial-grade API)

请说明 OAuth 2.0 的 FAPI(Financial-grade API)及其增强要求?

  • FAPI 是金融级 OAuth 2.0 扩展
  • 增强安全要求(PKCE、JARM、sender-constraint)
  • 高安全等级

FAPI(Financial-grade API)是面向金融行业的高安全等级 OAuth 2.0/OIDC 标准(OpenID Foundation 制定),在标准 OAuth 2.0 基础上强化了安全要求。核心增强:强制使用 PKCE(防授权码拦截)、要求非对称密钥(RS256/PS256)与受约束的令牌(sender-constrained tokens,如 DPoP 或 mTLS)、JWT 的 JARM(JWT 响应模式)保证授权响应完整性、严格的重定向 URI 校验、TLS 安全要求。FAPI 适用于银行、支付等对安全性要求极高的场景。

FAPI 是 OAuth 2.0 的"金融级强化版",通过 PKCE、非对称签名、sender-constrained token、JARM 等提升防篡改与防拦截能力。

#
★★

17. OAuth 2.0 的授权码模式(Authorization Code)

请说明 OAuth 2.0 的授权码模式(Authorization Code)的流程?

  • 授权码模式四个角色
  • 授权码换取令牌的流程
  • 安全性(PKCE)

OAuth 2.0 授权码模式(Authorization Code)是最安全的授权流程,适合有后端服务器的 Web 应用。流程:客户端引导用户到授权服务器(带 client_id、redirect_uri、scope 等参数)→ 用户登录并授权 → 授权服务器返回授权码(code)到 redirect_uri → 客户端用 code + client_secret 向授权服务器令牌端点换取 Access Token(和 Refresh Token)→ 客户端用令牌访问资源服务器。由于授权码在令牌端点用 client_secret 换令牌,令牌不暴露给浏览器,安全性高。配合 PKCE 可防授权码拦截(尤其无 server 场景)。

授权码模式的核心是"授权码作为中间凭证,用 client_secret 在服务端换令牌",避免令牌暴露在浏览器,是 OAuth 2.0 最安全的授权流程。

#
★★

18. OAuth 2.0 的角色(Resource Owner/Client/Authorization Server/Resource Server)

请说明 OAuth 2.0 的四个角色:Resource Owner、Client、Authorization Server、Resource Server?

  • 四个角色的定义
  • 各角色的职责
  • 角色间的交互

OAuth 2.0 定义四个角色:Resource Owner(资源所有者,通常是用户,授权访问其资源);Client(客户端应用,代表用户请求访问资源);Authorization Server(授权服务器,负责认证用户并发放访问令牌);Resource Server(资源服务器,持有受保护资源并校验令牌)。交互流程:Resource Owner 授权 Client → Client 向 Authorization Server 获取令牌 → Client 用令牌访问 Resource Server → Resource Server 校验令牌后返回资源。四角色可部署在不同服务器,也可合并(如授权服务器与资源服务器同体)。

四角色是 OAuth 2.0 的架构骨架。理解"谁授权、谁请求、谁发令牌、谁校验"即可把握 OAuth 2.0 的整体协作。

#
★★

19. OAuth 2.1 的简化与 PKCE 的强制

请说明 OAuth 2.1 的简化,以及 PKCE 的强制要求?

  • OAuth 2.1 的简化(移除隐式模式等)
  • PKCE 强制要求
  • 更安全的默认

OAuth 2.1 对 OAuth 2.0 进行了简化与安全强化:移除隐式授权模式(Implicit Flow)与资源所有者密码凭证模式(Resource Owner Password Credentials),统一推荐授权码模式 + PKCE;强制 PKCE 用于所有授权码流程(包括原生应用与 Web 应用),防止授权码拦截;继续使用范围最小化、简化令牌刷新等。OAuth 2.1 的核心是"更简单的选项 + 更强的安全默认",PKCE 从可选变为强制。

OAuth 2.1 的简化是"收窄授权模式 + 强制 PKCE",让授权码+PKCE成为唯一推荐路径,提升整体安全性。

#
★★

20. SecurityFilterChain 与 WebSecurityConfigurerAdapter 的演进

请说明 SecurityFilterChain 与 WebSecurityConfigurerAdapter 的演进关系?

  • WebSecurityConfigurerAdapter 已废弃
  • SecurityFilterChain 全新配置模型
  • 演进原因与迁移

WebSecurityConfigurerAdapter 是 Spring Security 5.x 的配置类基类,通过继承并重写 configure 方法配置安全;在 Spring Security 5.7+ 已废弃,6.x 全面移除,代之以 SecurityFilterChain 配置模型。SecurityFilterChain 通过 @Bean 暴露,用 http.requestMatchers(...).authorizeHttpRequests(...).formLogin(...) 等链式 Lambda DSL 声明安全规则,无需继承,更灵活、可组合多个安全链。演进原因:Lambda DSL 更安全、可读、避免继承带来的歧义;支持多 SecurityFilterChain 按匹配规则分发。升级需把继承配置迁移到 SecurityFilterChain Bean。

演进是"继承配置 → 组合式配置"。SecurityFilterChain + Lambda DSL 更灵活、支持多链,是 Spring Security 6+/7 的配置主流。

#
★★

21. SecurityFilterChain 在 Spring Security 7 中的多链配置与 HttpSecurity 组合

请说明 SecurityFilterChain 在 Spring Security 7 中的多链配置与 HttpSecurity 组合?

  • 多 SecurityFilterChain 按匹配规则分发
  • HttpSecurity 组合配置
  • 优先级与匹配

Spring Security 7 中可通过定义多个 SecurityFilterChain Bean 实现多链配置,每个 SecurityFilterChain 用 requestMatchers 指定匹配的 URL 模式,Spring Security 按顺序匹配第一个符合条件的链。HttpSecurity 提供链式安全配置(authorizeHttpRequests、formLogin、oauth2ResourceServer、csrf 等),可在多链中组合不同安全策略(如 /api/用 JWT 无状态、/admin/ 用表单认证)。多链配置按声明顺序匹配,第一个匹配者生效,可对不同路径应用不同安全规则。

多链配置 + HttpSecurity 组合实现"按路径差异化的安全策略"。核心是 requestMatchers 匹配与声明顺序的优先级。

#
★★

22. Spring Data Elasticsearch 的工程应用

请说明 Spring Data Elasticsearch 的工程应用?

  • ElasticsearchRepository
  • 文档映射与查询
  • 与 MySQL 的同步

Spring Data Elasticsearch 通过 ElasticsearchRepository 提供基于 Elasticsearch 的数据访问,支持方法名派生查询、@Query 注解查询、聚合与分页。通过 @Document 映射实体到索引,@Field 控制字段类型与分词。工程应用:全文搜索、复杂查询、聚合分析。实践中常与 MySQL 配合(MySQL 存主数据,Elasticsearch 存索引数据),通过消息队列或事件同步数据,保证一致性。需注意重索引、分词器配置与索引迁移。

Spring Data Elasticsearch 使 Elasticsearch 上的搜索、聚合、分页以 Repository 方式使用。工程要点是与 MySQL 协同、索引管理与数据同步。

#
★★

23. Spring Data JPA 与 Kotlin/Scala 的边界

请说明 Spring Data JPA 与 Kotlin/Scala 的边界?

  • Kotlin 与 JPA 的兼容性(data class、非空)
  • Scala 与 JPA 的边界
  • 语言特性与 JPA 的摩擦

Spring Data JPA 主要面向 Java,与 Kotlin 兼容性较好:Kotlin data class 可用作实体(需注意无参构造、非空默认值、JPA 空值处理),Spring Data JPA 支持 Kotlin 的扩展(如 Kotlin 协程查询)。但存在边界:Kotlin 的 data class 默认 final 与不可变,与 JPA 的懒加载代理(CGLIB 子类)冲突,需用 open 关键字或配置;非空类型与 JPA 的 null 语义需注意。Scala 与 JPA 兼容性较差,因 Scala 类默认 final、伴生对象与 JPA 构造要求冲突,通常不推荐大量使用 JPA + Scala,多用其他持久化方案。总之 JPA 的代理/构造机制与 JVM 动态语言特性存在摩擦。

JPA 依赖"无参构造 + 可继承代理 + 可变字段",与 Kotlin/Scala 的不可变、final 特性冲突。Kotlin 兼容较好,Scala 较差,需处理语言特性与 JPA 约束的边界。

#
★★

24. Spring Data JPA 的 Auditing(@CreatedDate/@LastModifiedDate)

请说明 Spring Data JPA 的 Auditing(@CreatedDate/@LastModifiedDate)功能?

  • @CreatedDate/@LastModifiedDate/@CreatedBy/@LastModifiedBy
  • @EnableJpaAuditing 开启
  • AuditorAware 获取当前用户

Spring Data JPA 的 Auditing 自动填充审计字段:@CreatedDate 填充创建时间、@LastModifiedDate 填充最后修改时间、@CreatedBy/@LastModifiedBy 填充创建/修改人。使用需:实体类标注 @EntityListeners(AuditingEntityListener.class) 并在字段上标注以上注解,配置类加 @EnableJpaAuditing 开启,通过 AuditorAware 接口提供当前用户用于 @CreatedBy/@LastModifiedBy。审计字段由 JPA 在保存/更新时自动填充,无需手动设置。

Auditing 是"声明式审计字段自动填充"。@EnableJpaAuditing + AuditorAware + 实体注解三者配合,自动维护创建/修改时间与人员。

@EnableJpaAuditing
@Configuration
public class JpaConfig {}

@Entity
@EntityListeners(AuditingEntityListener.class)
public class Order {
    @CreatedDate private LocalDateTime createdAt;
    @LastModifiedDate private LocalDateTime updatedAt;
}
#
★★

25. Spring Data JPA 的 Optional 返回值约定

请说明 Spring Data JPA 的 Optional 返回值约定?

  • Repository 方法可用 Optional 返回
  • 空结果返回 Optional.empty()
  • 避免 NPE 与空值处理

Spring Data JPA 的 Repository 方法返回值可以用 Optional 包装,例如 findById 返回 Optional,查询无结果时返回 Optional.empty() 而非 null。这要求方法语义明确(单条结果查询),使用 Optional 强制调用方处理空值,避免 NPE。注意:若底层查询返回多行但方法声明 Optional,会抛异常;Optional 适用于单结果查询。Spring Data 也支持返回 List、Stream、Future 等。Optional 是"空值安全"的返回值约定。

Optional 返回值让"空结果"显式化,调用方必须处理空值,提升空安全。适用于单条查询,多结果需用 List/Stream。

#
★★

26. Spring Data JPA 的 Repository 抽象(JpaRepository/PagingAndSortingRepository)

请说明 Spring Data JPA 的 Repository 抽象体系(JpaRepository、PagingAndSortingRepository 等)?

  • Repository 接口层级
  • JpaRepository 与 PagingAndSortingRepository
  • 各接口能力

Spring Data JPA 的 Repository 抽象是分层接口体系:Repository 是最顶层标记接口;CrudRepository 提供 CRUD 与持久化基础能力;PagingAndSortingRepository 在 CrudRepository 基础上增加分页与排序(findAll(Pageable));JpaRepository 继承 PagingAndSortingRepository 并增加 JPA 特有能力(flush、批量删除、getById 等)。开发者自定义 Repository 接口继承 JpaRepository 即可获得完整 CRUD、分页、排序能力,并可按方法名派生查询。选择继承层级决定可获得的能力。

Repository 分层是"基础 CRUD → 分页排序 → JPA 特有"的能力递进。继承相应接口获取所需能力,是 Spring Data 的便捷抽象。

#
★★

27. Spring Data MongoDB 的 Repository 模式

请说明 Spring Data MongoDB 的 Repository 模式?

  • MongoRepository 与查询方法
  • 文档映射与聚合
  • 方法名派生查询

Spring Data MongoDB 提供 MongoRepository 接口,支持方法名派生查询(如 findByTitleContaining)、@Query 注解查询、分页排序、聚合(Aggregation)等。通过 @Document 映射实体到文档集合,@Id 映射主键。Repository 模式让 MongoDB 访问像 JPA 一样声明式,TEMPLATE API(MongoTemplate)提供更底层灵活的查询。工程上 Repository 适合常规 CRUD 与查询,MongoTemplate 适合复杂聚合与动态查询。

MongoRepository 把 MongoDB 访问抽象为 Repository 模式,方法名派生查询与 @Query 覆盖常见需求,复杂场景用 MongoTemplate。

#
★★

28. Spring Data MongoDB 的聚合管道(Aggregation Pipeline)

请说明 Spring Data MongoDB 的聚合管道(Aggregation Pipeline)?

  • 聚合管道阶段($match/$group/$project 等)
  • Aggregation 构建
  • 与 Repository 的配合

Spring Data MongoDB 的聚合管道(Aggregation Pipeline)由多个阶段(stage)组成:$match(过滤)、$group(分组)、$project(投影)、$sort(排序)、$limit/$skip(分页)、$unwind(展开数组)、$lookup(关联)等。通过 Aggregation 类构建管道,如 Aggregation.newAggregation(match(...), group(...), project(...)),再用 MongoTemplate.aggregate 执行。聚合管道在数据库端完成数据处理,适合统计、分组、报表等复杂查询。可配合 Repository 的 @Aggregation 注解或在 MongoTemplate 中构建。

聚合管道把数据处理下沉到数据库,灵活组合各阶段实现复杂统计。理解阶段顺序与 Aggregation 构建是使用聚合的关键。

#
★★

29. Spring Data R2DBC(反应式)的边界

请说明 Spring Data R2DBC(反应式)的边界?

  • R2DBC 反应式数据库访问
  • Repository 与 ReactiveCrudRepository
  • 边界(非阻塞、事务)

Spring Data R2DBC 提供反应式数据库访问,通过 ReactiveCrudRepository 提供返回 Mono/Flux 的响应式 CRUD。边界:R2DBC 是非阻塞驱动,Repository 方法返回响应式类型(Mono/Flux),需在反应式链路中消费;事务需用 TransactionalOperator(而非基于 ThreadLocal 的 @Transactional);分页/聚合/关联等能力相较 JPA 有限;适合与 WebFlux 全链路非阻塞。R2DBC 的边界是"反应式、非阻塞、事务需响应式处理",不适合阻塞栈混用。

R2DBC 的边界是"反应式 + 非阻塞 + 响应式事务"。它与 Spring Data JPA 的阻塞模型不同,需在 WebFlux 生态中使用。

#
★★

30. Spring Data REST 的过度暴露风险

请说明 Spring Data REST 的过度暴露风险?

  • Spring Data REST 自动暴露 Repository 为 REST 接口
  • 过度暴露风险(数据泄露、非法操作)
  • 防护措施

Spring Data REST 会自动把 Repository 暴露为 RESTful 接口(基于 Repository 生成 CRUD 端点),开发便捷但存在过度暴露风险:所有 Repository 方法(含删除、批量操作)自动暴露,可能泄露敏感数据、允许未授权的写操作、暴露内部结构。防护措施:配置 @RepositoryRestResource(exported=false) 关闭不需要暴露的 Repository;用 @PreAuthorize/@Secured 控制端点权限;限制暴露的关联与字段;配合 Spring Security 保护端点;谨慎启用,按需暴露。核心是"默认暴露需收敛,最小暴露 + 权限控制"。

Spring Data REST 的便捷伴随"过度暴露"风险。通过 exported=false 收敛暴露面 + 权限注解保护,是安全使用的前提。

#
★★

31. Spring Data 与 Spring Modulith 的事件协作

请说明 Spring Data 与 Spring Modulith 的事件协作?

  • Spring Data 的领域事件(@DomainEvents)
  • Spring Modulith 的事件发布
  • 模块间通过事件协作

Spring Data 支持领域事件(通过 @DomainEvents 注解发布实体事件),Spring Modulith 提供模块化事件(ApplicationModuleEvent)与跨模块发布订阅。二者协作:实体在保存时通过 @DomainEvents 发布业务事件,Spring Modulith 将这些事件关联到模块并支持跨模块投递(如 @TransactionalEventListener 在事务提交后投递),实现模块间解耦协作。Spring Data 负责"数据层事件",Spring Modulith 负责"模块间事件编排",结合实现事件驱动的模块化。

Spring Data 提供实体领域事件,Spring Modulith 提供模块级事件编排,二者结合让"数据变更触发跨模块事件"成为模块化架构的协作方式。

#
★★

32. Spring Data 模块的版本兼容矩阵

请说明 Spring Data 模块的版本兼容矩阵?

  • Spring Data 各模块(JPA/MongoDB/Redis)版本
  • 与 Spring Boot 的版本对齐
  • 版本兼容性

Spring Data 是一系列模块(Spring Data JPA、MongoDB、Redis、Elasticsearch、R2DBC 等)的集合,各模块有独立的版本号(如 2024.1.x、2025.x 等 release train 命名)。版本兼容矩阵指各模块与 Spring Boot、Spring Framework 的版本对齐关系:Spring Boot 依赖管理统一锁定 Spring Data 各模块版本,保证兼容。开发时通常通过 Spring Boot 的 BOM 管理版本,避免手动指定造成不兼容。升级 Spring Boot 时,对应 Spring Data 模块版本随之更新,需关注各模块间的 API 兼容。

版本兼容矩阵的核心是"Spring Boot 统一管理 Spring Data 模块版本"。通过 BOM 对齐版本,避免手动配置兼容问题。

#
★★

33. Spring Data 自定义 Repository 片段

请说明 Spring Data 自定义 Repository 片段(Fragment)的实现方式?

  • 自定义 Repository 方法
  • 片段接口与实现类
  • 与默认 Repository 组合

Spring Data 允许自定义 Repository 片段(custom fragment):定义自定义接口(含需补充的方法),提供实现类(命名约定 接口名 + Impl,如 UserRepositoryCustomImpl),Repository 接口继承自定义接口即可。Spring Data 在运行时把默认实现与自定义实现组合成代理。该方法用于在 Repository 中补充复杂查询、批量操作等 Spring Data 无法自动生成的方法。命名约定(Impl 后缀)与代理组合是关键,也可通过 @RepositoryDefinition 等配置。

自定义片段是对 Spring Data 自动生成能力的补充。通过"接口 + Impl 实现类 + 继承"组合,让 Repository 承载自定义逻辑。

public interface UserRepositoryCustom {
    List<User> findCustomUsers();
}
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
    public List<User> findCustomUsers() { /* ... */ }
}
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {}
#
★★

34. Spring Security 6.x 的 Lambda DSL 配置

请说明 Spring Security 6.x 的 Lambda DSL 配置方式?

  • Lambda DSL 链式配置
  • 替代旧 and() 方法
  • 配置示例

Spring Security 6.x 推荐使用 Lambda DSL 配置,即通过 Lambda 表达式链式配置 HttpSecurity,替代旧的 and() 方法。例如 http.authorizeHttpRequests(auth -> auth.requestMatchers("/public/**").permitAll().anyRequest().authenticated()).formLogin(withDefaults())。Lambda DSL 通过每个配置方法接收一个 Lambda,在 Lambda 内完成该模块的子配置,代码更清晰、避免链式断裂、类型安全。Spring Security 6 全面移除 WebSecurityConfigurerAdapter,Lambda DSL 是标准配置方式。

Lambda DSL 是 Spring Security 6+ 的配置范式,用 Lambda 组织配置项,可读性与类型安全优于旧 and() 链。掌握其写法是配置安全的基础。

#
★★

35. Spring Security 与 CORS 的协作配置顺序,CorsFilter 应在 SecurityFilterChain 之前处理预检请求

请说明 Spring Security 与 CORS 的协作配置顺序,CorsFilter 为何应在 SecurityFilterChain 之前处理预检请求?

  • CORS 预检请求(OPTIONS)处理
  • CorsFilter 在 SecurityFilterChain 之前
  • 预检请求不带认证信息

浏览器跨域预检请求(OPTIONS)通常不携带认证信息(Authorization 头),若 SecurityFilterChain 先于 CORS 处理预检请求,会因无认证而拒绝 OPTIONS,导致跨域失败。因此 CorsFilter 应在 SecurityFilterChain 之前处理预检请求:先由 CorsFilter 识别 OPTIONS 预检并返回 CORS 头,放行预检;实际请求再由 SecurityFilterChain 处理认证。配置时通过 http.cors(withDefaults()) 启用 CORS 支持,并确保 CorsFilter 在过滤器链中位于安全过滤器之前。这是"预检请求不放行则跨域失败"的关键。

CORS 与 Security 的顺序是"预检先于认证"——OPTIONS 预检无认证信息,需 CorsFilter 先行放行。配置顺序错误会导致跨域失败。

#
★★

36. Spring Security 的 ACL 模块

请说明 Spring Security 的 ACL 模块及其作用?

  • ACL 对象级授权(Object-Level Security)
  • 基于特定对象的权限(针对某条记录)
  • ACL 存储与查询

Spring Security 的 ACL(Access Control List)模块用于对象级授权(Object-Level Security),即针对特定领域对象(如某条订单记录)的细粒度权限控制,区别于角色/URL 级授权。ACL 通过 ACL 表存储对象与权限的映射(谁对哪个对象拥有什么权限),运行时通过 AclService 查询判断当前用户对特定对象是否有权限。适用于"同一用户对不同对象权限不同"的场景。ACL 需要维护 ACL 元数据表,支持 ACL 权限(读、写、删除等),实现较复杂,适用于需要对象级细粒度授权的场景。

ACL 是"对象级"授权,针对具体数据实例判断权限。相比角色/URL 级授权更细粒度,依赖 ACL 表存储与查询,复杂度较高。

#
★★

37. Spring Data 分页(Pageable/Sort)的实现与深分页(大 offset)的性能问题与优化

请说明 Spring Data 分页(Pageable/Sort)的实现,以及深分页(大 offset)的性能问题与优化?

  • Pageable/Sort 分页参数
  • 深分页(大 offset)性能问题
  • 优化(游标/keyset 分页、限制 offset)

Spring Data 通过 Pageable(页码、每页大小、排序)与 Sort 实现分页,Repository 方法接收 Pageable 返回 Page。深分页问题:当 offset 很大时(如第 100000 条开始),数据库需扫描并丢弃前 offset 行,性能随 offset 增大而恶化。优化方式:keyset/游标分页(基于索引列定位,如 WHERE id > lastId ORDER BY id LIMIT n),避免大 offset;限制最大 offset 与页大小;用索引覆盖排序字段;或对超大分页采用分批导出。Pageable 深分页在数据量大时性能差,需用游标分页替代。

深分页的本质是"扫描并丢弃前 offset 行"。keyset/游标分页通过索引定位避免大 offset,是海量数据分页的标准优化。

#
★★

38. Spring Data JPA 的乐观锁(@Version)与悲观锁(@Lock)的使用边界与冲突处理

请说明 Spring Data JPA 的乐观锁(@Version)与悲观锁(@Lock)的使用边界与冲突处理?

  • @Version 乐观锁
  • @Lock 悲观锁
  • 使用边界与冲突处理

乐观锁通过 @Version 版本字段实现:更新时比较版本号,版本不一致则抛 OptimisticLockException,适用于读多写少、冲突概率低的场景,轻量无锁。悲观锁通过 @Lock(LockModeType.PESSIMISTIC_WRITE) 在查询时加数据库锁(SELECT ... FOR UPDATE),适用于写冲突频繁、需保证强一致的场景,但会阻塞并发。使用边界:乐观锁适合并发冲突少的业务,悲观锁适合冲突预期高或需锁定数据的场景。冲突处理:乐观锁冲突捕获 OptimisticLockException 重试或提示;悲观锁依赖数据库锁等待与超时处理。

乐观锁(版本号)与悲观锁(DB 锁)在"冲突策略"上取舍——乐观锁冲突时重试,悲观锁提前加锁。按冲突概率与一致性要求选择。

#

39. Spring Security 7 对 OpenID Connect(OIDC)的支持与登录流程

请说明 Spring Security 7 对 OpenID Connect(OIDC)的支持与登录流程?

  • OIDC 基于 OAuth 2.0 的身份层
  • id_token 与身份认证
  • 登录流程(授权码 + 用户信息)

OIDC(OpenID Connect)在 OAuth 2.0 基础上增加身份层,通过 id_token 提供用户身份信息。Spring Security 7 支持 OIDC 客户端登录(oauth2Login()),使用授权码流程:客户端引导用户到 OpenID Provider(OP)→ 用户登录并授权 → OP 返回授权码与 id_token → 客户端校验 id_token 签名并解析用户声明 → 通过 OidcUser 建立认证。OIDC 暴露 /userinfo 端点获取用户信息,支持 OIDC discovery(获取元数据)。Spring Security 的 oauth2Login 与 oidcLogout 支持完整的 OIDC 登录与登出。

OIDC 是"OAuth 2.0 + 身份层",通过 id_token 携带身份。Spring Security 的 oauth2Login 实现授权码登录并解析 id_token,是 OIDC 登录的核心。

#

40. Spring Security 的过滤器链(FilterChain)

请说明 Spring Security 的过滤器链(FilterChain)机制?

  • 过滤器链(SecurityFilterChain)的过滤器组成
  • 过滤器的执行顺序
  • 各过滤器职责(认证、授权、CSRF)

Spring Security 通过一条过滤器链(SecurityFilterChain)处理请求,链中由多个 Filter 组成,按顺序执行。核心过滤器包括:SecurityContextPersistenceFilter(加载/保存 SecurityContext)、CsrfFilter(CSRF 防护)、UsernamePasswordAuthenticationFilter(表单登录认证)、BasicAuthenticationFilter(Basic 认证)、BearerTokenAuthenticationFilter(JWT 认证)、AuthorizationFilter(授权)、ExceptionTranslationFilter(异常转换)等。链中过滤器按注册顺序执行,前面的过滤器负责认证,后面的负责授权,异常统一由 ExceptionTranslationFilter 转换。可通过自定义 Filter 加入链,也可配置多个 SecurityFilterChain 按路径分发。

过滤器链是 Spring Security 的请求处理骨架,各过滤器按"认证 → 授权 → 异常处理"的顺序协作。理解链的结构与顺序是排查安全问题的关键。

#

41. UserDetailsService 与 AuthenticationProvider 在自定义认证中的协作边界

请说明 UserDetailsService 与 AuthenticationProvider 在自定义认证中的协作边界?

  • UserDetailsService 负责加载用户
  • AuthenticationProvider 负责认证逻辑
  • 协作边界

UserDetailsService 与 AuthenticationProvider 在认证中职责不同:UserDetailsService 负责根据用户名从数据源加载用户信息(返回 UserDetails),只有一个方法 loadUserByUsername;AuthenticationProvider 负责认证逻辑(校验密码、令牌等),通过 authenticate 方法返回认证结果。协作边界:DaoAuthenticationProvider 是典型的 AuthenticationProvider 实现,它内部调用 UserDetailsService 加载用户,再校验密码,验签通过则返回已认证的 Authentication。自定义认证时,简单场景自定义 UserDetailsService 提供用户来源,复杂认证逻辑(如多因素、自定义凭据)自定义 AuthenticationProvider。UserDetailsService 只管"取用户",AuthenticationProvider 管"验认证"。

边界是"取用户"与"验认证"分离。UserDetailsService 提供用户数据,AuthenticationProvider 决定认证成败,DaoAuthenticationProvider 是二者的结合点。

#

42. 异常处理入口(AuthenticationEntryPoint/AccessDeniedHandler)的未认证与无权限区分

请说明 AuthenticationEntryPoint 与 AccessDeniedHandler 在未认证与无权限场景下的区分?

  • AuthenticationEntryPoint 处理未认证(401)
  • AccessDeniedHandler 处理已认证但无权限(403)
  • 区别与配置

AuthenticationEntryPoint 与 AccessDeniedHandler 是 Spring Security 的两个异常处理入口,针对不同场景:AuthenticationEntryPoint 处理"未认证"(anonymous,未登录或凭据无效),触发 401 Unauthorized,引导用户登录或返回认证错误;AccessDeniedHandler 处理"已认证但无权限"(认证通过但权限不足),触发 403 Forbidden,返回权限不足错误。配置时通过 http.exceptionHandling(ex -> ex.authenticationEntryPoint(...).accessDeniedHandler(...)) 分别设置。区分二者是 REST 场景下返回正确状态码(401 vs 403)的关键。

关键区分是"未认证=401"与"已认证无权限=403"。AuthenticationEntryPoint 管前者,AccessDeniedHandler 管后者,正确配置才能返回合理状态码。

#

43. Spring Data R2DBC(响应式数据库)在 JDK 25 虚拟线程下的应用取舍

请说明 Spring Data R2DBC(响应式数据库)在 JDK 25 虚拟线程下的应用取舍?

  • R2DBC 反应式 vs 虚拟线程阻塞
  • 虚拟线程化简单阻塞 JDBC
  • 取舍依据

在 JDK 25 虚拟线程下,Spring Data R2DBC 的应用取舍:虚拟线程使得阻塞式 JDBC 也能以极低线程成本支撑高并发,编程简单,因此很多原本用 R2DBC 的场景可改用"虚拟线程 + 阻塞 JDBC"。但 R2DBC 仍有价值:全链路非阻塞、支持流式/背压、在 WebFlux 反应式栈中天然契合。取舍依据:若应用已采用 WebFlux 反应式栈,保留 R2DBC 保持全链路非阻塞;若应用是阻塞 MVC 栈,虚拟线程 + JDBC 更简单实用。R2DBC 在虚拟线程时代适用收窄,但反应式生态中不可替代。

虚拟线程削弱了 R2DBC 的"非阻塞"优势(阻塞变便宜),但全链路反应式、流式背压场景仍需 R2DBC。取舍关键在"是否全链路反应式"。

#

44. Spring Data JPA 批量更新/删除(@Modifying 与 @Query)为何需要 clearAutomatically/flushAutomatically 配置

请说明 Spring Data JPA 批量更新/删除(@Modifying 与 @Query)为何需要 clearAutomatically/flushAutomatically 配置?

  • @Modifying 批量更新/删除
  • clearAutomatically 清除持久化上下文
  • flushAutomatically 自动 flush

@Modifying 注解配合 @Query 用于批量更新/删除(执行 DML 语句)。但批量 DML 直接操作数据库,绕过持久化上下文(Persistence Context),导致上下文中的实体与数据库不一致。因此需要配置:clearAutomatically=true 在批量执行后自动清空持久化上下文,避免脏数据;flushAutomatically=true 在批量执行前自动 flush 未提交的变更,保证执行顺序一致。不加这两个配置,可能读到旧数据或批量语句与缓存实体冲突。

@Modifying 批量操作绕过持久化上下文,clearAutomatically 清理上下文、flushAutomatically 先 flush 待变更,保证一致性。