断言 + 异常机制 = 清晰简洁的代码
业务代码的"噪音"
如果让一个开发者回顾自己写的业务代码,他会发现一个令人沮丧的事实:真正处理业务的代码可能只占 30%,剩下的 70% 都是各种"防御性代码"。
// 这种代码你一定写过
public Result buyItem(long userId, int itemId, int count) {
if (userId <= 0) {
return Result.fail(1001, "用户ID无效");
}
if (itemId <= 0) {
return Result.fail(1002, "商品ID无效");
}
if (count <= 0 || count > 99) {
return Result.fail(1003, "购买数量无效");
}
User user = userService.getUser(userId);
if (user == null) {
return Result.fail(1004, "用户不存在");
}
if (user.isBanned()) {
return Result.fail(1005, "用户已被封禁");
}
Item item = itemService.getItem(itemId);
if (item == null) {
return Result.fail(1006, "商品不存在");
}
if (!item.isOnSale()) {
return Result.fail(1007, "商品已下架");
}
if (user.getGold() < item.getPrice() * count) {
return Result.fail(1008, "金币不足");
}
// === 终于到业务逻辑了 ===
user.setGold(user.getGold() - item.getPrice() * count);
user.addItem(itemId, count);
return Result.success("购买成功");
}
20 行防御性代码,3 行业务逻辑。这不是代码,这是防弹衣。
ionet 的解法:断言 + 异常
在 ionet 中,同样的业务用断言写:
@ActionMethod(BuyCmd.buyItem)
private void buyItem(FlowContext flowContext, BuyMessage message) {
// 断言:直接抛出错误码,框架自动返回给客户端
ErrorCode.userNotFound.assertTrue(user != null);
ErrorCode.userBanned.assertFalse(user.isBanned());
ErrorCode.itemNotFound.assertTrue(item != null);
ErrorCode.itemOffSale.assertTrue(item.isOnSale());
ErrorCode.goldNotEnough.assertTrue(user.getGold() >= item.getPrice() * message.count);
// 业务逻辑
user.setGold(user.getGold() - item.getPrice() * message.count);
user.addItem(message.itemId, message.count);
}
关键差异:
- 没有 if-else:断言失败自动抛出异常
- 没有 Result 封装:框架自动将错误码返回给客户端
- 方法签名清晰:return 就是正常的业务数据,不需要
Result<T>包装 - 可以直接 return void:如果不需要返回数据给客户端
ErrorCode 枚举:集中管理错误码
public enum ErrorCode implements ErrorInformation {
// 用户相关
userNotFound(1001, "用户不存在"),
userBanned(1002, "用户已被封禁"),
// 商品相关
itemNotFound(2001, "商品不存在"),
itemOffSale(2002, "商品已下架"),
// 资源相关
goldNotEnough(3001, "金币不足"),
diamondNotEnough(3002, "钻石不足");
final int code;
final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
@Override
public String getMessage() { return this.message; }
@Override
public int getCode() { return this.code; }
}
所有错误码集中在一个枚举中,一目了然。不会出现"这个错误码 1008 是在哪个方法里定义的?"的困惑。
异常的传播路径
当断言失败时,内部流程是这样的:
断言失败 → 抛出业务异常(携带 ErrorCode)
→ 业务框架捕获异常
→ 将 ErrorCode 放入 ResponseMessage.responseStatus
→ 通过对外服协议返回给客户端
→ 客户端收到 { responseStatus: 1001, message: "用户不存在" }
你不需要做任何额外的事情。 不需要写异常处理器,不需要全局拦截器——框架已经内置了完整的异常传播链路。
assertTrue vs assertFalse
ionet 的 ErrorCode 提供了两个断言方法:
// 条件为 true 时通过,为 false 时抛出异常
ErrorCode.goldNotEnough.assertTrue(user.getGold() >= price);
// 条件为 false 时通过,为 true 时抛出异常
ErrorCode.userBanned.assertFalse(user.isBanned());
选哪个取决于语义的自然性。比如:
- "金币不足" →
assertTrue(金币 >= 价格)更自然 - "用户被封禁" →
assertFalse(被封禁)更自然
嵌套调用中的异常
断言机制不仅在 Action 方法中有效,在 Action 调用的任何方法中都有效:
@ActionMethod(0)
private UserMessage login(LoginMessage message) {
// 这个方法内部也可以抛出断言异常
User user = userService.validateAndGetUser(message.jwt);
return toUserMessage(user);
}
// 在 Service 中
public User validateAndGetUser(String jwt) {
ErrorCode.jwtInvalid.assertTrue(jwt != null && !jwt.isEmpty());
User user = findByJwt(jwt);
ErrorCode.userNotFound.assertTrue(user != null);
return user;
}
无论异常从哪一层抛出,框架都能正确捕获并返回给客户端。
对比传统方案
| 维度 | 传统 if-else | ionet 断言 |
|---|---|---|
| 代码量 | ~20 行防御代码 | ~5 行断言 |
| 可读性 | 业务逻辑被淹没 | 业务逻辑突出 |
| 错误码管理 | 散落在各方法中 | 枚举集中管理 |
| 返回值封装 | 需要 Result<T> |
直接 return 业务数据 |
| 异常传播 | 需手动处理 | 框架自动处理 |
| 客户端收到 | 可能不规范 | 总是规范的错误码 |
小结
ionet 的断言 + 异常机制,让你的业务代码回归业务本身。
- 断言失败 = 自动返回错误码给客户端,不需要手动处理
- 错误码枚举管理,不会散落在业务代码中
- 方法签名纯粹,return 就是正常数据,不需要 Result 包装
这个机制是 ionet "零学习成本"的重要组成部分——让你不需要学习复杂的异常处理链路,也能写出健壮的业务代码。
更多资源
下一篇预告:[告别 if-else 地狱 —— JSR380 参数验证在 ionet 中的应用]
浙公网安备 33010602011771号