第九课实战版:异常与日志体系 —— 后端稳定性的第一道防线

第9课的目标,从来不是教你"写几个异常类"。

而是:在你的后端工程中,亲手搭建一套

👉 可扩展、可定位、可治理的稳定性底座。

从这一课开始,你写的不再只是功能代码,

而是在搭一套系统工程能力

如果用 Android 来类比,这一课本质上是在后端项目中建立:

  • BaseResponse
  • 全局异常处理
  • 拦截器 / Filter
  • 统一日志与 requestId

也就是:后端的 base / core 模块(Infrastructure)

一、第9课在整套 24 课体系中的真实位置

在前 8 课,你已经完成了:

  • 分层架构(interfaces / application / domain / infrastructure)
  • ORM、事务、Service 编排

但到目前为止,你的系统还不具备"工程稳定性"

👉 第9课的角色是:
给整个系统安装"神经系统 + 黑匣子 + 保险丝"。

它直接支撑后续:

  • 第10课 Controller 规范
  • 第11课 接口质量工程
  • 第12课 登录与身份系统

所以,第9课不在业务层,而在:

Infrastructure ------ 基础设施层 / 工程底座

二、Infrastructure 是什么?(Android 视角最容易懂)

Infrastructure = 业务无关 + 全局复用 + 系统级能力

就像 Android 里的 base 模块:

  • BaseActivity / BaseResponse
  • 网络拦截器
  • 全局异常
  • 日志、埋点、trace

在后端里,它对应的就是:

  • 统一返回体 Result
  • 错误码体系 ErrorCode
  • 业务异常 BizException
  • 全局异常处理器
  • requestId / traceId
  • 日志规范 / Filter / AOP

👉 它不解决"业务",

👉 它决定"系统怎么活"。

三、第9课实战目标(必须形成的工程闭环)

完成这一课,你的工程中必须出现:

  • ✅ infrastructure/common 基础模块
  • ✅ 统一异常体系(业务失败 + 系统事故)
  • ✅ 统一返回体
  • ✅ requestId 全链路
  • ✅ 日志分层规范
  • ✅ 线上问题可反向定位能力

这是后续所有系统能力的地基。

四、工程落位(基础设施层包结构)

复制代码
infrastructure
└── common
    ├── result        // 统一返回体(BaseResponse)
    ├── error         // 错误码体系
    ├── exception     // BizException / GlobalHandler
    ├── web           // Filter / Interceptor
    ├── log           // 日志规范 / AOP(扩展)
    └── util

👉 这一整块,就是你项目的"base 模块"。

五、异常体系骨架(系统的失败模型)

1️⃣ 统一返回体 Result

java 复制代码
public class Result<T> {
    private boolean success;
    private String code;
    private String message;
    private T data;
    private String requestId;
}

👉 所有接口输出统一,所有失败工程化。

2️⃣ 错误码体系 ErrorCode

java 复制代码
public interface ErrorCode {
    String code();
    String message();
}
java 复制代码
public enum UserErrorCode implements ErrorCode {
    USER_NOT_FOUND("U_404", "用户不存在"),
    PHONE_EXISTS("U_409", "手机号已注册");
}
java 复制代码
public enum SystemErrorCode implements ErrorCode {
    SYSTEM_ERROR("SYS_500", "系统繁忙"),
    UNAUTHORIZED("SYS_401", "未登录"),
    FORBIDDEN("SYS_403", "无权限");
}

👉 错误不是字符串,是系统语义。

3️⃣ 业务异常 BizException

java 复制代码
public class BizException extends RuntimeException {
    private final String code;

    public BizException(ErrorCode errorCode) {
        super(errorCode.message());
        this.code = errorCode.code();
    }

    public String getCode() {
        return code;
    }
}

使用:

java 复制代码
if (!userExists) {
    throw new BizException(UserErrorCode.USER_NOT_FOUND);
}

👉 业务层只描述"失败",不处理失败。

4️⃣ 全局异常出口 GlobalExceptionHandler

java 复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BizException.class)
    public Result<Void> handleBiz(BizException e) {
        log.warn("biz error: {}", e.getMessage());
        return Result.fail(e.getCode(), e.getMessage(), RequestId.get());
    }

    @ExceptionHandler(Exception.class)
    public Result<Void> handleSystem(Exception e) {
        log.error("system error", e);
        return Result.fail("SYS_500", "系统繁忙", RequestId.get());
    }
}

👉 所有异常,在这里结束。

六、日志体系骨架(系统的黑匣子)

1️⃣ requestId(每个请求的"案号")

java 复制代码
public class RequestIdFilter implements Filter {
    public void doFilter(...) {
        String requestId = UUID.randomUUID().toString();
        MDC.put("requestId", requestId);
        chain.doFilter(request, response);
        MDC.clear();
    }
}

logback:

java 复制代码
requestId=%X{requestId}

👉 所有日志自动带 requestId。

2️⃣ 日志分层原则

  • Controller:接口入口 / 参数 / 耗时
  • Application:业务流程 / 编排
  • Domain:关键业务决策
  • Infrastructure:IO / DB / MQ
  • ExceptionHandler:事故日志

错误日志必须能回答:

👉 谁 → 干了什么 → 为什么失败 → 关键数据

七、最小工程闭环示例

Controller:

java 复制代码
@PostMapping("/users")
public Result<UserDTO> register(@RequestBody RegisterReq req) {
    return Result.ok(service.register(req), RequestId.get());
}

Domain / Application:

java 复制代码
if (repo.exists(phone)) {
    throw new BizException(UserErrorCode.PHONE_EXISTS);
}

异常 → GlobalHandler → Result → requestId → 日志定位

👉 至此,第9课工程闭环成立。

八、第9课完成标准(极其重要)

当你满足下面 6 条,你这一课才算真正完成:

  • ✅ infrastructure/common 独立存在
  • ✅ 项目中几乎没有到处 try-catch
  • ✅ 所有业务失败统一 BizException
  • ✅ 所有未知失败统一 system error
  • ✅ 所有日志带 requestId
  • ✅ 报错可以反向还原一次请求

九、第9课对你能力层级的意义

从这一课开始,你已经不再是:

❌ 写接口的人

而是在做:

✅ 系统稳定机制设计

✅ 工程底座搭建

✅ 事故可治理能力

你已经站在架构能力起点了。

十、总结

真正稳定的系统,不是从不出错,

而是出错时,依然可控、可查、可修。

第9课搭的不是异常类,

是一套系统的"生命线"。

相关推荐
Rust研习社21 小时前
组合真的优于继承吗?为什么 Rust 和 Go 都拥抱组合舍弃继承?
后端·rust·编程语言
IT_陈寒1 天前
JavaScript的闭包把我坑惨了,说好的内存会自动回收呢?
前端·人工智能·后端
CaffeinePro1 天前
Pydantic深度使用:数据校验、枚举、ORM映射
后端·fastapi
Chenyiax1 天前
从 Chat 到 Responses:OpenAI API 抽象为什么变了?
后端
MariaH1 天前
Koa和Express的区别
后端
MariaH1 天前
Koa框架的使用
后端
luckdewei1 天前
那个用 passlib 做认证的新同事,上线第一天就把用户密码写进了日志
后端
ping某1 天前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
JustHappy1 天前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom1 天前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github