# java-flexdto **Repository Path**: ssuiwp/java-flexdto ## Basic Information - **Project Name**: java-flexdto - **Description**: java type check map - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-11-03 - **Last Updated**: 2026-11-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # FlexDTO: Java Web DTO 爆炸治理与编译期契约推导系统 [](https://openjdk.org/) [](LICENSE) [](https://maven.apache.org/) 在复杂的企业级 Java Web 架构中,随着微服务调用与跨层传递字段的增多,系统往往面临 **DTO 爆炸(成百上千个冗余的 `XxxReq`/`XxxBo`/`XxxVo`)** 与 **方法签名爆炸(`
`)** 的两难困境。
**FlexDTO** 结合了运行时强类型双层存储容器与基于 `javac` AST 静态分析的**编译期调用链契约自动推导(Call-Graph Contract Inference)**,彻底打破 Java 名义类型系统的“不可能三角”:
1. **不写多余 DTO 类**:无需定义成百上千个结构高度重复的入参/领域对象。
2. **方法签名永远固定简短**:无论内部流转 5 个还是 50 个字段,方法入参永远固定为 `(FlexDto dto)`。
3. **编译期严格安全感知**:通过 `JavacPlugin` 静态分析全调用链路,一旦下游读取了上游未声明/未放入的字段,**在编译期即刻以精准的错误信息阻断构建**。
---
## 目录结构
```text
java-flexdto/
├── flexdto-core/ # 运行时核心模块
│ ├── SFunction.java # 强类型方法引用函数式接口
│ ├── LambdaMeta.java # Lambda 元数据提取容器
│ ├── LambdaUtils.java # 高性能反射解析 SerializedLambda 缓存
│ ├── FlexDto.java # 双层存储容器 (Entity Slot + Flex Fields)
│ ├── FlexDtoSerializer.java # Jackson 序列化支持
│ └── FlexDtoDeserializer.java# Jackson 反序列化支持
├── flexdto-compiler-plugin/ # 编译期 Javac 插件 (-Xplugin:FlexContractCheck)
│ ├── FlexContractPlugin.java # JavacPlugin 入口与 TaskListener 注册
│ ├── FlexAstScanner.java # AST 扫描器 (捕捉 get/set/contract/pick 与调用链路)
│ ├── ContractAnalyzer.java # 不动点迭代算法 (推导传递 Required 集并阻断契约缺失)
│ ├── FieldRef.java # 字段引用归一化对象 (Order::getId, OrderVocab::riskScore)
│ └── CallSite.java # 调用点快照与字段生产快照
├── flexdto-idea-plugin/ # IntelliJ IDEA 专属插件 (实时红字标错 + Alt+Enter 修复)
│ ├── FlexContractInspection.java # PSI 实时语法树检查器 (继承 LocalInspectionTool)
│ ├── AddContractFieldQuickFix.java # Alt + Enter 快速修复动作 (实现 LocalQuickFix)
│ ├── plugin.xml # 插件清单与扩展点注册
│ ├── build-plugin.sh # 一键编译与热部署脚本 (Bash)
│ └── build-plugin.bat # 一键编译与热部署脚本 (Windows CMD)
└── flexdto-sample/ # 三层架构完整样例与集成测试
├── model/Order.java # 完整实体对象 (挂载入 Entity Slot)
├── model/OrderVocab.java # 散装非实体字段词汇表 (OrderVocab 接口)
├── manager/OrderManager.java # 底层 Manager (消费 riskScore)
├── service/OrderService.java # 中间业务层 Service (透传 dto 并计算 reviewTime)
├── controller/ReviewController.java # 顶层入口 (contract 契约声明与调用)
└── ReviewControllerTest.java # 运行期端到端验证测试
```
---
## 核心设计与使用范式
### 1. 双层存储设计(降噪防拆碎)
如果流程涉及 30 个属于同一个 `Order` 的固有字段,无需拆解为 30 个散装字段:
* **强类型实体槽 (Entity Slot)**:整存整取已有的 Entity,`dto.putEntity(order)` / `dto.getEntity(Order.class)`。
* **散装动态字段 (Vocab Field)**:使用轻量级接口声明方法签名(例如 `OrderVocab`),通过方法引用 `dto.get(OrderVocab::riskScore)` 与 `dto.set(OrderVocab::reviewTime, ...)` 强类型读写。
```java
// 词汇表无需实现类,仅提供强类型方法引用
public interface OrderVocab {
Integer riskScore();
Boolean canApprove();
LocalDateTime reviewTime();
String auditReason();
}
```
### 2. 业务三层架构书写规范
```java
// 1. 最底层 Manager(第 3 层):读取实体固有金额与散装风控得分
public class OrderManager {
public void verifyRisk(FlexDto dto) {
BigDecimal price = dto.get(Order::getTotalPrice);
Integer score = dto.get(OrderVocab::riskScore); // <-- 新增读取字段,无需改任何方法签名!
if (price.compareTo(new BigDecimal("1000")) > 0 && score != null && score > 80) {
dto.set(OrderVocab::auditReason, "高价值订单且风控评分偏高,触发人工复审");
}
}
}
// 2. 中间层 Service(第 2 层):只管自己需要的字段,中途产生新字段并继续透传
public class OrderService {
private final OrderManager orderManager = new OrderManager();
public void executeReview(FlexDto dto) {
Long id = dto.get(Order::getId);
Boolean canApprove = dto.get(OrderVocab::canApprove);
dto.set(OrderVocab::reviewTime, LocalDateTime.now());
orderManager.verifyRisk(dto); // 直接透传,无需改方法入参
}
}
// 3. 顶层 Controller(第 1 层):声明前端契约
public class ReviewController {
private final OrderService reviewService = new OrderService();
@PostMapping("/review")
public FlexDto submitReview(@RequestBody FlexDto req) {
// 声明前端入参提供的字段集
req.contract(Order::getId, Order::getTotalPrice, OrderVocab::canApprove, OrderVocab::riskScore);
reviewService.executeReview(req);
return req;
}
}
```
---
## 编译期静态分析与精准拦截
在编译阶段,`FlexContractPlugin` 介入 `javac` 的 AST 分析,通过正向可用字段分析(Forward Available Fields)与逆向所需字段推导(Backward Required Fields)相结合,提供两大核心编译期检查:
### 维度 1:方法内直接读取未赋值/未声明字段拦截(Intra-Method Get-Before-Set)
对于本地新实例化的 `FlexDto`(`new FlexDto()`)或顶层入口参数(`@RequestBody FlexDto req`),在尚未通过 `set(...)`、`putEntity(...)`、`contract(...)` 赋值或声明之前直接调用 `get(...)`:
```java
FlexDto req = new FlexDto()
.putEntity(order)
.set(OrderVocab::canApprove, true);
// 试图读取未赋值过的 reviewTime:
LocalDateTime time = req.get(OrderVocab::reviewTime);
```
**编译器直接拦截并报错**:
```plaintext
[ERROR] ReviewControllerTest.java:[26] 契约缺失: 变量 req 尝试获取未赋值/未声明的字段 [OrderVocab::reviewTime]
└── 消费位置: ReviewControllerTest.testSubmitReviewSuccess(ReviewControllerTest.java:26)
└── 原因: 该字段从未被 set(...) 赋值,读取必然返回 null
```
### 维度 2:跨方法调用链契约自动推导与拦截(Inter-Method Call-Graph Contract)
1. 提取每个方法的产出集 $\text{Produced}(m)$(`contract`、`set`、`pick`、`putEntity`)与消费集 $\text{SelfGet}(m)$(`dto.get(...)`)。
2. 通过**不动点迭代(Fixpoint Worklist)算法**沿调用树逆向推导传递依赖:
$$\text{Required}(m) = \text{SelfGet}(m) \cup \bigcup_{c \in \text{Callees}(m)} \big(\text{Required}(c) \setminus \text{ProducedBeforeCall}(m, c)\big)$$
3. 当上游入口未提供下游必需字段时,**直接中断编译并输出精准的定位与调用链路**:
```plaintext
[ERROR] ReviewController.java:[15] 契约缺失: 调用 executeReview(req) 缺少必需字段 [OrderVocab::riskScore]
└── 消费位置: OrderManager.verifyRisk(OrderManager.java:8)
└── 调用链路: submitReview -> executeReview -> verifyRisk
```
---
## 构建工具与 IDE 集成
### 1. Maven 集成 (`pom.xml`)
在业务工程中引入 `flexdto-core` 并在 `maven-compiler-plugin` 中配置编译参数:
```xml