1. 前言
系统开发过程中,异常处理与业务同样重要,完善的异常处理才能让业务系统更加健壮。下面会介绍如何实现全局异常处理以及全局异常处理实现的原理。
2.全局异常实现
2.1 @ControllerAdvice声明
@ControllerAdvice
public class GlobalExceptionHandler {
}
2.2 @ExceptionHandler声明
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public BaseResponse<?> handleMethodArgumentException(MethodArgumentNotValidException exception) {
List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
Set<String> errorMessage = new HashSet<>();
for (FieldError fieldError : fieldErrors) {
errorMessage.add(fieldError.getDefaultMessage());
}
BaseResponse<?> baseResponse = new BaseResponse<>();
baseResponse.setCode(10001);
baseResponse.setMsg(errorMessage.toString());
return baseResponse;
}
3.全局异常原理
3.1 声明异常解析器
@Bean
public HandlerExceptionResolver handlerExceptionResolver(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
configureHandlerExceptionResolvers(exceptionResolvers);
if (exceptionResolvers.isEmpty()) {
addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
}
extendHandlerExceptionResolvers(exceptionResolvers);
HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
composite.setOrder(0);
composite.setExceptionResolvers(exceptionResolvers);
return composite;
}
3.1.1 添加默认异常解析器
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,
ContentNegotiationManager mvcContentNegotiationManager) {
ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
exceptionHandlerResolver.setMessageConverters(getMessageConverters());
exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
if (jackson2Present) {
exceptionHandlerResolver.setResponseBodyAdvice(
Collections.singletonList(new JsonViewResponseBodyAdvice()));
}
if (this.applicationContext != null) {
exceptionHandlerResolver.setApplicationContext(this.applicationContext);
}
exceptionHandlerResolver.afterPropertiesSet();
exceptionResolvers.add(exceptionHandlerResolver);
ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
responseStatusResolver.setMessageSource(this.applicationContext);
exceptionResolvers.add(responseStatusResolver);
exceptionResolvers.add(new DefaultHandlerExceptionResolver());
}
3.1.2 ExceptionHandlerExceptionResolver异常解析器初始化
public void afterPropertiesSet() {
// Do this first, it may add ResponseBodyAdvice beans
initExceptionHandlerAdviceCache();
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
3.1.3 处理@ControllerAdvice
private void initExceptionHandlerAdviceCache() {
// 1. 遍历所有被@ControllerAdvice注解标注的Bean,生成ControllerAdviceBean对象
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
// 2. 创建ExceptionHandlerMethodResolver对象,遍历对应类下面的所有方法,解析方法上的@ExceptionHandler注解,将异常类型与目标方法映射关系存入mappedMethods集合中
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
// 3. 将ControllerAdviceBean与ExceptionHandlerMethodResolver映射关系存入map中
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
}
3.2 初始化异常解析器
DispatcherServlet初始化方法中不仅会初始化HandlerMappings、HandlerAdapters,还会初始化HandlerExceptionResolvers
protected void initStrategies(ApplicationContext context) {
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
}
3.3 处理异常
我们都知道DispatcherServlet的doDispatch()方法用来处理客户端的请求
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 处理业务方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
}
catch (Throwable err) {
}
finally {
}
}
如上逻辑可以得知mv = ha.handle(processedRequest, response, mappedHandler.getHandler());用来处理业务方法,假设业务方法抛出异常,异常会被catch块捕获,程序会进入processDispatchResult()方法
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
}
3.3.1 遍历异常处理器解析异常
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
// 遍历异常解析器解析异常
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
throw ex;
}
3.3.2 根据异常类型找到目标方法
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
@Nullable HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = null;
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
}
}
}
return null;
}
在3.1.3章节可以了解到exceptionHandlerAdviceCache结构存放的是ControllerAdviceBean与ExceptionHandlerMethodResolver的映射关系,ExceptionHandlerMethodResolver中的mappedMethods结构存放的是异常类型与目标方法的映射关系,因此Method method = resolver.resolveMethod(exception);可以通过异常类型得到目标方法,有了目标方法就可以利用反射来执行,也就是@ControllerAdvice中被@ExceptionHandler注解标注匹配异常类型对应的方法