最近在做公司的网上商城 ,也在学习一套 商城的视频。提到了“全局异常处理”。这里我们转帖一篇,侵删。
-------------------------------------------------------------------------
异常,异常。我们一定要捕获一切该死的异常,宁可错杀一千也不能放过一个!产品上线后的异常更要命,一定要屏蔽错误内容,以免暴露敏感信息!在用Spring MVC开发WEB应用时捕获全局异常的方法基本有两种:
- WEB.XML,就是指定error-code和page到指定地址,这也是最传统和常见的做法
- 用Spring的全局异常捕获功能,这种相对可操作性更强一些,可根据自己的需要做一后善后处理,比如日志记录等。
SO,本文列出Spring-MVC做WEB开发时常用全局异常捕获的几种解决方案抛砖引玉,互相没有依赖,每个都可单独使用!
1、定义服务器错误WEB.XML整合Spring MVC
web.xml:
1 <error-page> 2 <error-code>404</error-code> 3 <location>/404</location> 4 </error-page> 5 <error-page> 6 <error-code>500</error-code> 7 <location>/500</location> 8 </error-page> 9 10 <!-- 未捕获的错误,同样可指定其它异常类,或自定义异常类 --> 11 <error-page> 12 <exception-type>java.lang.Exception</exception-type> 13 <location>/uncaughtException</location> 14 </error-page>
applicationContext.xml:
1 <!-- 错误路径和错误页面,注意指定viewResolver --> 2 <mvc:view-controller path="/404" view-name="404"/> 3 <mvc:view-controller path="/500" view-name="500"/> 4 <mvc:view-controller path="/uncaughtException" view-name="uncaughtException"/>
2、Spring全局异常,Controller增强方式( Advising Controllers)
异常抛出:
1 @Controller 2 public class MainController { 3 @ResponseBody 4 @RequestMapping("/") 5 public String main(){ 6 throw new NullPointerException("NullPointerException Test!"); 7 } 8 }
异常捕获:
1 //注意使用注解@ControllerAdvice作用域是全局Controller范围,即必须与抛出异常的method在同一个controller 2 //可应用到所有@RequestMapping类或方法上的@ExceptionHandler、@InitBinder、@ModelAttribute,在这里是@ExceptionHandler 3 @ControllerAdvice 4 public class AControllerAdvice { 5 @ExceptionHandler(NullPointerException.class) 6 @ResponseStatus(HttpStatus.BAD_REQUEST) 7 @ResponseBody 8 public String handleIOException(NullPointerException ex) { 9 return ClassUtils.getShortName(ex.getClass()) + ex.getMessage(); 10 } 11 }
为了确保@ResponseStatus标注的异常被Spring框架处理,可以这样编写全局异常处理类:
1 @ControllerAdvice 2 class GlobalDefaultExceptionHandler { 3 public static final String DEFAULT_ERROR_VIEW = "error"; 4 5 @ExceptionHandler(value = Exception.class) 6 public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { 7 // If the exception is annotated with @ResponseStatus rethrow it and let 8 // the framework handle it - like the OrderNotFoundException example 9 // at the start of this post. 10 // AnnotationUtils is a Spring Framework utility class. 11 if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) 12 throw e; 13 14 // Otherwise setup and send the user to a default error-view. 15 ModelAndView mav = new ModelAndView(); 16 mav.addObject("exception", e); 17 mav.addObject("url", req.getRequestURL()); 18 mav.setViewName(DEFAULT_ERROR_VIEW); 19 return mav; 20 } 21 }
3、Spirng全局异常,配置方式
异常抛出,同上!
异常捕获:
1 <!-- 全局异常配置 --> 2 <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> 3 <property name="exceptionMappings"> 4 <props> 5 <prop key="java.lang.Exception">errors/500</prop> 6 <prop key="java.lang.Throwable">errors/500</prop> 7 </props> 8 </property> 9 <property name="statusCodes"> 10 <props> 11 <prop key="errors/500">500</prop> 12 </props> 13 </property> 14 <!-- 设置日志输出级别,不定义则默认不输出警告等错误日志信息 --> 15 <property name="warnLogCategory" value="WARN"></property> 16 <!-- 默认错误页面,当找不到上面mappings中指定的异常对应视图时,使用本默认配置 --> 17 <property name="defaultErrorView" value="errors/500"></property> 18 <!-- 默认HTTP状态码 --> 19 <property name="defaultStatusCode" value="500"></property> 20 </bean>
对应500错误的view jsp页面:
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 2 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 4 <html> 5 <head> 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <title>500 Error</title> 8 </head> 9 <body> 10 <% Exception ex = (Exception)request.getAttribute("exception"); %> 11 <H2>Exception: <%= ex.getMessage()%></H2> 12 <P/> 13 <% ex.printStackTrace(new java.io.PrintWriter(out)); %> 14 </body> 15 </html>
4、Sping全局异常,自定义异常类和异常解析
自定义异常类:
1 public class CustomException extends RuntimeException { 2 3 public CustomException(){ 4 super(); 5 } 6 7 public CustomException(String msg, Throwable cause){ 8 super(msg, cause); 9 //Do something... 10 } 11 }
抛出异常:
1 @ResponseBody 2 @RequestMapping("/ce") 3 public String ce(CustomException e){ 4 throw new CustomException("msg",e); 5 }
实现异常捕获接口HandlerExceptionResolver:
1 public class CustomHandlerExceptionResolver implements HandlerExceptionResolver{ 2 3 @Override 4 public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { 5 Map<String, Object> model = new HashMap<String, Object>(); 6 model.put("e", e); 7 //这里可根据不同异常引起类做不同处理方式,本例做不同返回页面。 8 String viewName = ClassUtils.getShortName(e.getClass()); 9 return new ModelAndView(viewName, model); 10 } 11 }
新的的HandlerExceptionResolver实现类只需在配置文件中定义即可,可以配置优先级。DispatcherServlet初始化HandlerExceptionResolver的时候会自动寻找容器中实现了HandlerExceptionResolver接口的类,然后添加进来。配置Spring支持异常捕获:
1 <bean class="cn.bg.controller.CustomHandlerExceptionResolver"/>
5、Errors and REST
使用Restful的Controller可以使用@ResponseBody处理错误,首先定义一个错误:
1 public class ErrorInfo { 2 public final String url; 3 public final String ex; 4 5 public ErrorInfo(String url, Exception ex) { 6 this.url = url; 7 this.ex = ex.getLocalizedMessage(); 8 } 9 }
通过一个@ResponseBody返回一个错误实例:
1 @ResponseStatus(HttpStatus.BAD_REQUEST) 2 @ExceptionHandler(MyBadDataException.class) 3 @ResponseBody ErrorInfo handleBadRequest(HttpServletRequest req, Exception ex) { 4 return new ErrorInfo(req.getRequestURL(), ex); 5 }