spring boot 全局异常处理通过 @restcontrolleradvice 和 @exceptionhandler 捕获灰度发布异常,核心做法是定义一个专门处理灰度逻辑异常的 handler,并在其中返回统一的错误响应。具体实现时,需确保灰度路由组件抛出的异常能被 spring mvc 的异常解析机制识别,通常直接抛出运行时异常即可被全局拦截。

如何定义全局异常处理器来拦截灰度异常
定义全局异常处理器需要创建一个标注了 @RestControllerAdvice 的类,并在其中编写处理特定灰度异常的方法。该方法需使用 @ExceptionHandler 注解指定要捕获的异常类型,例如自定义的 GrayScaleException。当灰度服务在判断用户是否进入灰度环境或执行灰度逻辑时抛出该异常,Spring Boot 会自动将其转发到此方法进行处理,从而避免将堆栈信息暴露给前端。

import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(GrayScaleException.class)
public ResponseEntity<String> handleGrayScaleException(GrayScaleException ex) {
return ResponseEntity.status(503).body("Gray scale service unavailable: " + ex.getMessage());
}
}
上述代码展示了最简化的全局异常处理结构。关键点在于 @RestControllerAdvice 的作用范围覆盖了所有 @RestController,而 @ExceptionHandler 精准定位到灰度相关的异常。在实际项目中,建议将异常信息封装为标准的 JSON 对象,以便前端统一解析。若灰度逻辑依赖于外部配置中心,连接失败时也可复用此 Handler 返回降级策略。
灰度异常的具体触发场景与处理策略
灰度发布过程中常见的异常包括灰度规则匹配失败、灰度服务实例不可用以及配置加载超时。针对这些场景,全局异常处理器应区分对待:对于规则匹配失败,通常属于业务逻辑层面的正常分支,不应视为系统异常;而对于服务不可用或配置超时,则属于基础设施故障,需要触发熔断或降级逻辑。通过在灰度过滤器中捕获底层异常并重新包装为 GrayScaleException,可以确保全局处理器能统一接管这些故障。

import org.springframework.stereotype.Component;
@Component
public class GrayScaleFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
try {
// 模拟灰度判断逻辑
boolean isGray = checkGrayRule();
if (!isGray) {
throw new GrayScaleException("User not in gray scale group");
}
chain.doFilter(request, response);
} catch (GrayScaleException e) {
// 记录日志并可选择性转发给全局处理器
// 若未手动处理,异常会向上传播至 @RestControllerAdvice
throw e;
}
}
private boolean checkGrayRule() {
return false;
}
}
<pre class="brush:php;toolbar:false;">
<p>在过滤器层面抛出异常时,需注意事务和请求上下文的清理。如果灰度判断发生在 Controller 内部,则直接抛出异常即可由全局处理器捕获。若发生在过滤器或拦截器阶段,需确保异常类型未被框架特殊处理(如 <code>ServletException</code>),否则可能绕过 Spring 的全局异常机制。因此,自定义异常必须继承自 <code>RuntimeException</code> 或其子类,以保证传播路径畅通。</p>
<h2>基于灰度服务返回 503 状态码的具体问题</h2><p>当灰度服务因过载或维护返回 503 状态码时,全局异常处理器应如何正确映射这一结果?默认情况下,Spring Boot 会将 503 视为服务器错误,但在全局异常处理中,我们可能需要根据业务需求调整响应体格式。若灰度组件直接抛出异常并设置状态码,全局处理器只需提取该状态码并构建对应的 JSON 响应即可。关键在于确保异常对象中携带了原始的状态码信息,或者在 Handler 中硬编码 503 以强制降级。</p>
<pre class="brush:php;toolbar:false;">import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
@ExceptionHandler(GrayScaleException.class)
@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
public ResponseEntity<Map<String, Object>> handleGrayScaleException(GrayScaleException ex) {
Map<String, Object> body = new HashMap<>();
body.put("error", "GRAY_SCALE_ERROR");
body.put("message", ex.getMessage());
body.put("timestamp", System.currentTimeMillis());
return new ResponseEntity<>(body, HttpStatus.SERVICE_UNAVAILABLE);
}
上述代码展示了如何将 503 状态码与结构化响应体结合。通过 @ResponseStatus 注解可以直接设定 HTTP 状态码,而返回值则负责构建详细的错误描述。这种处理方式确保了前端能够明确区分“灰度服务暂时不可用”与“其他服务器内部错误”。在实际应用中,还可结合 Redis 缓存灰度状态,若缓存获取失败则抛出异常,由同一 Handler 统一返回降级提示,保持接口行为的一致性。


















