在用户中心这类核心服务中,优雅的异常处理是系统健壮性的生命线。未处理的异常会导致:
通过全局异常处理器,我们可以实现:
✅ 统一错误响应格式
✅ 集中管理错误码
✅ 自动记录关键日志
✅ 防止敏感信息泄露
@Slf4j
@ControllerAdvice
@ResponseBody
@Order(-1) // 确保最高优先级
public class UserCenterExceptionHandler {
// 关键注解说明:
// - @ControllerAdvice: 控制器增强,拦截所有Controller异常
// - @Order(-1): 确保优先于其他异常处理器
// - @ResponseBody: 直接返回序列化结果
private static final Logger LOGGER = LoggerFactory.getLogger(...);
}
@ExceptionHandler(UserException.class)
public Object handleUserException(UserException e) {
// 结构化日志记录(关键!)
LOGGER.error("[UserException] code={} | msg={} | location={}",
e.getCode(), e.getMessage(), getExceptionLocation(e));
return Result.failed(e.getMessage(), e.getCode());
}
日志优化技巧:
@ExceptionHandler(RuntimeException.class)
public Result handleRuntimeException(Exception e) {
// 防止敏感信息泄露
String safeMsg = "系统繁忙,请稍后重试";
LOGGER.error("[UnknownException] location={} | detail={}",
getExceptionLocation(e), e.getMessage());
return Result.failed(safeMsg, ErrorCodeEnum.SYSTEM_ERROR.getCode());
}
private String getExceptionLocation(Exception e) {
return Arrays.stream(e.getStackTrace())
.filter(stack -> !stack.getClassName().startsWith("com.sun.proxy")) // 过滤代理类
.findFirst()
.map(stack -> String.format("%s.%s(%s:%d)",
stack.getClassName(),
stack.getMethodName(),
stack.getFileName(),
stack.getLineNumber()))
.orElse("unknown_location");
}
优化前 | 优化后 |
---|---|
com.alipay.UserService$$EnhancerBySpringCGLIB$$123aab.doSomething(UserService.java:-1) |
com.alipay.UserServiceImpl.updatePassword(UserServiceImpl.java:42) |
graph TD
A[Throwable] --> B[Checked Exception]
A --> C[Unchecked Exception]
C --> D[BusinessException]
C --> E[SystemException]
D --> F[UserException]
D --> G[OrderException]
E --> H[DBConnectionException]
E --> I[CacheException]
public enum ErrorCodeEnum {
// 格式:类型_模块_编号
B_AUTH_1001("B_AUTH_1001", "认证失败"),
S_USER_2001("S_USER_2001", "用户服务异常"),
// 错误码组成规则:
// 第1位:B-业务错误/S-系统错误
// 第2位:模块缩写
// 后4位:具体错误编号
}
@ExceptionHandler(Exception.class)
public Result handleException(HttpServletRequest request, Exception e) {
// 生成唯一追踪ID
String traceId = UUID.randomUUID().toString();
// 将TraceID返回给客户端
return Result.failed()
.code(ErrorCode.SYSTEM_ERROR)
.message("请联系管理员并提供追踪ID: " + traceId)
.data("traceId", traceId);
// 后台日志关联TraceID
LOGGER.error("[TraceID:{}] 系统异常: {}", traceId, e.getMessage());
}
// 错误示例:直接返回异常堆栈
return Result.failed(e.getMessage());
// 正确做法:生产环境屏蔽详情
if (env.equals("prod")) {
return Result.failed("系统繁忙");
}
// 避免在异常处理中执行耗时操作
@ExceptionHandler
public Result handle(IOException e) {
// ❌ 同步写入日志文件
// ✅ 使用AsyncAppender异步记录
}
// 结合Micrometer实现异常指标统计
@ExceptionHandler
public Result handle(Exception e) {
Metrics.counter("system.exception",
"type", e.getClass().getSimpleName())
.increment();
// 推送到Prometheus+Grafana
}
分层处理:
监控三板斧:
演进路线:
journey
title 异常处理演进路线
section 基础版
统一响应格式 --> 错误码体系
section 进阶版
链路追踪 --> 监控告警
section 终极版
智能熔断 --> 自动修复