SpringMVC 提供了 3 种异常处理方法:
- 使用 @ExceptionHandler 注解实现异常处理
- 简单异常处理器 SimpleMappingExceptionResolver
- 实现异常处理接口 HandlerExceptionResolver,自定义异常处理器
通过比较,实现异常处理接口 HandlerExceptionResolver 是最合适的,可以给定更多的异常信息,可一自定义异常显示页面等,下面介绍使用这种方式处理异常。
1. 定义自己的异常
实现自定义异常是为了能更灵活的添加一些默认异常没有的功能,例如制定异常显示的页面,如果需要更多功能,自行扩展。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.xtuer.exception;
public class ApplicationException extends RuntimeException { private String errorViewName = null;
public ApplicationException(String message) { this(message, null); }
public ApplicationException(String message, String errorViewName) { super(message); this.errorViewName = errorViewName; }
public String getErrorViewName() { return errorViewName; } }
|
2. 异常处理类
异常没有被我们捕获,被抛给 Spring 后,Spring 会调用此异常处理类把异常默认显示在 error.htm,errorViewName 可以在上面的 ApplicationException 里定义,如果是 AJAX 访问,异常信息则以 AJAX 的方式返回给客户端,而不是显示在错误页面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| package com.xtuer.exception;
import com.alibaba.fastjson.JSON; import com.xtuer.bean.Result; import com.xtuer.controller.UriView; import com.xtuer.util.NetUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ui.ModelMap; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public final class XHandlerExceptionResolver implements HandlerExceptionResolver { private static Logger logger = LoggerFactory.getLogger(XHandlerExceptionResolver.class);
@Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { String error = ex.getMessage(); String stack = ExceptionUtils.getStackTrace(ex);
logger.warn(error); logger.warn(stack);
return NetUtils.useAjax(request) ? handleAjaxException(response, error, stack) : handleNonAjaxException(ex, error, stack); }
private ModelAndView handleAjaxException(HttpServletResponse response, String error, String stack) { Result<String> result = Result.fail(error, stack); NetUtils.ajaxResponse(response, JSON.toJSONString(result)); return null; }
private ModelAndView handleNonAjaxException(Exception ex, String error, String stack) { String errorViewName = "error.htm";
if (ex instanceof ApplicationException) { ApplicationException appEx = (ApplicationException) ex; errorViewName = (appEx.getErrorViewName() == null) ? errorViewName : appEx.getErrorViewName(); }
ModelMap model = new ModelMap(); model.addAttribute("error", error); model.addAttribute("detail", stack);
return new ModelAndView(errorViewName, model); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| package com.xtuer.util;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter;
public class NetUtils { private static Logger logger = LoggerFactory.getLogger(NetUtils.class);
public static boolean useAjax(HttpServletRequest request) { return "XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With")); }
public static void ajaxResponse(HttpServletResponse response, String data) { response.setContentType("application/json"); response.setCharacterEncoding("UTF-8");
try { PrintWriter writer = response.getWriter(); writer.write(data); writer.flush(); writer.close(); } catch (IOException ex) { logger.warn(ExceptionUtils.getStackTrace(ex)); } } }
|
3. 在 spring-mvc.xml 里注册异常处理器
1 2 3 4 5 6 7 8 9
| <bean id="compositeExceptionResolver" class="org.springframework.web.servlet.handler.HandlerExceptionResolverComposite"> <property name="exceptionResolvers"> <list> <bean class="com.xtuer.exception.XHandlerExceptionResolver"/> </list> </property> <property name="order" value="0"/> </bean>
|
<bean class="com.xtuer.exception.XHandlerExceptionResolver"/>
注册的异常处理器只能处理进入 Controller 中的方法体里抛出的异常,例如缺少参数,参数类型转换错误时发生异常则不会进入 Controller 的方法,这时抛出的异常则不能被这种方式注册的异常处理器处理。
如果需要所有的异常都能被我们的异常处理器处理,则需要按上面的方法使用 HandlerExceptionResolverComposite 来注册我们的异常处理器,并且 order 的值要足够小,order 越小优先级越高,因为 SpringMVC 有一个异常处理器链。
4. 测试用的 Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Controller public class DemoController { @GetMapping("/exception") public String exception() { throw new RuntimeException("普通访问发生异常"); }
@GetMapping("/exception-ajax") @ResponseBody public Result exceptionWhenAjax(Demo demo) { System.out.println(JSON.toJSONString(demo)); throw new RuntimeException("AJAX 访问发生异常"); } }
|
5. 显示错误的页面 error.htm
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>错误</title> </head> <body> 错误: ${error!}<br>
<#if detail??> <pre>错误细节:<br>${detail}</pre> </#if> </body> </html>
|
6. 测试