需求:
需要将项目里自定义的拦截器中抛出的错误信息按照一定的规则返回到不同界面进行展示。
分析:
自定义的拦截器属于HandlerInterceptor组件,用于拦截HandlerAdapter调用Handler(Controller类对象、方法对象)的过程。因为DispatcherServlet会处理此过程中产生的异常,所以可以通过扩展spring的HandlerExceptionResolver来满足需求。
DispatcherServlet.java部分源代码:
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;
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// 调用HandlerAdapter实例的handle方法
//如果自定义的HandlerInterceptorAdapter中报错,则此方法也会报错,错误统一在下面的processDispatchResult方法中处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//处理结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
....此处省略
}
处理正常返回的ModelAndView或者将异常被解析到ModelAndView中去
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
//处理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);
}
}
....此处省略
}
processHandlerException方法部分代码:
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
//在这里可通过扩展handlerExceptionResolver来进行自定义异常处理
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
......此处省略
}
以下为扩展的示例:
public class TestHandlerExceptionResolver implements HandlerExceptionResolver {
/** 普通日志 */
private static final Logger LOGGER = LoggerFactory
.getLogger(TestHandlerExceptionResolver.class);
/** 默认错误展示页面 */
private final String defaultErrorPage = "error.vm";
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
ModelAndView mv = new ModelAndView(defaultErrorPage);
if (ex instanceof DuplicateFormSubmitException) {
ExceptionUtil.caught(ex, "请求处理过程中表单重复提交");
mv.getModel().put(MVCCommonConstant.VALUE_RETURN_STAT_FAIL, "请求处理过程中表单重复提交");
} else if (ex instanceof AlipaySecurityException) {
ExceptionUtil.caught(ex, "您无权访问此数据或功能!");
mv.getModel().put(MVCCommonConstant.VALUE_RETURN_STAT_FAIL, "您无权访问此数据或功能!");
} else if (ex instanceof InvalidUploadFileException) {
ExceptionUtil.caught(ex, "文件上传失败");
mv.getModel().put(MVCCommonConstant.VALUE_RETURN_STAT_FAIL,
"请确认您的附件是否是xlsx,xls,txt格式," + "并且大小不能超过3M,否则不允许上传!");
} else if (ex instanceof IcfmngFileDownloadException) {
mv.getModel().put(MVCCommonConstant.VALUE_RETURN_STAT_FAIL, ex.getMessage());
} else if (ex instanceof ReadFileException) {
LOGGER.error("读取文件异常", ex);
mv.getModel().put(MVCCommonConstant.VALUE_RETURN_STAT_FAIL, "读取文件异常。");
} else {
LOGGER.error("请求处理过程中发生异常!", ex);
mv.getModel().put(MVCCommonConstant.VALUE_RETURN_STAT_FAIL, "系统发生未知异常,请联系工程师处理。");
}
return mv;
}
}
通过扩展HandlerExceptionResolver的方式返回的页面会优先于web.xml中配置的错误界面。