先放實(shí)體類,通用的響應(yīng)體
@Data
public class ResponseDTO<T> {
private int code;
private String msg;
private T data;
@Override
public String toString() {
return "ResponseDTO [code=" + code + ", msg=" + msg + ", data=" + data + "]";
}
public ResponseDTO(int code, String msg, T data) {
super();
this.code = code;
this.msg = msg;
this.data = data;
}
}
響應(yīng)枚舉,記錄各個(gè)狀態(tài)下的信息
public class ResponseContants {
private static final int UNKNOW_ERROR_CODE = 10000;
private static final int PARAM_ERROR_CODE = 10001;
private static final int SERVICE_ERROR_CODE = 10002;
public enum ResponseMsg{
SUCCESS(0, "success"),
UNKNOW_ERROR(UNKNOW_ERROR_CODE, "未知異常"),
PARAM_ERROR(PARAM_ERROR_CODE, "參數(shù)存在錯(cuò)誤"),
SERVICE_ERROR(SERVICE_ERROR_CODE, "處理業(yè)務(wù)的過程中出現(xiàn)異常");
private int msgCode;
private String msg;
ResponseMsg(int msgCode, String msg) {
this.msgCode = msgCode;
this.msg = msg;
}
public int getMsgCode() {
return msgCode;
}
public void setMsgCode(int msgCode) {
this.msgCode = msgCode;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
@Override
public String toString() {
return "ResponseContants [msgCode=" + msgCode + ", msg=" + msg + "]";
}
}
}
生成響應(yīng)體的工具
public class CommonUtils {
//生成通用響應(yīng)體
public static <T> ResponseDTO<T> genResponseDTO(T data, ResponseMsg responseMsg) {
return new ResponseDTO<T>(responseMsg.getMsgCode(), responseMsg.getMsg(), data);
}
public static<T> ResponseDTO<T> genResponseDTO(int code, String message, T data) {
return new ResponseDTO<T>(code, message, data);
}
}
全局異常捕獲器
@ControllerAdvice
@ComponentScan(basePackages = "com.cet.electric.ibsdataservice")
@Slf4j
@Responbody
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
// 該注解聲明異常處理方法
public ResponseDTO<ErrorMsg> exceptionHandler(HttpServletRequest request, Exception exception) {
log.error("GlobalExceptionHandler exceptionHandler exception = {}", exception);
if (exception instanceof ErrorMsg) {
return CommonUtils.genResponseDTO(((ErrorMsg) exception).getCode(), exception.getMessage(),
(ErrorMsg) exception);
}
return CommonUtils.genResponseDTO(ResponseContants.ResponseMsg.UNKNOW_ERROR.getMsgCode(),
ResponseContants.ResponseMsg.UNKNOW_ERROR.getMsg(),
new ErrorMsg(ResponseContants.ResponseMsg.UNKNOW_ERROR.getMsgCode(), exception.getMessage()));
}
}
隨后我們自己隨便定義一個(gè)接口,拋出異常,發(fā)現(xiàn)最后返回的響應(yīng)是我們自己定義的。
那么spring究竟是怎么實(shí)現(xiàn)的呢。
那么首先來看看一個(gè)接口。HandlerExceptionResolver ,這個(gè)接口是異常處理解析器的接口,里面只提供了一個(gè)方法,就是resolveException方法,并且返回視圖。
public interface HandlerExceptionResolver {
/**
* Try to resolve the given exception that got thrown during handler execution,
* returning a {@link ModelAndView} that represents a specific error page if appropriate.
* <p>The returned {@code ModelAndView} may be {@linkplain ModelAndView#isEmpty() empty}
* to indicate that the exception has been resolved successfully but that no view
* should be rendered, for instance by setting a status code.
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or {@code null} if none chosen at the
* time of the exception (for example, if multipart resolution failed)
* @param ex the exception that got thrown during handler execution
* @return a corresponding {@code ModelAndView} to forward to,
* or {@code null} for default processing in the resolution chain
*/
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
那么接下來,看看這個(gè)接口的對應(yīng)實(shí)現(xiàn),有好幾層,其中AbstractHandlerExceptionResolver是核心。實(shí)現(xiàn)了大部分的功能。而AbstractHandlerMethodExceptionResolver則是AbstractHandlerExceptionResolver的子類,專門用于處理方法的異常。
ExceptionHandlerExceptionResolver->AbstractHandlerMethodExceptionResolver->
AbstractHandlerExceptionResolver->HandlerExceptionResolver
通過debug。ExceptionHandlerExceptionResolver的initExceptionHandlerAdviceCache方法,會(huì)將spring中被ControllerAdviceBean注釋所修飾的bean給找出來,并且存入緩存中
private void initExceptionHandlerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
//找出容器中被@ControllerAdviceBean注解修飾的bean,并且進(jìn)行排序
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
//遍歷,并且放入緩存中
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
if (logger.isDebugEnabled()) {
int handlerSize = this.exceptionHandlerAdviceCache.size();
int adviceSize = this.responseBodyAdvice.size();
if (handlerSize == 0 && adviceSize == 0) {
logger.debug("ControllerAdvice beans: none");
}
else {
logger.debug("ControllerAdvice beans: " +
handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");
}
}
}
DispatcherServlet是如何處理的,對于拋出的異常,無非也是try catch再去處理,可以看以下代碼
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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 {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
//出現(xiàn)異常,則先捕獲
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//將請求,請求匹配的處理器,視圖,以及異常傳入,接下來我們往下看
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
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) {
//異常必然不是這個(gè)類型的。因此走另外一個(gè)分支
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
//這里則是根據(jù)異常處理器返回視圖的關(guān)鍵
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
。。。下面還有一段代碼。但是對分析這個(gè)過程來說意義不大,所以直接忽略
}
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// Success and error responses may use different content types
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
//異常的視圖的則是從此處獲取了,那么resolver是什么東西呢?我們通過斷點(diǎn)來看看,如下圖所示
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
//其實(shí)下面還有一段。不過感覺一個(gè)是看不懂二是意義不大,直接忽略
}
dispatcher中的handlerExceptionResolvers,有2個(gè)對象分別是DefaultErrorAttributes以及HandlerExceptionResolverComposite,通過debug發(fā)現(xiàn),HandlerExceptionResolverComposite類型才是關(guān)鍵

然后我們來看看HandlerExceptionResolverComposite的resolveException方法。其實(shí)通過名字都知道這個(gè)類是一個(gè)復(fù)合的異常處理解析器。其中有一個(gè)成員變量用于存儲(chǔ)異常解析器。
public class HandlerExceptionResolverComposite implements HandlerExceptionResolver, Ordered {
//用于存儲(chǔ)異常解析器
@Nullable
private List<HandlerExceptionResolver> resolvers;
/**
* Resolve the exception by iterating over the list of configured exception resolvers.
* <p>The first one to return a {@link ModelAndView} wins. Otherwise {@code null}
* is returned.
*/
@Override
@Nullable
//遍歷處理異常,若異常處理器匹配,則直接返回處理后的結(jié)構(gòu),總共有3個(gè)對象,如下圖所示
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler,Exception ex) {
if (this.resolvers != null) {
for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (mav != null) {
return mav;
}
}
}
return null;
}
}
復(fù)合異常處理器所存儲(chǔ)的異常解析器類型。這里我們只需要關(guān)注ExceptionHandlerExceptionResolver即可。

下面來看看ExceptionHandlerExceptionResolver的resolveException方法,發(fā)現(xiàn)是在父類實(shí)現(xiàn)的。下面直接貼代碼
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
@Override
@Nullable
public ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
//這里是再次判斷請求跟handler是否匹配。
if (shouldApplyTo(request, handler)) {
prepareResponse(ex, response);
//開始處理異常的方法。
ModelAndView result = doResolveException(request, response, handler, ex);
if (result != null) {
// Print warn message when warn logger is not enabled...
if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
}
// warnLogger with full stack trace (requires explicit config)
logException(ex, request);
}
return result;
}
else {
return null;
}
}
@Override
@Nullable
protected final ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
//開始處理異常。這個(gè)方法的話,是在子類ExceptionHandlerExceptionResolver實(shí)現(xiàn)的,接下來看代碼。
return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
}
}
看看具體實(shí)現(xiàn)
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
implements ApplicationContextAware, InitializingBean {
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
//找出這個(gè)出現(xiàn)異常的方法對應(yīng)的處理異常的方法。直接往里面走。
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
if (this.argumentResolvers != null) {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
if (logger.isDebugEnabled()) {
logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
}
Throwable cause = exception.getCause();
if (cause != null) {
// Expose cause as provided argument as well
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
}
else {
// Otherwise, just the given exception as-is
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
}
catch (Throwable invocationEx) {
// Any other than the original exception is unintended here,
// probably an accident (e.g. failed assertion or the like).
if (invocationEx != exception && logger.isWarnEnabled()) {
logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
}
// Continue with default processing of the original exception...
return null;
}
return mav;
}
}
//這個(gè)方法具體就是返回對應(yīng)的異常處理方法的。
@Nullable
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
@Nullable HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = null;
//這里其實(shí)就是controller里面的handlerMethod,當(dāng)然不為空
if (handlerMethod != null) {
//優(yōu)先從緩存中取
handlerType = handlerMethod.getBeanType();
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
//一開始肯定是取不到的,所以這個(gè)分支里面的代碼可以暫時(shí)不看
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
// For advice applicability check below (involving base packages, assignable types
// and annotation presence), use target class instead of interface-based proxy.
if (Proxy.isProxyClass(handlerType)) {
handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
}
}
//既然緩存中不存在,那么只能通過遍歷異常增強(qiáng)器緩存,來找到對應(yīng)的處理器了。下面我們來看看遍歷的過程
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
//判斷是否匹配
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
//這里面其實(shí)是通過處理方法的異常,是不是處理異常的父類,從而找出對應(yīng)的處理方法。同時(shí)將處理的異常類型,放入緩存中,對應(yīng)關(guān)系是異常類型->處理異常的方法。結(jié)果如下圖
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
}
}
}
return null;
}
}
這個(gè)緩存是在bean的后置處理器執(zhí)行的時(shí)候生成的。一開始我們的異常處理器就已經(jīng)被放到緩存中了


找到匹配的方法之后,最后不就是Invoke嗎?ExceptionHandlerExceptionResolver中的doResolveHandlerMethodException方法
/**
* Find an {@code @ExceptionHandler} method and invoke it to handle the raised exception.
*/
@Override
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
//找到異常對應(yīng)的處理
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
if (this.argumentResolvers != null) {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
if (logger.isDebugEnabled()) {
logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
}
Throwable cause = exception.getCause();
if (cause != null) {
// 通過反射,處理異常,并且放入視圖容器中
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
}
else {
// 通過反射,處理異常,并且放入視圖容器中
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
}
catch (Throwable invocationEx) {
// Any other than the original exception is unintended here,
// probably an accident (e.g. failed assertion or the like).
if (invocationEx != exception && logger.isWarnEnabled()) {
logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
}
// Continue with default processing of the original exception...
return null;
}
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}
else {
//將視圖容器中的模型,封裝到mav中取,結(jié)果可以看下圖
ModelMap model = mavContainer.getModel();
HttpStatus status = mavContainer.getStatus();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
}

至此,異常的處理便處理完畢了。