先放实体类,通用的响应体
@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;
}
}
响应枚举,记录各个状态下的信息
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, "参数存在错误"),
SERVICE_ERROR(SERVICE_ERROR_CODE, "处理业务的过程中出现异常");
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 + "]";
}
}
}
生成响应体的工具
public class CommonUtils {
//生成通用响应体
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()));
}
}
随后我们自己随便定义一个接口,抛出异常,发现最后返回的响应是我们自己定义的。
那么spring究竟是怎么实现的呢。
那么首先来看看一个接口。HandlerExceptionResolver ,这个接口是异常处理解析器的接口,里面只提供了一个方法,就是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);
}
那么接下来,看看这个接口的对应实现,有好几层,其中AbstractHandlerExceptionResolver是核心。实现了大部分的功能。而AbstractHandlerMethodExceptionResolver则是AbstractHandlerExceptionResolver的子类,专门用于处理方法的异常。
ExceptionHandlerExceptionResolver->AbstractHandlerMethodExceptionResolver->
AbstractHandlerExceptionResolver->HandlerExceptionResolver
通过debug。ExceptionHandlerExceptionResolver的initExceptionHandlerAdviceCache方法,会将spring中被ControllerAdviceBean注释所修饰的bean给找出来,并且存入缓存中
private void initExceptionHandlerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
//找出容器中被@ControllerAdviceBean注解修饰的bean,并且进行排序
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) {
//出现异常,则先捕获
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) {
//异常必然不是这个类型的。因此走另外一个分支
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);
}
}
。。。下面还有一段代码。但是对分析这个过程来说意义不大,所以直接忽略
}
@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是什么东西呢?我们通过断点来看看,如下图所示
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
//其实下面还有一段。不过感觉一个是看不懂二是意义不大,直接忽略
}
dispatcher中的handlerExceptionResolvers,有2个对象分别是DefaultErrorAttributes以及HandlerExceptionResolverComposite,通过debug发现,HandlerExceptionResolverComposite类型才是关键
然后我们来看看HandlerExceptionResolverComposite的resolveException方法。其实通过名字都知道这个类是一个复合的异常处理解析器。其中有一个成员变量用于存储异常解析器。
public class HandlerExceptionResolverComposite implements HandlerExceptionResolver, Ordered {
//用于存储异常解析器
@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
//遍历处理异常,若异常处理器匹配,则直接返回处理后的结构,总共有3个对象,如下图所示
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;
}
}
复合异常处理器所存储的异常解析器类型。这里我们只需要关注ExceptionHandlerExceptionResolver即可。
下面来看看ExceptionHandlerExceptionResolver的resolveException方法,发现是在父类实现的。下面直接贴代码
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) {
//开始处理异常。这个方法的话,是在子类ExceptionHandlerExceptionResolver实现的,接下来看代码。
return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
}
}
看看具体实现
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
implements ApplicationContextAware, InitializingBean {
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
//找出这个出现异常的方法对应的处理异常的方法。直接往里面走。
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;
}
}
//这个方法具体就是返回对应的异常处理方法的。
@Nullable
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
@Nullable HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = null;
//这里其实就是controller里面的handlerMethod,当然不为空
if (handlerMethod != null) {
//优先从缓存中取
handlerType = handlerMethod.getBeanType();
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);
}
// 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());
}
}
//既然缓存中不存在,那么只能通过遍历异常增强器缓存,来找到对应的处理器了。下面我们来看看遍历的过程
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;
}
}
这个缓存是在bean的后置处理器执行的时候生成的。一开始我们的异常处理器就已经被放到缓存中了
找到匹配的方法之后,最后不就是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) {
//找到异常对应的处理
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中取,结果可以看下图
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;
}
}
至此,异常的处理便处理完毕了。