深度解析 Spring MVC @ControllerAdvice
注解
@ControllerAdvice
是 Spring MVC 中实现全局处理的核心注解,它提供了一种声明式的方式来统一处理控制器层级的通用功能。本文将全面剖析其工作原理、源码实现、应用场景及最佳实践。
一、注解定义与核心作用
1. 源码定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
public @interface ControllerAdvice {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] assignableTypes() default {};
Class<? extends Annotation>[] annotations() default {};
}
2. 核心作用
- 统一异常处理:全局处理控制器抛出的异常
- 全局数据绑定:添加所有控制器共享的模型属性
- 全局请求预处理:在控制器执行前进行通用处理
- 全局后处理:在控制器执行后进行通用操作
二、工作原理与处理流程
1. 全局处理流程
2. 核心处理阶段
- 模型初始化:调用
@ModelAttribute
方法准备全局数据 - 请求处理:执行目标控制器方法
- 异常捕获:如出现异常,调用匹配的
@ExceptionHandler
- 响应后处理:调用
@ResponseBodyAdvice
方法处理返回结果
三、源码深度解析
1. 控制器增强扫描
ControllerAdviceBean
负责扫描和管理所有 @ControllerAdvice
组件:
class ControllerAdviceBean implements Ordered {
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
// 扫描带@ControllerAdvice的Bean
return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, Object.class)
.values().stream()
.filter(bean -> AnnotationUtils.findAnnotation(bean.getClass(), ControllerAdvice.class) != null)
.map(bean -> new ControllerAdviceBean(bean, context))
.sorted(OrderComparator.INSTANCE)
.collect(Collectors.toList());
}
}
2. 异常处理机制
ExceptionHandlerExceptionResolver
负责异常派发:
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver {
protected ModelAndView doResolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
// 1. 查找控制器内部的异常处理器
if (handler instanceof HandlerMethod) {
Method method = ((HandlerMethod) handler).getMethod();
Method exceptionHandlerMethod = findExceptionHandlerMethod(method, ex);
if (exceptionHandlerMethod != null) {
return invokeExceptionHandlerMethod(exceptionHandlerMethod, handler, ex, request, response);
}
}
// 2. 查找@ControllerAdvice中的全局异常处理器
for (Object advice : controllerAdviceCache) {
Method exceptionHandlerMethod = findExceptionHandlerMethod(advice, ex);
if (exceptionHandlerMethod != null) {
return invokeExceptionHandlerMethod(exceptionHandlerMethod, advice, ex, request, response);
}
}
return null;
}
}
3. 全局数据绑定
RequestMappingHandlerAdapter
处理全局模型属性:
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter {
private void updateModelAttributes(ModelMap model, Object handler,
NativeWebRequest request) throws Exception {
// 获取所有@ControllerAdvice
for (ControllerAdviceBean advice : controllerAdviceCache) {
// 执行@ModelAttribute方法
for (Method method : advice.getModelAttributeMethods()) {
ModelAttribute ann = method.getAnnotation(ModelAttribute.class);
String name = getNameForModelAttribute(ann, method);
Object value = method.invoke(advice.resolveBean());
model.addAttribute(name, value);
}
}
}
}
四、核心应用场景
1. 全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("RESOURCE_NOT_FOUND", ex.getMessage()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ValidationErrors> handleValidationErrors(MethodArgumentNotValidException ex) {
List<FieldError> errors = ex.getBindingResult().getFieldErrors()
.stream()
.map(error -> new FieldError(error.getField(), error.getDefaultMessage()))
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(new ValidationErrors(errors));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleAll(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("SERVER_ERROR", "Internal server error"));
}
}
2. 全局数据绑定
@ControllerAdvice
public class GlobalModelAttributes {
@ModelAttribute("appVersion")
public String appVersion() {
return "1.5.2";
}
@ModelAttribute("currentYear")
public int currentYear() {
return Year.now().getValue();
}
@ModelAttribute("environment")
public String environment() {
return System.getProperty("env", "development");
}
}
3. 全局请求预处理
@ControllerAdvice
public class RequestPreProcessor {
@ModelAttribute
public void addRequestInfo(HttpServletRequest request, Model model) {
model.addAttribute("requestId", UUID.randomUUID().toString());
model.addAttribute("clientIp", request.getRemoteAddr());
}
@InitBinder
public void initBinder(WebDataBinder binder) {
// 全局禁止绑定敏感字段
binder.setDisallowedFields("internalToken", "securityCode");
}
}
4. 全局响应后处理
@ControllerAdvice
public class GlobalResponseProcessor implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return returnType.hasMethodAnnotation(ResponseBody.class) ||
returnType.getContainingClass().isAnnotationPresent(RestController.class);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType contentType,
Class<? extends HttpMessageConverter<?>> converterType, ServerHttpRequest request, ServerHttpResponse response) {
// 统一封装响应格式
if (!(body instanceof ApiResponse)) {
return new ApiResponse<>(body);
}
return body;
}
}
public class ApiResponse<T> {
private String code = "SUCCESS";
private T data;
private long timestamp = System.currentTimeMillis();
// 构造器和方法
}
五、高级特性与配置
1. 限定作用范围
// 仅作用于指定包下的控制器
@ControllerAdvice(basePackages = "com.example.product.controllers")
// 仅作用于指定的控制器类
@ControllerAdvice(assignableTypes = {UserController.class, OrderController.class})
// 仅作用于带特定注解的控制器
@ControllerAdvice(annotations = RestController.class)
// 组合限定条件
@ControllerAdvice(
basePackages = "com.example.api",
annotations = VersionedAPI.class
)
2. 响应内容协商
@ControllerAdvice
public class ContentNegotiationAdvice implements RequestBodyAdvice, ResponseBodyAdvice<Object> {
// 请求体处理前
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
// 响应体处理前
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType contentType,
Class<? extends HttpMessageConverter<?>> converterType, ServerHttpRequest request, ServerHttpResponse response) {
// 根据Accept头动态调整响应格式
if (request.getHeaders().getAccept().contains(MediaType.APPLICATION_XML)) {
return new XmlResponse(body);
}
return body;
}
}
3. 多模块化处理
// 核心模块处理器
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice(assignableTypes = CoreController.class)
public class CoreExceptionHandler {
@ExceptionHandler(CoreException.class)
public ResponseEntity<ErrorResponse> handleCoreException() {
// 核心模块特有处理
}
}
// 通用处理器
@Order(Ordered.LOWEST_PRECEDENCE)
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException() {
// 通用处理
}
}
六、最佳实践与设计模式
1. 分层处理架构
graph TD
A[控制器执行] --> B{是否异常}
B -->|是| C[控制器内部@ExceptionHandler]
C -->|未处理| D[模块级@ControllerAdvice]
D -->|未处理| E[全局@ControllerAdvice]
E --> F[返回错误响应]
B -->|正常| G[控制器返回结果]
G --> H[全局ResponseBodyAdvice]
H --> I[返回统一响应]
2. 响应标准化
统一响应格式规范:
{
"success": true,
"code": "SUCCESS",
"message": "操作成功",
"data": {},
"timestamp": 1630425600000,
"requestId": "req_123456"
}
实现代码:
@ControllerAdvice
public class StandardResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType contentType,
Class<? extends HttpMessageConverter<?>> converterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof StandardResponse) {
return body;
}
// 创建统一响应对象
StandardResponse<Object> result = new StandardResponse<>();
result.setData(body);
result.setSuccess(true);
result.setTimestamp(System.currentTimeMillis());
// 设置请求ID(如果存在)
Object requestId = request.getHeaders().getFirst("X-Request-ID");
if (requestId != null) {
result.setRequestId(requestId.toString());
}
return result;
}
}
3. 安全实践
敏感信息处理:
@ControllerAdvice
public class SecurityEnhancement {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex,
HttpServletRequest request) {
// 生产环境隐藏异常详情
if ("production".equals(environment)) {
return ResponseEntity.internalServerError()
.body(new ErrorResponse("SERVER_ERROR", "Internal server error"));
} else {
return ResponseEntity.internalServerError()
.body(new ErrorResponse("SERVER_ERROR", ex.getMessage()));
}
}
@ModelAttribute
public void removeSensitiveHeaders(HttpServletResponse response) {
// 移除敏感头信息
response.setHeader("X-Powered-By", "");
response.setHeader("Server", "Secure-Server");
}
}
七、企业级应用方案
1. 微服务架构集成
@ControllerAdvice
public class MicroserviceIntegration {
// 分布式链路追踪
@ModelAttribute
public void addTraceInfo(@RequestHeader(value = "X-B3-TraceId", required = false) String traceId,
Model model) {
if (traceId == null) {
traceId = IdGenerator.generateTraceId();
}
model.addAttribute("traceId", traceId);
MDC.put("traceId", traceId);
}
// 微服务异常转换
@ExceptionHandler(FeignException.class)
public ResponseEntity<ErrorResponse> handleFeignException(FeignException ex) {
ErrorResponse error = parseFeignException(ex);
return ResponseEntity.status(ex.status()).body(error);
}
}
2. 监控与指标收集
@ControllerAdvice
public class MetricsCollector {
private final MeterRegistry meterRegistry;
public MetricsCollector(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
// 请求计数器
@ModelAttribute
public void countRequest(HttpServletRequest request) {
String uri = request.getRequestURI();
Counter.builder("http.requests")
.tag("method", request.getMethod())
.tag("uri", uri)
.tag("status", "started")
.register(meterRegistry)
.increment();
}
// 异常指标
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleAndRecordException(Exception ex,
HttpServletRequest request, HttpServletResponse response) {
String uri = request.getRequestURI();
Counter.builder("http.errors")
.tag("method", request.getMethod())
.tag("uri", uri)
.tag("exception", ex.getClass().getSimpleName())
.register(meterRegistry)
.increment();
return ResponseEntity.status(500).build();
}
}
3. API 版本控制
@ControllerAdvice
public class ApiVersionHandler {
@ModelAttribute
public void checkApiVersion(@RequestHeader("API-Version") String apiVersion,
HttpServletResponse response) {
response.setHeader("API-Version", apiVersion);
if (!"v2".equals(apiVersion) && !"v3".equals(apiVersion)) {
throw new UnsupportedApiVersionException("Unsupported API version");
}
}
@ExceptionHandler(UnsupportedApiVersionException.class)
public ResponseEntity<ErrorResponse> handleUnsupportedVersion() {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("UNSUPPORTED_VERSION", "Please use v2 or v3 API"));
}
}
八、常见问题解决方案
1. 全局处理器未生效
解决方案:
// 1. 确保组件扫描包含
@ComponentScan(basePackages = "com.example.advice")
// 2. 检查Order顺序
@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE) // 提高优先级
// 3. 确认方法访问权限
@ControllerAdvice
public class MyAdvice {
@ExceptionHandler // public方法
public ResponseEntity<?> handle() { ... }
}
2. 与控制器处理器冲突
解决方案:
// 1. 明确职责划分
// 控制器处理业务异常,全局处理技术异常
// 2. 使用优先级控制
@Order(Ordered.LOWEST_PRECEDENCE)
@ControllerAdvice
public class FallbackAdvice {
@ExceptionHandler(Exception.class)
public ResponseEntity<?> fallback() { ... }
}
// 3. 在控制器中重定向到全局处理
@RestController
public class ProductController {
@ExceptionHandler(ProductException.class)
public void handleProductException(ProductException ex) throws Exception {
// 重定向到全局处理
throw new GlobalHandlingException(ex);
}
}
3. 跨域问题
解决方案:
// 全局CORS配置
@ControllerAdvice
public class CorsControllerAdvice implements CorsConfigurationSource {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setMaxAge(3600L);
return config;
}
}
九、未来发展方向
1. 响应式架构支持
@ControllerAdvice
public class ReactiveControllerAdvice {
@ExceptionHandler
public Mono<ResponseEntity<String>> handleException(ServerWebExchange exchange, Exception ex) {
return Mono.just(ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Error occurred: " + ex.getMessage()));
}
@ModelAttribute
public Mono<Void> addAttributes(ServerWebExchange exchange) {
return Mono.fromRunnable(() ->
exchange.getAttributes().put("requestTime", Instant.now()));
}
}
2. 智能异常诊断
@ControllerAdvice
public class AIDiagnosisAdvice {
@ExceptionHandler(Exception.class)
public ResponseEntity<DiagnosisResult> handleWithDiagnosis(Exception ex,
@Autowired AIDiagnosisService diagnosisService) {
Diagnosis diagnosis = diagnosisService.diagnose(ex);
return ResponseEntity.status(diagnosis.getRecommendedStatus())
.body(new DiagnosisResult(diagnosis));
}
}
3. 自适应安全响应
@ControllerAdvice
public class AdaptiveSecurityAdvice {
@ExceptionHandler(SecurityException.class)
public ResponseEntity<?> handleSecurityException(SecurityException ex,
HttpServletRequest request) {
ClientInfo client = securityService.analyzeClient(request);
return ResponseEntity
.status(client.isSuspicious() ? 403 : 401)
.header("X-Security-Level", client.threatLevel())
.build();
}
}
十、总结
@ControllerAdvice
是 Spring MVC 中实现全局处理的核心机制,其关键优势在于:
- 集中管理:统一处理横切关注点
- 模块化设计:分模块实现不同关注点
- 灵活扩展:通过优先级和限定条件控制处理范围
- 标准化支持:实现统一响应格式和错误处理
在实际应用中应当:
- 合理分层:控制器级处理 > 模块级处理 > 全局处理
- 标准化响应:统一 API 响应格式
- 安全隔离:控制异常信息暴露
- 性能监控:集成指标收集机制
在企业级系统中:
- 微服务架构:处理分布式系统的共性需求
- 云原生环境:适配 Kubernetes 等环境特性
- 多租户系统:处理租户隔离的通用逻辑
- 国际化支持:统一处理多语言资源
掌握 @ControllerAdvice
的高级特性和最佳实践,能够帮助开发者:
- 构建高内聚、低耦合的控制器层
- 实现一致的用户体验
- 提升系统可维护性和扩展性
- 构建企业级的标准化处理流程
作为 Spring MVC 全局处理的基石,@ControllerAdvice
在现代 Web 应用开发中扮演着不可替代的角色,深入理解其原理和应用是每个 Spring 开发者必备的核心能力。