最近学习Spring时,认识到Spring异常处理的强大。之前处理工程异常,代码中最常见的就是try-catch-finally,有时一个try,多个catch,覆盖了核心业务逻辑:

1 try{
2 ..........
3 }catch(Exception1 e){
4 ..........
5 }catch(Exception2 e){
6 ...........
7 }catch(Exception3 e){
8 ...........
9 }

Spring能够较好的处理这种问题,核心如下,文章主要关注前两个:

  • @ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度
  • @ControllerAdvice:异常集中处理,更好的使业务逻辑与异常处理剥离开
  • @ResponseStatus:可以将某种异常映射为HTTP状态码

@ExceptionHandler

源码如下:

1 @Target({ElementType.METHOD})
2 @Retention(RetentionPolicy.RUNTIME)
3 @Documented
4 public @interface ExceptionHandler {
5 Class<? extends Throwable>[] value() default {};
6 }

该注解作用对象为方法,并且在运行时有效,value()可以指定异常类。由该注解注释的方法可以具有灵活的输入参数(详细参见Spring API):

  • 异常参数:包括一般的异常或特定的异常(即自定义异常),如果注解没有指定异常类,会默认进行映射。
  • 请求或响应对象 (Servlet API or Portlet API): 你可以选择不同的类型,如ServletRequest/HttpServletRequest或PortleRequest/ActionRequest/RenderRequest
  • Session对象(Servlet API or Portlet API): HttpSession或PortletSession。
  • WebRequest或NativeWebRequest
  • Locale
  • InputStream/Reader
  • OutputStream/Writer
  • Model

方法返回值可以为:

  • ModelAndView对象
  • Model对象
  • Map对象
  • View对象
  • String对象
  • 还有@ResponseBody、HttpEntity<?>或ResponseEntity<?>,以及void

@ControllerAdvice

源码如下:

 1 @Target({ElementType.TYPE})
2 @Retention(RetentionPolicy.RUNTIME)
3 @Documented
4 @Component
5 public @interface ControllerAdvice {
6 @AliasFor("basePackages")
7 String[] value() default {};
8
9 @AliasFor("value")
10 String[] basePackages() default {};
11
12 Class<?>[] basePackageClasses() default {};
13
14 Class<?>[] assignableTypes() default {};
15
16 Class<? extends Annotation>[] annotations() default {};
17 }

该注解作用对象为TYPE,包括类、接口和枚举等,在运行时有效,并且可以通过Spring扫描为bean组件。其可以包含由@ExceptionHandler、@InitBinder 和@ModelAttribute标注的方法,可以处理多个Controller类,这样所有控制器的异常可以在一个地方进行处理。

实例

异常类:

 1 public class CustomGenericException extends RuntimeException{
2 private static final long serialVersionUID = 1L;
3
4 private String errCode;
5 private String errMsg;
6
7 public String getErrCode() {
8 return errCode;
9 }
10
11 public void setErrCode(String errCode) {
12 this.errCode = errCode;
13 }
14
15 public String getErrMsg() {
16 return errMsg;
17 }
18
19 public void setErrMsg(String errMsg) {
20 this.errMsg = errMsg;
21 }
22
23 public CustomGenericException(String errCode, String errMsg) {
24 this.errCode = errCode;
25 this.errMsg = errMsg;
26 }
27 }

控制器:

 1 @Controller
2 @RequestMapping("/exception")
3 public class ExceptionController {
4
5 @RequestMapping(value = "/{type}", method = RequestMethod.GET)
6 public ModelAndView getPages(@PathVariable(value = "type") String type) throws Exception{
7 if ("error".equals(type)) {
8 // 由handleCustomException处理
9 throw new CustomGenericException("E888", "This is custom message");
10 } else if ("io-error".equals(type)) {
11 // 由handleAllException处理
12 throw new IOException();
13 } else {
14 return new ModelAndView("index").addObject("msg", type);
15 }
16 }
17 }

异常处理类:

 1 @ControllerAdvice
2 public class ExceptionsHandler {
3
4 @ExceptionHandler(CustomGenericException.class)//可以直接写@ExceptionHandler,不指明异常类,会自动映射
5 public ModelAndView customGenericExceptionHnadler(CustomGenericException exception){ //还可以声明接收其他任意参数
6 ModelAndView modelAndView = new ModelAndView("generic_error");
7 modelAndView.addObject("errCode",exception.getErrCode());
8 modelAndView.addObject("errMsg",exception.getErrMsg());
9 return modelAndView;
10 }
11
12 @ExceptionHandler(Exception.class)//可以直接写@EceptionHandler,IOExeption继承于Exception
13 public ModelAndView allExceptionHandler(Exception exception){
14 ModelAndView modelAndView = new ModelAndView("generic_error");
15 modelAndView.addObject("errMsg", "this is Exception.class");
16 return modelAndView;
17 }
18 }

JSP页面:

正常页面index.jsp:

 1 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
2 <html>
3 <body>
4 <h2>Spring MVC @ExceptionHandler Example</h2>
5
6 <c:if test="${not empty msg}">
7 <h2>${msg}</h2>
8 </c:if>
9
10 </body>
11 </html>

异常处理页面generic_error.jsp

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body> <c:if test="${not empty errCode}">
<h1>${errCode} : System Errors</h1>
</c:if> <c:if test="${empty errCode}">
<h1>System Errors</h1>
</c:if> <c:if test="${not empty errMsg}">
<h2>${errMsg}</h2>
</c:if> </body>
</html>

测试运行如下:

正常情况:

CustomGenericException异常情况:

IOException异常情况:

总结

  • @ExceptionHandler和@ControllerAdvice能够集中异常,使异常处理与业务逻辑分离
  • 本文重点理解两种注解方式的使用

参考:

Spring全局异常处理的更多相关文章

  1. Spring 全局异常处理

    [参考文章]:Spring全局异常处理的三种方式 [参考文章]:Spring Boot 系列(八)@ControllerAdvice 拦截异常并统一处理 [参考文章]:@ControllerAdvic ...

  2. 《Spring全局异常处理》从零掌握@ControllerAdvice注解

    一.开门见山 在前后端分离框架的大趋势下,前后端基本的职责已经确定. 前端主要负责界面的处理以及基本的判空检验.数据来源则通过vue调用后端发布的接口. 后端的原型还是mvc的模式: controll ...

  3. Spring全局异常处理的三种方式

    在J2EE项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的.不可预知的异常需要处理.每个过程都单独处理异常,系统的代码耦合度高,工作 ...

  4. spring全局异常处理 自定义返回数据结构

    在写api接口中,正常返回和异常错误返回我们都希望很清楚的将这些信息清楚的返回给用户,出现异常情况下需要清楚的知道是参数异常还是未知异常,而不是返回一个不正确的数据结构. 所以此处只针对写api接口时 ...

  5. Spring MVC 解决无法访问静态文件和"全局异常处理"

    我们都知道,Spring MVC的请求都会去找controller控制器,若果我们页面中引入了一个外部样式,这样是没效果的, 我们引入样式的时候是通过<like href="...&q ...

  6. Spring Boot 2.x 系列教程:WebFlux REST API 全局异常处理 Error Handling

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 本文内容 为什么要全局异常处理? WebFlux REST 全 ...

  7. Spring Boot 全局异常处理

    Spring Boot版本 1.5 @ControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExcept ...

  8. Spring中通过java的@Valid注解和@ControllerAdvice实现全局异常处理。

    通过java原生的@Valid注解和spring的@ControllerAdvice和@ExceptionHandler实现全局异常处理的方法: controller中加入@Valid注解: @Req ...

  9. Spring Boot 2.X(十一):全局异常处理

    前言 在 Java Web 系统开发中,不管是 Controller 层.Service 层还是 Dao 层,都有可能抛出异常.如果在每个方法中加上各种 try catch 的异常处理代码,那样会使代 ...

随机推荐

  1. 02 - Unit06:弹出对话框

    弹出对话框 如何实现弹出 //弹出出对话框 $("#can").load("alert/alert_notebook.html"); //显示背景色 $(&qu ...

  2. hBuilder培训资源视频教程汇总

    DCloud对开发者的学习支持分3个层面:官方文档.三方专业培训.网友经验分享 DCloud的精力主要在做产品,配套的文档也会一直完善好.但专业的培训还不是DCloud能做好的,在HTML5中国产业联 ...

  3. MFC vs. SDK程序流程

    大家都知道,windows API编程以及其消息处理,其过程都清晰可见,大体步骤如下: 1)声明消息窗口类 2)注册窗口类 3)createwindows 4)消息获得以及分派(windows pro ...

  4. python3 关联规则Apriori代码模版

    #!/usr/bin/env python3 # -*- coding: utf-8 -*- from numpy import * def loadDataSet(): return [['a', ...

  5. java中的DAO设计模式

    创建数据库和表 sql语句: DROP TABLE IF EXISTS product; CREATE TABLE product( product_id varchar(20) NOT NULL, ...

  6. 第3课 QT的诞生和本质

    1. GUI用户界面元素 (1)操作系统提供了创建用户界面元素所需要的函数 (2)各种功能不同的函数依次调用,从而创建出界面元素 (3)操作系统提供的原生函数无法直接映射到界面元素 2. 面向对象的G ...

  7. 精《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #7 Cgroup、Namespace、Linux容器

    HACK #7 Cgroup.Namespace.Linux容器 本节将介绍Cgroup与Namespace以及通过这两个功能实现的容器功能.CgroupCgroup(control group)是将 ...

  8. python的with用法(参考)

    一.With语句是什么? 有一些任务,可能事先需要设置,事后做清理工作.对于这种场景,Python的with语句提供了一种非常方便的处理方式.一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中 ...

  9. Rhythmk 一步一步学 JAVA (20) JAVA enum常用方法

    JAVA 枚举定义常用方法: 1.static Enum valueOf(Class enum,String name) 返回指定name的枚举类型 2.Static Enum values[] 返回 ...

  10. javascript instanceof,typeof的区别

    区分string 与 String的区别 为什么结果会是false呢? <script type="text/javascript"> var aColors = [& ...