Java异常处理核心是分层归因、主动封装与语义对齐:HTTP层用validateStatus区分业务码,JDBC层借SQLExceptionTranslator映射精准异常,泛型反序列化须传TypeReference避免擦除导致的ClassCastException。
“强制捕获转化层架构”并不是一个标准技术概念,java 或主流前端/后端生态中不存在可配置、可声明的‘捕获转化层’架构。所谓“用某一层强行转化原始报错为新版异常”,本质是混淆了错误来源与处理责任边界——报错是否可捕获、是否需转化、如何统一语义,取决于异常发生的位置、抛出时机、调用栈深度及框架约定,而非靠虚构的“转化层”一揽子解决。
真正要落地的是分层归因 + 主动封装 + 语义对齐。下面从三个关键场景切入,讲清楚怎么做:
一、HTTP 请求异常:别依赖 Axios 默认 catch,要主动接管状态语义
Axios 将 4xx/5xx 响应直接 reject,但业务上 400(参数错误)、401(未登录)、403(无权限)含义完全不同,不能全塞进 error.response.data 里靠 if-else 拆解。
- 显式配置
validateStatus,让特定业务码进入.then()流程axios.get('/api/user', { validateStatus: (status) => status < 500 // 4xx 不进 catch,统一走 then }).then(res => { if (res.status === 400) handleValidationError(res.data); if (res.status === 401) redirectToLogin(); }); - 对真正需拦截的底层错误(网络中断、超时、CORS),在
request.interceptors中统一补全上下文axios.interceptors.response.use( res => res, error => { const enriched = { ...error, timestamp: Date.now(), url: error.config?.url, isNetworkError: !error.response // 无 response 即网络层失败 }; captureException(enriched); // 上报监控 return Promise.reject(enriched); } );
二、JDBC/ORM 底层异常:不包装成 RuntimeException,而按故障类型路由
SQLException 是检查型异常,Spring 的 @Transactional 默认只对 RuntimeException 回滚。若直接 throw new RuntimeException(e),会丢失 SQL 状态码(如 SQLState=23505 表示唯一键冲突)、错误码(errorCode=1 for MySQL duplicate key),后续无法做精准重试或降级。
-
使用 Spring 的
SQLExceptionTranslator体系,把原生异常映射为语义化子类@Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; public void createUser(User user) { try { jdbcTemplate.update("INSERT INTO users ...", ...); } catch (DataAccessException e) { if (e.getCause() instanceof SQLException sqlEx) { String sqlState = sqlEx.getSQLState(); if ("23505".equals(sqlState)) { // PostgreSQL unique_violation throw new UserAlreadyExistsException(user.getEmail()); } } throw e; // 其他异常继续向上抛 } } } -
在全局异常处理器中,按自定义异常类型返回对应 HTTP 状态码和 message
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(UserAlreadyExistsException.class) public ResponseEntity<ApiError> handleUserExists(UserAlreadyExistsException e) { return ResponseEntity.status(409).body(new ApiError("USER_EXISTS", e.getMessage())); } }
三、泛型擦除引发的运行时类型异常:不靠 try-catch 弥补,而用类型元数据加固
比如 ApiResponse<list>></list> 反序列化成 LinkedHashMap,是因为 JSON 框架(如 Jackson)在运行时看不到 List<user></user> 的泛型信息,导致 response.getData() 返回 Object,强转 List<user></user> 报 ClassCastException。
-
所有泛型反序列化必须传
TypeReference,禁用裸 class// ❌ 错误:擦除后只剩 ApiResponse.class,List<User> 信息丢失 ApiResponse data = objectMapper.readValue(json, ApiResponse.class); // ✅ 正确:保留完整泛型路径 ApiResponse<List<User>> response = objectMapper.readValue( json, new TypeReference<ApiResponse<List<User>>>() {} ); -
工具方法封装时,强制要求调用方传入
Class<t></t>或TypeReference<t></t>public <T> T post(String url, Object body, Class<T> responseType) { ... } public <T> T post(String url, Object body, TypeReference<T> typeRef) { ... }
异常不是需要“抹平”的噪音,而是系统状态的诚实表达。所谓“完美转化”,其实是把原始信号翻译成当前层能理解、能响应、能传播的语义单元——它靠的是明确的分界、克制的封装、一致的契约,而不是加一层虚设的“强制转化架构”。

















