真正的原因是"SpringDoc 的坑"
⚠️ 结论
SpringDoc 在"静态工厂方法 + 嵌套泛型返回值"场景下,
有时无法正确推断T的真实类型
也就是说:
java
public R<List<FriendVO>> listFriends() {
return R.ok(friends);
}
Java 编译期是没问题的
运行期业务也是完全 OK 的
❌ 但 SpringDoc 在生成 OpenAPI Schema 时,推断失败
所以它只生成了:
R
└─ data: object
而 不会展开 FriendVO
为什么会这样?
-
Java 泛型 运行期会擦除
-
SpringDoc 主要靠:
- 方法返回签名(返回类型)
- 注解
- 反射
-
静态泛型工厂方法(
R.ok(T data))是关键触发点
bash
Java 中完整方法签名 = 方法名 + 参数列表(类型、数量、顺序)
而方法返回签名专指方法的返回值类型(注意:仅包含类型,不包含返回值名称 / 变量)
SpringDoc 有时候能推断
有时候推断不了(尤其是 R<List<VO>> 这种双层泛型)
👉 这是 SpringDoc 2.x 的已知行为
✅ 解决方案一(最推荐 )
👉 在 Controller 方法上 显式声明 Response Schema
java
@Operation(
summary = "查询我的好友列表",
description = "传入当前登录用户ID,查询已成功建立好友关系的全部好友列表"
)
@ApiResponse(
responseCode = "200",
description = "成功",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = FriendVO.class)
)
)
@GetMapping("/list")
public R<List<FriendVO>> listFriends(
@NotNull @Min(1) @RequestParam Long userid) {
return R.ok(friendService.getFriendList(userid));
}
🔥 这一行是"核按钮":
java
@Schema(implementation = FriendVO.class)
📌 含义:
"别猜了,我明确告诉你 data 里装的是 FriendVO"
✅ 解决方案二(更优雅,适合多个接口)
给 R.data 加一个 @Schema 提示
你已经写了:
java
@Schema(description = "业务数据载体,成功时返回具体业务数据,失败时为null,泛型适配任意类型数据")
private T data;
👉 但 SpringDoc 仍然不知道 T 是什么
可以加一个 泛型兜底(实战常用):
java
@Schema(
description = "业务数据",
oneOf = { FriendVO.class }
)
private T data;
⚠️ 不过这个方式 适合单一业务域
如果 R 是全局通用,方案一更干净
✅ 解决方案三(调试 / 验证用,非最终)
加一个"直返 VO 的接口":
java
@GetMapping("/_debug/friend-vo")
public FriendVO debugFriendVO() {
return new FriendVO();
}
如果这个 能在 Schemas 里看到 FriendVO
👉 那就 100% 证明不是 VO / Lombok / 注解问题