Spring Boot系列十 Spring MVC全局异常处理总结
来源:互联网 发布:淘宝发物流怎么填单号 编辑:程序博客网 时间:2024/06/16 13:00
1. 概述
SpringMVC 提供的异常处理主要有两种方式:
- 一种是直接实现自己的HandlerExceptionResolver
- 一种是使用注解
通过注解的方式实现处理异常主要有以下两种方式:
- 1 @ControllerAdvice+@ExceptionHandler:配置对全局异常进行处理
- 2 @Controller + @ExceptionHandler:配置对当前所在Controller的异常进行处理
在SpringMVC中,处理异常类实际上是HandlerExceptionResolver子类。HandlerExceptionResolver处理所有controller类在执行过程中抛出的未被处理的异常。
本文演示如何使用以上多种处理异常的方式,最后演示以不同的方式将异常结果返回给调用者
- 返回 modelAndView
- 返回一个页面的地址
- 返回 JSON
- 返回 http 错误码
2. 演示工程mvc
所有代码都中mvc工程中
3.1. 基础类和JSP页面
辅助类
EmployeeEx:POJO类
ExceptionJSONInfo:封装返回的JSON对象
public class EmployeeEx { private String name; private int id; …}public class ExceptionJSONInfo { private String url; private String message; …}
定义测试的异常类
EmployeeExNotFoundException
public class EmployeeExNotFoundException extends Exception { private static final long serialVersionUID = -3332292346834265371L; public EmployeeExNotFoundException(int id){ super("EmployeeNotFoundException with id="+id); }}
EmployeeExJsonException
public class EmployeeExJsonException extends Exception { private static final long serialVersionUID = -3332292346834265371L; public EmployeeExJsonException(int id){ super("EmployeeExJsonException with id="+id); }}
JSP页面
本demo使用的jsp都在此“META-INF.resources.WEB-INF.page.exceptionhandling”目录下,由于代码简单,这里一一列出。
3. 通过注解的方式捕获异常
本节演示两种通过注解捕获异常的方式
- @Controller + @ExceptionHandler
- @ControllerAdvice + @ExceptionHandler
3.1. @Controller + @ExceptionHandler
@Controller:注解此类是Controller类
@ExceptionHandler:此注解注解到类的方法上,当此注解里定义的异常抛出时,此方法会被执行。如果@ExceptionHandler所在的类是@Controller,则此方法只作用在此类。如果@ExceptionHandler所在的类是@ControllerAdvice,则此方法会作用在全局。本节演示前种用法
下面演示这种用法:
EmployeeExController:
- 在@Controller里定义的@ExceptionHandler只截获所在类抛出的异常
- handleEmployeeNotFoundException()通过@ExceptionHandler定义要捕获的异常是EmployeeExNotFoundException,此方法会转到exceptionhandling/error页面,此页面会打印错误信息
@Controllerpublic class EmployeeExController { private static final Logger logger = LoggerFactory.getLogger(EmployeeExController.class); @RequestMapping(value="/emp/{id}", method=RequestMethod.GET) public String getEmployee(@PathVariable("id") int id, Model model) throws Exception{ if(id==1){ throw new EmployeeExNotFoundException(id); }else if(id==2){ … } /** * 此方法只处理本类抛出的 EmployeeNotFoundException 异常 * @param request * @param ex * @return */ @ExceptionHandler(EmployeeExNotFoundException.class) public ModelAndView handleEmployeeNotFoundException(HttpServletRequest request, Exception ex){ logger.error("Requested URL="+request.getRequestURL()); logger.error("Exception Raised="+ex); ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("exception", ex); modelAndView.addObject("url", request.getRequestURL()); // 转到error.jsp modelAndView.setViewName("exceptionhandling/error"); return modelAndView; } }
测试
执行URL:
http://127.0.0.1:8080/emp/1
输出
3.2. @ControllerAdvice + @ExceptionHandler
@ControllerAdvice:定义全局异常处理的类,默认情况下它会监控所有的@RequestMapping方法抛出的异常。不过我们也可以对指定过滤的条件
EmployeeExController
此方法中抛出SQLException
@Controllerpublic class EmployeeExController { @RequestMapping(value="/emp/{id}", method=RequestMethod.GET) public String getEmployee(@PathVariable("id") int id, Model model) throws Exception{ if(id==1){ …. }else if(id==2){ throw new SQLException("SQLException, id="+id); }else if(id==3){ … }
GlobalExceptionHandler
@ControllerAdvice:通过@ExceptionHandler方法捕获特定异常。默认情况下@ControllerAdvice监控所有的@RequestMapping方法,也可以对指定过滤的条件。下面的方法会捕获所有抛出SQLException的@RequestMapping方法
/** * 全局异常处理异常 * @ControllerAdvice * */@ControllerAdvicepublic class GlobalExceptionHandler { @ExceptionHandler(SQLException.class) public String handleSQLException(HttpServletRequest request, Exception ex) { logger.info("SQLException Occured:: URL=" + request.getRequestURL()); return "exceptionhandling/database_error"; } ….}
@ControllerAdvice默认监控所有的@RequestMapping方法,也可以对指定过滤的条件:
// 监控所有的被@RestController注解的Controllers类 @ControllerAdvice(annotations = RestController.class)public class AnnotationAdvice {}// 监控特定的包下的Controllers类@ControllerAdvice("org.example.controllers")public class BasePackageAdvice {}// 监控指定类的Controllers类@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})public class AssignableTypesAdvice {}
测试
执行URL:
http://127.0.0.1:8080/emp/2
输出
4. 通过HandlerExceptionResolver的方式捕获
在接口HandlerExceptionResolver,我们可以自定义实现全局异常捕获
4.1. 系统默认实现的HandlerExceptionResolver
以下是系统默认加载到spring mvc容器中的HandlerExceptionResolver
- ExceptionHandlerExceptionResolver: 根据@ExceptionHandler注解的方法处理对应的异常。其实上文的通过注解的方式处理异常,实际就是在这个类中实现
- ResponseStatusExceptionResolver: 根据@ResponseStatus注解的方法处理异常
- DefaultHandlerExceptionResolver: 将异常转化为特定的HTTP的状态码
- HandlerExceptionResolverComposite:此类通过列表包含以上3个HandlerExceptionResolver,当捕获异常时,会循环调用以上3个HandlerExceptionResolver进行处理
4.2. 实现自己的HandlerExceptionResolver
此类只演示HandlerExceptionResolver的用法,只打印错误信息,不进行任何处理。通过接口Ordered的getOrder定义此HandlerExceptionResolver的优先级,优先级最高
@Component // 需要带上此注解public class MySimpleHandlerExceptionResolverimplements HandlerExceptionResolver,Ordered{ private static final Logger logger = LoggerFactory.getLogger(MySimpleMappingExceptionResolver.class); @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { logger.info("url = {}, exception message = {}", request.getRequestURI(), ex.getMessage()); // 返回null,让后面HandlerExceptionResolver继续进行处理;如果不让后面的HandlerExceptionResolver进行处理,则这里返回一个ModelAndView对象即可 return null; } public int getOrder(){ // 表示此HandlerExceptionResolver的优先级最高 return Ordered.HIGHEST_PRECEDENCE; }}
4.3. 测试
由于此andlerExceptionResolver的优先级最高,所以所有抛出异常被会被此类捕获,如:请求 http://127.0.0.1:8080/emp/2
后台有打印信息,说明此配置生效了
2017-12-20 11:39:03.145 [http-nio-8080-exec-1] INFO c.h.s.m.e.MySimpleHandlerExceptionResolver - url = /emp/2, exception message = SQLException, id=2
5. 对异常进行处理
捕获异常后,我们可以返回所有的spring mvc能够返回的所有的类型,这里只演示以下几种:
- 返回 modelAndView
- 返回一个页面的地址
- 返回 JSON
- 返回 http 错误码
5.1. 返回 modelAndView
上面的 “@Controller + @ExceptionHandler” 演示这个方式的用法
5.2. 返回一个页面的地址
上面的 “@ControllerAdvice + @ExceptionHandler” 演示这个方式的用法
5.3. 返回 JSON
EmployeeExController
@Controllerpublic class EmployeeExController { @RequestMapping(value = "/emp/{id}", method = RequestMethod.GET) public String getEmployee(@PathVariable("id") int id, Model model) throws Exception { .. } else if (id == 4) { throw new EmployeeExJsonException(id); } else if (id == 10) { …}
GlobalExceptionHandler
@ResponseBody:截获异常,生成对象并通过此注解生成json,返回给客户端
@ControllerAdvicepublic class GlobalExceptionHandler { @ExceptionHandler(EmployeeExJsonException.class) public @ResponseBody ExceptionJSONInfo handleEmployeeNotFoundException(HttpServletRequest request, Exception ex) { ExceptionJSONInfo response = new ExceptionJSONInfo(); response.setUrl(request.getRequestURL().toString()); response.setMessage(ex.getMessage()); return response; }}
测试
执行 http://127.0.0.1:8080/emp/4
返回
5.4. 返回 http 错误码
如果发生IOException异常,则作为返回http错误代码为404。
EmployeeExController
@Controllerpublic class EmployeeExController { @RequestMapping(value="/emp/{id}", method=RequestMethod.GET) public String getEmployee(@PathVariable("id") int id, Model model) throws Exception{ … }else if(id==3){ throw new IOException("IOException, id="+id); }else if(id==10){ … }}
GlobalExceptionHandler :
如果发生IOException异常,则作为返回http错误代码为404
这里写代码片
@ControllerAdvicepublic class GlobalExceptionHandler { /** * For IOException, we are returning void with status code as 404, * so our error-page will be used in this case. * */ @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "IOException occured") @ExceptionHandler(IOException.class) public void handleIOException() { logger.error("IOException handler executed"); // returning 404 error code }}
测试
执行 http://127.0.0.1:8080/emp/3
6. 代码
上文的详细代码见github代码,请尽量使用tag v0.5,不要使用master,因为master一直在变,不能保证文章中代码和github上的代码一直相同
- Spring Boot系列十 Spring MVC全局异常处理总结
- Spring boot 全局异常处理
- spring boot全局异常处理
- Spring mvc 全局异常处理
- Spring MVC全局异常处理
- spring mvc 全局异常处理
- spring boot 异常处理全局拦截
- Spring boot 七 全局异常处理
- spring boot 全局异常
- Spring MVC全局的异常处理方式
- Spring 全局异常处理
- spring boot 全局异常捕获
- spring boot全局异常捕捉
- spring boot 全局异常捕捉
- Spring Boot & Spring MVC 异常处理的N种方法
- Spring Boot & Spring MVC 异常处理的 N 种方法
- spring boot 学习--03---web控制层全局异常处理
- Spring Boot 菜鸟教程 6 全局异常处理
- Unity3D Shader之路 VS2015下Graphics Debugger调试Shader
- 欢迎使用Markdown编辑器写博客
- C#数据结构中的二叉树
- 函数、函数的递归、重载、委托的声明
- hive中的内部表、外部表、分区表
- Spring Boot系列十 Spring MVC全局异常处理总结
- 深入Spring Boot:排查@Transactional引起的NullPointerException
- 深入理解Java虚拟机-垃圾收集器与内存分配策略(三)
- org.apache.maven.archiver.MavenArchiver.getManifest错误
- 浏览器报错:ReferenceError: require is not defined
- 《软件测试的艺术(原书第2版)》【PDF】下载
- SQL语句删除单表中的重复数据
- 【CVPR2017论文】Loss Max-Pooling for Semantic Image Segmentation
- LTspice Tutorials