异常处理HandlerExceptionResolver
HandlerExceptionResolver定义了SpringMVC异常处理的标准接口,可扩展实现自定义异常处理逻辑。
接口定义
Java
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
@Nullable Object handler,
Exception ex);
}
内置实现类
| 实现类 | 功能 | 优先级 |
|---|---|---|
| ExceptionHandlerExceptionResolver | 处理@ExceptionHandler方法 | 1 |
| ResponseStatusExceptionResolver | 处理@ResponseStatus注解 | 2 |
| DefaultHandlerExceptionResolver | 处理Spring标准异常 | 3 |
| SimpleMappingExceptionResolver | 异常映射到视图 | 4 |
自定义HandlerExceptionResolver
实现方式
Java
@Component
public class CustomExceptionResolver implements HandlerExceptionResolver, Ordered {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
// 处理自定义异常
if (ex instanceof BusinessException) {
BusinessException be = (BusinessException) ex;
response.setStatus(be.getCode());
response.setContentType("application/json;charset=UTF-8");
try {
response.getWriter().write(
"{\"code\":\"" + be.getCode() + "\",\"message\":\"" + be.getMessage() + "\"}");
} catch (IOException e) {
log.error("写入响应失败", e);
}
return new ModelAndView(); // 空ModelAndView表示已处理
}
return null; // null表示不处理,交给下一个Resolver
}
@Override
public int getOrder() {
return 0; // 最高优先级
}
}
注册自定义Resolver
Java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
// 添加到解析器链前端
resolvers.add(0, new CustomExceptionResolver());
}
}
ExceptionHandlerExceptionResolver详解
核心逻辑
Java
public class ExceptionHandlerExceptionResolver
extends AbstractHandlerMethodExceptionResolver {
// 异常处理方法缓存
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =
new ConcurrentHashMap<>(64);
// @ControllerAdvice缓存
private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver>
exceptionHandlerAdviceCache = new LinkedHashMap<>();
protected ModelAndView doResolveHandlerMethodException(
HttpServletRequest request, HttpServletResponse response,
HandlerMethod handlerMethod, Exception exception) {
// 查找匹配的@ExceptionHandler方法
ServletInvocableHandlerMethod exceptionHandlerMethod =
getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
// 执行异常处理方法
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);
return getModelAndView(mavContainer, model, webRequest);
}
}
异常方法查找顺序
Java
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = handlerMethod.getBeanType();
// 1. 先在当前Controller查找@ExceptionHandler
ExceptionHandlerMethodResolver resolver =
this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
// 2. 在@ControllerAdvice中查找
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry :
this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver adviceResolver = entry.getValue();
Method adviceMethod = adviceResolver.resolveMethod(exception);
if (adviceMethod != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), adviceMethod);
}
}
}
return null;
}
ResponseStatusExceptionResolver
Java
public class ResponseStatusExceptionResolver
extends AbstractHandlerExceptionResolver {
protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus,
HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws IOException {
int statusCode = responseStatus.code().value();
String reason = responseStatus.reason();
if (StringUtils.hasLength(reason)) {
response.sendError(statusCode, reason);
} else {
response.setStatus(statusCode);
}
return new ModelAndView();
}
}
// 使用示例
@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "资源不存在")
public class ResourceNotFoundException extends RuntimeException {
}
DefaultHandlerExceptionResolver
Java
public class DefaultHandlerExceptionResolver
extends AbstractHandlerExceptionResolver {
protected ModelAndView doResolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
try {
// HTTP方法不支持
if (ex instanceof HttpRequestMethodNotSupportedException) {
return handleHttpRequestMethodNotSupported(
(HttpRequestMethodNotSupportedException) ex, request, response);
}
// Media类型不支持
if (ex instanceof HttpMediaTypeNotSupportedException) {
return handleHttpMediaTypeNotSupported(
(HttpMediaTypeNotSupportedException) ex, response);
}
// 缺少必填参数
if (ex instanceof MissingServletRequestParameterException) {
return handleMissingServletRequestParameter(
(MissingServletRequestParameterException) ex, response);
}
// 类型转换异常
if (ex instanceof TypeMismatchException) {
return handleTypeMismatch((TypeMismatchException) ex, response);
}
// 参数绑定异常
if (ex instanceof BindException) {
return handleBindException((BindException) ex, response);
}
// 更多标准异常处理...
return null;
} catch (IOException handlerException) {
return null;
}
}
}
SimpleMappingExceptionResolver
Java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("BusinessException", "error/business");
mappings.setProperty("DataAccessException", "error/data");
mappings.setProperty("Exception", "error/general");
resolver.setExceptionMappings(mappings);
resolver.setDefaultErrorView("error/default");
resolver.setExceptionAttribute("exception");
return resolver;
}
}
异常处理链执行顺序
Java
// DispatcherServlet
protected ModelAndView processHandlerException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mav = null;
// 遍历所有HandlerExceptionResolver
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
mav = resolver.resolveException(request, response, handler, ex);
if (mav != null) {
break; // 找到处理结果后停止
}
}
// 所有Resolver都未处理
if (mav == null) {
throw new ServletException("Could not resolve exception", ex);
}
return mav;
}
扩展点总结
| 扩展方式 | 适用场景 | 说明 |
|---|---|---|
| @ExceptionHandler | Controller内异常处理 | 粒度细,只处理当前Controller异常 |
| @ControllerAdvice | 全局异常处理 | 统一管理,支持限定Controller范围 |
| 自定义Resolver | 特殊异常处理逻辑 | 完全控制异常响应格式 |
异常处理优先级图
text
异常抛出
↓
遍历HandlerExceptionResolver链
↓
CustomExceptionResolver (order=0)
↓ (返回null)
ExceptionHandlerExceptionResolver
↓ 查找@ExceptionHandler
↓ 执行处理方法
↓ (返回null)
ResponseStatusExceptionResolver
↓ 处理@ResponseStatus
↓ (返回null)
DefaultHandlerExceptionResolver
↓ 处理Spring标准异常
↓ (返回null)
抛出ServletException
HandlerExceptionResolver按优先级顺序执行,返回非null表示已处理。
要点总结
- HandlerExceptionResolver接口定义异常处理契约
- 返回null表示不处理,交给下一个Resolver
- ExceptionHandlerExceptionResolver处理@ExceptionHandler方法
- 自定义Resolver可实现完全的异常响应控制
- 多个Resolver形成责任链,按Order顺序执行
jwdev/articles/SPRINGMVC/专家/容器级WEB组件扩展/容器级WEB组件扩展/异常处理HandlerExceptionResolver.md
📝 发现内容有误?点击此处直接编辑