GraphQL 与 Spring for GraphQL

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

1. GraphQL 与 REST 的选型边界,字段级查询、客户端驱动与服务端资源模型

GraphQL 与 REST 的选型边界:字段级查询、客户端驱动与服务端资源模型如何权衡?

  • 字段级查询
  • 客户端驱动
  • 资源模型

GraphQL 与 REST 选型边界:GraphQL 支持字段级查询(客户端按需选择字段,避免过度/不足获取)、客户端驱动(客户端决定查询结构,声明式描述数据需求)、单一端点(POST /graphql),适合复杂嵌套数据、多端复用、字段粒度需求强的场景。REST 基于资源模型(URL 表示资源、方法表示操作),服务端驱动(服务端定义资源与字段),天然支持缓存、幂等、HTTP 语义,适合资源化、公开、可缓存接口。选型:字段级需求强、客户端多样、嵌套复杂用 GraphQL;资源化、可缓存、需 HTTP 语义用 REST。边界是"客户端驱动 vs 服务端资源模型"。

GraphQL 客户端驱动、字段级查询适合复杂嵌套;REST 服务端资源模型、HTTP 语义适合公开可缓存。按需求驱动方式选型。

#
★★★

2. GraphQL 的 Schema 定义(SDL),类型系统、标量、枚举、接口与联合类型

GraphQL 的 Schema 定义(SDL):类型系统、标量、枚举、接口与联合类型是什么?

  • SDL 类型系统
  • 标量
  • 接口/联合

GraphQL Schema 用 SDL(Schema Definition Language)定义:类型系统包括 Object 类型(type User { id: ID! name: String })、标量(Scalar:ID、String、Int、Float、Boolean、自定义标量)、枚举(enum)、接口(interface,多个类型实现共同字段)、联合类型(union,多个类型中选一个,无共同字段)。Query 定义查询入口,Mutation 定义变更,Subscription 定义订阅。SDL 描述图结构,字段有类型与非空(!)、列表([])。价值:类型系统让 schema 自描述、可验证、驱动代码生成。

SDL 类型系统含 Object、标量、枚举、接口、联合,定义 Query/Mutation/Subscription。schema 自描述可验证。

#
★★★

3. GraphQL 的 N+1 查询问题,DataLoader 批处理与字段级解析器(resolver)

GraphQL 的 N+1 查询问题:DataLoader 批处理与字段级解析器(resolver)如何解决?

  • N+1 查询
  • DataLoader
  • 字段解析器

GraphQL 的 N+1 问题:查询列表的每行都触发子字段的独立解析,如果一个字段解析器对每行查一次数据库,产生 N 次查询(N+1)。例如查询 N 个用户,每个用户又要查其订单,逐行查订单就是 N 次查询。解决:DataLoader 批处理——把同一字段的多次解析合并为一次批量加载(按 key 批量查询,如一次查 N 个用户的订单),并把结果按 key 分发回各解析器;字段级解析器(resolver)按字段提供数据,配合 DataLoader 做批量去重。DataLoader 用缓存 + 批处理,把 N+1 转化为 1+1(一次查询所有用户 + 一次批量订单)。核心是"批处理合并查询"。

N+1 是逐行解析触发逐条查询。DataLoader 把同字段多次解析合并为一次批量查询,消除 N+1。是 GraphQL 性能关键。

#
★★★

4. Spring for GraphQL 的注解式控制器,@QueryMapping/@MutationMapping/@SchemaMapping

Spring for GraphQL 的注解式控制器:@QueryMapping/@MutationMapping/@SchemaMapping 是什么?

  • @QueryMapping
  • @MutationMapping
  • @SchemaMapping

Spring for GraphQL 的注解式控制器:@QueryMapping 映射 GraphQL 的 Query 字段到方法(处理查询);@MutationMapping 映射 Mutation 字段(处理变更);@SchemaMapping 映射 schema 中任意字段(如子字段、类型字段)到方法(处理字段解析)。方法参数绑定询问参数,返回值序列化为 GraphQL 字段。这些注解把 GraphQL schema 的字段映射到 Java 方法,类似 Spring MVC 的 @RequestMapping。@SchemaMapping 用于子字段解析(字段级 resolver),配合批处理。价值:注解式开发,简洁映射 schema 到方法。

@QueryMapping 处理查询、@MutationMapping 处理变更、@SchemaMapping 处理任意字段/子字段解析。是注解式映射 schema 到方法。

#
★★★

5. GraphQL 查询的执行流程,parse/validate/execute 三阶段,字段解析器如何由执行策略逐层驱动取值

GraphQL 查询的执行流程:parse/validate/execute 三阶段,字段解析器如何由执行策略逐层驱动取值?

  • parse/validate/execute
  • 执行策略
  • 字段解析

GraphQL 查询执行流程三阶段:parse(把查询字符串解析为 AST/文档)、validate(用 schema 校验查询合法性,如字段存在、类型匹配、参数有效)、execute(按 schema 执行查询顶层字段,逐层解析)。execute 阶段用执行策略(ExecutionStrategy)驱动:顶层字段解析器(resolver)返回结果,其子字段再由子字段解析器逐层解析,直到叶子字段。执行策略决定并行/串行(如 serial 串行、async 并行、可配置)。字段解析器逐层驱动取值:Query 根字段 → 嵌套对象字段 → 标量叶子,形成结果树。三阶段保证查询正确性与执行。

parse 解析、validate 校验、execute 执行。execute 用执行策略逐层驱动字段解析器构建结果树。三阶段是 GraphQL 执行核心。

#
★★

6. GraphQL 的查询成本控制,查询深度限制、复杂度分析与持久化查询(persisted query)

GraphQL 的查询成本控制:查询深度限制、复杂度分析与持久化查询(persisted query)是什么?

  • 深度限制
  • 复杂度分析
  • 持久化查询

GraphQL 查询成本控制:深度限制(限制查询嵌套深度,防止深嵌套耗尽资源);复杂度分析(分析查询的成本,如字段数、列表大小、联合开销,限制复杂度超过阈值的查询);持久化查询(persisted query,客户端先注册查询,之后用 ID 引用,只允许执行已注册的查询,防止任意查询)。这些防止恶意/过深查询拖垮服务端。深度限制简单,复杂度分析更精确,持久化查询最严格(白名单)。工程上组合使用:持久化查询 + 复杂度/深度限制兜底。成本控制是 GraphQL 安全与性能关键。

深度限制、复杂度分析、持久化查询从不同层面限制查询成本,防过深/恶意查询。持久化查询白名单最严格。

#
★★

7. GraphQL 的缓存策略,HTTP 缓存为何失效、CDN/边缘缓存的替代方案

GraphQL 的缓存策略:HTTP 缓存为何失效?CDN/边缘缓存的替代方案是什么?

  • HTTP 缓存失效
  • CDN/边缘缓存
  • 替代方案

GraphQL 的 HTTP 缓存失效原因:GraphQL 用单一 POST /graphql 端点,POST 响应默认不可缓存;客户端查询多变,字段级差异使缓存键难以复用;缺少资源级 URL 与 ETag 语义。因此传统 HTTP 缓存/CDN 对 GraphQL 失效。替代方案:1) 持久化查询 + GET(用 GET 查询,可缓存);2) 持久化查询(persisted query)使查询可缓存(相同查询 ID);3) 响应级缓存(服务端缓存 DataLoader 结果、字段级缓存);4) 边缘缓存(把持久化查询响应缓存到 CDN/边缘);5) 缓存持久化(APQ - Automatic Persisted Queries)。核心是"查询可标识化(持久化)才能缓存"。CDN/边缘缓存结合持久化查询实现。

GraphQL POST 多变难缓存,持久化查询 + GET 使查询可缓存,配合边缘缓存。替代传统 HTTP 缓存。

#
★★

8. GraphQL 的错误模型,errors 数组、扩展字段(extensions)与部分成功语义

GraphQL 的错误模型:errors 数组、扩展字段(extensions)与部分成功语义是什么?

  • errors 数组
  • extensions
  • 部分成功

GraphQL 错误模型:执行失败时返回 errors 数组(每个错误含 message、locations、path),data 中可能含部分成功的结果。部分成功语义:GraphQL 允许部分字段成功、部分失败,已成功字段返回 data,失败字段在 errors 中记录(对应 path),不整体失败。扩展字段(extensions):错误或 data 可带 extensions 扩展信息(如错误码、trace-id、自定义元数据),用于结构化诊断。错误模型的价值:部分成功 + 结构化错误,客户端可处理部分结果并定位失败字段。不同于 REST 的整体状态码,GraphQL 用 errors 表达字段级错误。

GraphQL 用 errors 数组表达字段级错误,支持部分成功(data 保留成功字段),extensions 携带错误码等扩展。错误模型结构化。

#
★★

9. GraphQL 订阅(Subscription)与 WebSocket 传输,实时字段推送

GraphQL 订阅(Subscription)与 WebSocket 传输:实时字段推送如何实现?

  • Subscription
  • WebSocket 传输
  • 实时推送

GraphQL 订阅(Subscription)用于实时推送:客户端用 Subscription 订阅某事件,服务端在事件发生时推送数据。传输常用 WebSocket:客户端经 WebSocket 发送订阅请求,服务端建立订阅,事件触发时通过该 WebSocket 推送数据。Spring for GraphQL 支持 Subscription 与 WebSocket 传输(graphql-ws 协议)。实现:Subscription 字段解析器返回 Publisher(响应流),事件经 Publisher 推送;WebSocket 承载订阅会话。实时推送用于消息、通知、实时数据。需处理连接生命周期、心跳、重连。订阅是 GraphQL 的实时通道。

Subscription 用 WebSocket 传输,字段解析器返回 Publisher,事件实时推送。是 GraphQL 的实时推送通道。

#
★★

10. GraphQL 联邦(Federation)与网关,子图(subgraph)拆分与 Java 生态支持

GraphQL 联邦(Federation)与网关:子图(subgraph)拆分与 Java 生态支持是什么?

  • Federation 联邦
  • 子图拆分
  • Java 生态

GraphQL 联邦(Federation)把多个服务(子图 subgraph)组合为一个统一 GraphQL schema,通过网关(如 Apollo Router)聚合。子图拆分:每个服务定义自己的 schema(子图),通过 @key 等指令声明跨实体引用,网关把各子图合并为统一 schema 并路由查询到相应子图。价值:多服务共享一个 GraphQL 入口,避免单体 schema。Java 生态支持:Apollo 的 federation-jvm、Spring 生态(com.apollographql.federation 的 Spring 支持)实现子图;需要网关(Apollo Router、GraphQL Mesh)。联邦解决"多服务组合 GraphQL"的规模化问题。

Federation 用子图拆分 + 网关聚合统一 schema,@key 声明跨实体引用。Java 生态有 federation-jvm 支持子图。

#
★★

11. GraphQL 的输入类型与参数校验,服务端校验与查询变量

GraphQL 的输入类型与参数校验:服务端校验与查询变量是什么?

  • 输入类型
  • 参数校验
  • 查询变量

GraphQL 的输入类型(input type)用于 Mutation 的输入参数(结构化输入),查询变量(variables)把参数从查询中分离,客户端传变量、服务端按 schema 校验。服务端校验:schema 的类型系统(非空、类型、枚举)提供基础校验;字段解析器内可做业务校验(如 Bean Validation @Valid)。查询变量提高安全性(避免注入)与复用(变查询)。价值:输入类型定义结构化输入,查询变量分离参数,schema + 业务校验保证参数正确。是 GraphQL 参数处理与校验方式。

input 类型定义输入结构,查询变量分离参数,schema 类型 + 业务校验保证参数正确。参数处理与校验。

#
★★

12. GraphQL 与 Spring Security,字段级授权与 DataFetcher 中的安全上下文

GraphQL 与 Spring Security:字段级授权与 DataFetcher 中的安全上下文是什么?

  • 字段级授权
  • 安全上下文
  • DataFetcher

GraphQL 与 Spring Security 集成:认证(@AuthenticationPrincipal 获取当前用户)与授权(@PreAuthorize 方法级、字段级授权)。字段级授权:对敏感字段(如手机号、邮箱)用授权注解限制返回,未授权用户该字段返回 null/错误。DataFetcher 中的安全上下文:字段解析器(DataFetcher)中通过 SecurityContextHolder 获取当前用户安全上下文,做字段级判断(如只返回自己的数据)。字段级授权是 GraphQL 安全关键(不同用户看到不同字段)。集成验证:认证在入口,字段级授权在解析器,保证字段权限。

认证 + 字段级授权(@PreAuthorize/安全上下文判断)控制字段可见性。DataFetcher 中读取安全上下文做字段级权限。

#
★★

13. GraphQL 的性能陷阱,字段解析串行化、数据获取合并与批处理(DataLoader)

GraphQL 的性能陷阱:字段解析串行化、数据获取合并与批处理(DataLoader)是什么?

  • 字段解析串行化
  • 数据获取合并
  • DataLoader 批处理

GraphQL 性能陷阱:1) 字段解析串行化(默认执行策略串行解析字段,慢字段拖慢整个查询,可并行化);2) 数据获取合并(同一数据被多次获取,需合并/去重);3) N+1 查询(逐行解析触发逐条查询)。解决:DataLoader 批处理(把同一字段多次解析合并为一次批量查询并缓存,消除 N+1 与重复获取);并行执行策略(异步并行解析字段);批处理合并数据获取。性能陷阱核心是"字段级解析导致 N+1 与串行化",DataLoader + 并行是优化关键。

字段解析串行化与 N+1 是 GraphQL 性能陷阱。DataLoader 批处理合并分查询、并行执行策略提升,优化性能。

#
★★

14. GraphQL 的版本演进策略,字段废弃(deprecation)与 schema 演进代替版本号

GraphQL 的版本演进策略:字段废弃(deprecation)与 schema 演进代替版本号是什么?

  • deprecation
  • schema 演进
  • 版本策略

GraphQL 的版本演进策略:用字段废弃(@deprecated)标记废弃字段,客户端仍可用但不再推荐,逐步移除;用 schema 演进(新增字段、废弃旧字段)代替版本号(不像 REST 的 /v1、/v2)。好处:schema 只增不改,客户端按需升级,无需多版本共存;新增字段兼容旧客户端,废弃字段标记迁移。策略:schema 演进是"兼容性优先的增量演进",通过非破坏性变更(新增字段、废弃字段、可选参数)演进,避免破坏性变更。相比 REST 版本号,GraphQL 用"演进 + 废弃"实现平滑演进。

GraphQL 用 @deprecated 废弃字段 + schema 增量演进代替版本号,兼容性优先,避免多版本共存。

#
★★

15. Spring for GraphQL 的异常处理,@GraphQlExceptionHandler 与 ErrorType 分类、错误扩展字段如何返回客户端

Spring for GraphQL 的异常处理:@GraphQlExceptionHandler 与 ErrorType 分类、错误扩展字段如何返回客户端?

  • @GraphQlExceptionHandler
  • ErrorType
  • 错误扩展

Spring for GraphQL 的异常处理:用 @GraphQlExceptionHandler 方法处理特定异常,返回 GraphQLError(含 message、ErrorType、extensions)。ErrorType 分类:GraphQLErrorType 枚举(如 BAD_REQUEST、INTERNAL_ERROR、FORBIDDEN、NOT_FOUND)分类错误。错误扩展字段:在 GraphQLError 的 extensions 中携带自定义字段(错误码、trace-id、详情),返回客户端。@GraphQlExceptionHandler 把业务异常映射为结构化的 GraphQL 错误(分类 + 扩展)。价值:统一异常处理、错误分类、扩展信息,客户端可解析。

@GraphQlExceptionHandler 把异常映射为 GraphQLError,ErrorType 分类,extensions 携带错误码等。异常结构化处理。

#
★★

16. GraphQL 的 null 传播,子字段返回 null 时父字段非空约束如何导致整条路径失败,schema 设计如何权衡

GraphQL 的 null 传播:子字段返回 null 时父字段非空约束如何导致整条路径失败?schema 设计如何权衡?

  • null 传播
  • 非空约束
  • schema 权衡

GraphQL 的 null 传播:若子字段返回 null,且其父字段声明为非空(!),则父字段整体变为 null(因为非空字段不能为 null),null 向上传播,导致整条路径失败(字段变为 null 或整条查询失败)。即"非空字段的 null 子值向上传播"。schema 设计权衡:非空约束(!)保证字段必有值,但子字段 null 会传播使父字段失败;过度使用非空增加脆弱性(一个子字段 null 拖垮整条路径)。权衡:对关键字段用非空,但对可能缺失/可空的字段用可空,避免 null 传播放大。设计时考虑"非空 vs 可空"的传播影响。

非空字段若子值 null 会向上传播使整条路径失败。schema 权衡非空(强保证)与可空(防传播),避免过度非空。

#

17. GraphQL 的分页规范,Connection 的 edges/node/pageInfo 与游标分页

GraphQL 的分页规范:Connection 的 edges/node/pageInfo 与游标分页是什么?

  • Connection 规范
  • edges/node/pageInfo
  • 游标分页

GraphQL 分页规范(Relay Connection):Connection 类型包含 edges(边数组,每边含 node 与 cursor)、pageInfo(分页信息:hasNextPage、hasPreviousPage、startCursor、endCursor)、totalCount。用游标分页:分页参数用 first/after(向后,after 游标)或 last/before(向前),cursor 是编码的游标(基于排序键/ID),定位下一页/上一页。相比 offset 分页,游标分页稳定(数据增删不位移)、高效(索引定位)。Connection 规范让分页在客户端通用化。工程上用 Connection 实现游标分页,避免深 offset。

Connection 用 edges/node/pageInfo + 游标分页(first/after),稳定高效。是 GraphQL 分页标准规范。

#

18. GraphQL 与 OpenAPI 的共存,网关层协议转换与双协议暴露

GraphQL 与 OpenAPI 的共存:网关层协议转换与双协议暴露是什么?

  • GraphQL 与 OpenAPI 共存
  • 协议转换
  • 双协议暴露

GraphQL 与 OpenAPI 共存:服务可同时暴露 GraphQL 与 REST(OpenAPI)接口,通过网关层协议转换或在服务内双协议暴露。网关层协议转换:网关把 GraphQL 查询转换为对 REST 后端的调用(如 GraphQL Mesh、schema stitching、Apollo 插件),或把 REST 调用转换为 GraphQL;服务内双协议:同一业务逻辑同时提供 GraphQL 端点和 REST 端点,共享服务层。共存满足不同客户端需求(GraphQL 灵活、REST 标准可缓存)。协议转换需处理 schema 映射、错误模型、性能。共存是"多种接口形态满足多样客户端"。

GraphQL 与 OpenAPI 共存用网关协议转换或双协议暴露,共享服务层。满足不同客户端需求,需处理映射。

#

19. GraphQL 的工具链,GraphiQL、graphql-java runtime wiring 与代码生成

GraphQL 的工具链:GraphiQL、graphql-java runtime wiring 与代码生成是什么?

  • GraphiQL
  • graphql-java runtime wiring
  • 代码生成

GraphQL 工具链:GraphiQL(浏览器内交互式查询 IDE,可探索 schema、执行查询、调试);graphql-java runtime wiring(graphql-java 的运行时配置,用 RuntimeWiring 把 schema 类型字段绑定到 DataFetcher,即"wiring"运行时连接);代码生成(从 schema 生成 Java 类型/客户端代码,如 graphql-codegen,提升类型安全)。Spring for GraphQL 简化了 wiring(注解自动映射)。工具链支撑开发(GraphiQL 调试)、实现(wiring 绑定)、类型安全(代码生成)。价值是完整的开发与运行工具链。

GraphiQL 调试、graphql-java runtime wiring 绑定 DataFetcher、代码生成提类型安全。构成完整工具链。

#

20. 接口与联合类型的运行时解析,typeResolver 如何根据实际对象类型选择具体类型,Java 侧如何注册

接口与联合类型的运行时解析:typeResolver 如何根据实际对象类型选择具体类型?Java 侧如何注册?

  • typeResolver
  • 接口/联合类型
  • Java 注册

GraphQL 接口(interface)与联合类型(union)在运行时需确定具体类型:typeResolver 根据实际返回的对象类型选择对应的具体类型(如返回 User 对象,typeResolver 判断其是 User 类型)。typeResolver 的 getType 方法接收对象与上下文,返回 TypeResolutionEnvironment 中的具体 ObjectType。Java 侧注册:在 RuntimeWiring 中为接口/联合类型注册 typeResolver(typeWiring("Node").typeResolver(env -> ...)),或用 Spring for GraphQL 的自动注册(按返回类型推断)。typeResolver 是运行时多态解析的关键,让接口/联合字段返回具体类型。

typeResolver 根据实际对象类型选择具体类型,Java 侧在 RuntimeWiring 注册 typeResolver 或自动推断。实现运行时多态。