详解Spring boot/Spring 统一错误处理方案的使用

当我们开发spring web应用程序时,对于如 IOException , ClassNotFoundException 之类的检查异常,往往编译器会提示程序员采用 try-catch 进行显式捕获,而对于像 ClassCastException , NullPointerException 这类非检查异常,编译器是不会提示你了,这往往也是能体现程序员代码编写能力的一个方面。

在spring web特别是spring-boot应用中,当一个请求调用成功时,一般情况下会返回 json 格式的对象,就像下面图所示:

但如果请求抛出了一个 RuntimeException 呢?如果我们不做处理,再次调用时将出现下面的页面:

也就是说当调用出现错误时,spring-boot默认会将请求映射到 /error 路径中去,如果没有相应的路径请求处理器,那么就会返回上面的 Whitelabel 错误页面。

1、自定义错误处理页面

当然对运行时异常不做处理是不可能的啦!通常的做法是自定义统一错误页面,然后返回。按照上面的思路,我们实现一个请求路径为 /error 的控制器,控制器返回一个资源路径地址,定义请求映射路径为 /error 的控制器并实现 ErrorController 接口,代码如下:

MyErrorPageController

package com.example.demo.controller.handler.errorpage;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 *
 * The class MyErrorPageController.
 *
 * Description:自定义错误页面
 *
 * @author: huangjiawei
 * @since: 2018年6月13日
 * @version: $Revision$ $Date$ $LastChangedBy$
 *
 */
@Controller
public class MyErrorPageController implements ErrorController {

  @RequestMapping("/error")
  public String handleError() {
  	return "error.html"; // 该资源位于resources/static目录下
  }

  @Override
  public String getErrorPath() {
  	return null;
  }
}

然后在 reosurces/static 目录下建立 error.html 文件:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>这是个错误页面!存放在resources/static目录下,spring-boot发生错误时默认调用</h1>
</body>
</html>

再次请求 http://localhost:7000/demo/getUserInfoWithNoHandler.json ,如下:

2、使用 @ControllerAdvice@ResponseBody@ExceptionHandler 统一处理异常

在spring中可以使用上面3个注解进行统一异常处理,默认情况下我们可以针对系统中出现的某种类型的异常定义一个统一的处理器handler,比如说系统抛出了一个 NullPointerException ,那么我们可以定义一个专门针对 NullPointerException 的处理器,代码如下:

getUserInfoWithNullPointerException 接口

/**
 * 测试空指针错误的处理
 * @return
 * @throws NullPointerException
 */
@RequestMapping(value = "getUserInfoWithNullPointerException.json", method = RequestMethod.GET)
public Student getUserInfoWithNullPointerException() throws NullPointerException {
	throw new NullPointerException();
}

NullPointerExceptionHandler.java

package com.example.demo.controller.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.demo.pojo.ErrorReturn;

/**
 *
 * The class NullPointerExceptionHandler.
 *
 * Description:处理空指针
 *
 * @author: huangjiawei
 * @since: 2018年6月13日
 * @version: $Revision$ $Date$ $LastChangedBy$
 *
 */
@ControllerAdvice
public class NullPointerExceptionHandler {
  @ExceptionHandler(NullPointerException.class)
  @ResponseBody
  public ErrorReturn dealNullPointerException() {
    e.printStackTrace();
  	ErrorReturn error = new ErrorReturn();
  	error.setReturnCode("-1");
  	error.setDesc("出现空指针异常啦!");
  	return error;
  }
}

浏览器执行: http://localhost:7000/demo/getUserInfoWithNullPointerException.json

同样的道理,如果我们还需要为其他的运行时异常提供统一的处理器,那么也可以像上面一样为每一个异常类型定义一个处理器,比如我们又想为 ArithmeticException 定义处理器,那么我们只需要建立一个类或者方法,然后在方法上的 @ExceptionHanler 注解内加上 ArithmeticException.class 指定异常类型即可。

不过你有没有发现,这样为每种异常类型定义一个异常处理类或者方法,因为运行时异常类型特别多,不可能为每种类型都指定一个处理器类或方法,针对这种情况,spring也是可以解决的。如果我们没有为某种特定类型异常,如 ArithmeticException 定义处理器,那么我们可以定义一个 Exception 或者 Throwable 处理器统一处理。

这样做的好处是,减少了处理器类的数量,同时将异常处理转移到父类上面去,这也是继承的一大优势吧!但是,当你既定义了特定类型的异常,同时又定义了 Exception 异常的处理器,那么要小心了,这里不一定有优先级的关系,也就是说不一定会出现只执行父异常处理器的情况,可能是只执行A处理器,而不执行B处理器或者只执行B处理器,不执行A处理器。如 NullPointerExceptionHandler 异常会向 Exception 异常传递(但 ArithmeticException 不会向 Exception 传递)

现在假设我们既定义上面的 NullPointerExceptionHandler ,又定义了下面的 ExceptionThrowableHandler ,那么当发生 NullPointerException 时,就会默认执行 ExceptionThrowableHandler 的方法。

ExceptionThrowableHandler.java

package com.example.demo.controller.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.demo.pojo.ErrorReturn;

/**
 *
 * The class ExceptionThrowableHandler.
 *
 * Description:有些异常会向高级别异常传递(但ArithmeticException不会向Exception传送)
 *
 * @author: huangjiawei
 * @since: 2018年6月13日
 * @version: $Revision$ $Date$ $LastChangedBy$
 *
 */
@ControllerAdvice
public class ExceptionThrowableHandler {

  @ExceptionHandler(Throwable.class)
  @ResponseBody
  public ErrorReturn dealThrowable() {
  	ErrorReturn error = new ErrorReturn();
  	error.setDesc("处理Throwable!");
  	error.setReturnCode("-1");
  	return error;
  }

  @ExceptionHandler(Exception.class)
  @ResponseBody
  public ErrorReturn dealCommonException() {
  	ErrorReturn error = new ErrorReturn();
  	error.setReturnCode("-1");
  	error.setDesc("公共异常处理!");
  	return error;
  }
}

浏览器执行 : http://localhost:7000/demo/getUserInfoWithNullPointerException.json

可以发现只执行 Exception 的处理器,没有执行空指针的处理器,也就是异常处理往上传送了。下面再来看看抛出 ArithmeticException 的情况:

getUserInfoWithArithmeticException.json

/**
 * 测试空指针错误的处理
 * @return
 * @throws NullPointerException
 */
@RequestMapping(value = "getUserInfoWithArithmeticException.json", method = RequestMethod.GET)
public Student getUserInfoWithArithmeticException() throws ArithmeticException {
	throw new ArithmeticException();
}

ArithmeticExceptionHandler.java

package com.example.demo.controller.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.demo.pojo.ErrorReturn;

@ControllerAdvice
public class ArithmeticExceptionHandler {
  /**
   * 处理ArithmeticException异常
   * @return
   */
  @ResponseBody
  @ExceptionHandler(ArithmeticException.class)
  public ErrorReturn dealArithmeticException() {
  	ErrorReturn errorObject = new ErrorReturn();
  	errorObject.setReturnCode("-1");
  	errorObject.setDesc("算数处理出现异常!");
  	return errorObject;
  }
}

浏览器执行 : http://localhost:7000/demo/getUserInfoWithArithmeticException.json

结果发现异常处理并没有往上层的 ExceptionHandler 传送。

总结:对于既定义特定类型的处理器,又定义 Exception 等父类型的处理器时要特别小心,并不是所有的异常都会往上级处理,如果我们想只减少处理器类的数量,不想为每种特定类型的处理器添加类或者方法,那么小编建议使用 instanceof 关键字对异常类型进行判断即可。

如下面的代码,我们只建立一个公共的异常处理器,处理 Exception 异常,同时使用 instanceof 进行判断。

@ExceptionHandler(Exception.class)
@ResponseBody
public ErrorReturn dealCommonException(Exception e) {
  ErrorReturn error = new ErrorReturn();
  // 此处可以采用 instanceof 判断异常类型
  if (e instanceof ArithmeticException) {
  	error.setReturnCode("-1");
  	error.setDesc("算数异常处理!");
  	return error;
  }
  System.err.println("exception");
  error.setReturnCode("-1");
  error.setDesc("公共异常处理!");
  return error;
}

浏览器执行抛出 ArithmeticException 的接口,如下:

本文代码地址: https://github.com/SmallerCoder/spring_exceptionHandler

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • spring boot启动时mybatis报循环依赖的错误(推荐)

    自己在做项目时,想使用热部署减少部署时间,于是添加了springboot-devtools 在maven中添加了依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> 然后正常的启动项目时发现控制台一直在不停的输出错误,错误如图 不明所以,然后就准备去调

  • SpringBoot集成Swagger2实现Restful(类型转换错误解决办法)

    pom.xml增加依赖包 <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <

  • SpringBoot拦截器实现对404和500等错误的拦截

    今天给大家介绍一下SpringBoot中拦截器的用法,相比Struts2中的拦截器,SpringBoot的拦截器就显得更加方便简单了. 只需要写几个实现类就可以轻轻松松实现拦截器的功能了,而且不需要配置任何多余的信息,对程序员来说简直是一种福利啊. 废话不多说,下面开始介绍拦截器的实现过程: 第一步:创建我们自己的拦截器类并实现 HandlerInterceptor 接口. package example.Interceptor; import javax.servlet.http.HttpSe

  • spring boot下 500 404 错误页面处理的方法

    spring boot 作为微服务的便捷框架,在错误页面处理上也有一些新的处理,不同于之前的spring mvc 500的页面处理是比较简单的,用java config或者xml的形式,定义如下的bean即可 <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"&

  • spring boot里增加表单验证hibernate-validator并在freemarker模板里显示错误信息(推荐)

    创建项目 使用IDEA创建一个spring-boot项目,依赖选上 web, validation, freemarker 即可 先看看效果 创建实体类 创建并加上注解,代码如下 public class Person implements Serializable { @NotNull @Length(min = 3, max = 10) // username长度在3-10之间 private String username; @NotNull @Min(18) // 年龄最小要18岁 pr

  • spring boot自定义404错误信息的方法示例

    前言 本文将给大家简单介绍一下,在springboot中怎么个性化404错误信息,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 返回json @Bean public EmbeddedServletContainerCustomizer containerCustomizer() { return new EmbeddedServletContainerCustomizer(){ @Override public void customize(ConfigurableEmbe

  • springboot 错误处理小结

    在 java web开发过程中,难免会有一些系统异常或人为产生一些异常.在 RESTful springboot 项目中如何优雅的处理? 分析:在RESTful 风格的springboot 项目中,返回的都是 body 对象,所以定义一个结果基类,其中包含 status,message,data(请求方法的返回结果),是比较合适的. 如果定义多个异常类进行处理,会比较麻烦.比如StudentNotExistsException.StudentExistsException...等,并且不能指定错

  • SpringBoot配置SwaggerUI访问404错误的解决方法

    SpringBoot 配置SwaggerUI 访问404的小坑. 在学习SpringBoot构建Restful API的时候遇到了一个小坑,配置Swagger UI的时候无法访问. 首先在自己的pom文件中加入Swagger的依赖,如下所示: <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version&

  • Spring Boot实现数据访问计数器方案详解

    目录 1.数据访问计数器 2.代码实现 2.1.方案说明 2.2.代码 2.3.调用 1.数据访问计数器   在Spring Boot项目中,有时需要数据访问计数器.大致有下列三种情形: 1)纯计数:如登录的密码错误计数,超过门限N次,则表示计数器满,此时可进行下一步处理,如锁定该账户. 2)时间滑动窗口:设窗口宽度为T,如果窗口中尾帧时间与首帧时间差大于T,则表示计数器满.   例如使用redis缓存时,使用key查询redis中数据,如果有此key数据,则返回对象数据:如无此key数据,则查

  • 详解快速搭建Spring Boot+Spring MVC

    Spring Boot的出现大大简化了Spring项目的初始搭建和开发过程,今天我们快速搭建一个带有页面渲染(themeleaf模板引擎)的Spring Boot环境. 一.首先我们在IDEA中创建一个Maven项目 勾选create from archetype,选择webapp 二.在pom文件中添加Spring Boot依赖和themeleaf依赖 <dependency> <groupId>org.springframework.boot</groupId> &

  • 详解JSP 中Spring工作原理及其作用

    详解JSP 中Spring工作原理及其作用 1.springmvc请所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责负责对请求进行真正的处理工作. 2.DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller. 3.DispatcherServlet请请求提交到目标Controller 4.Controller进行业务逻辑处理后,会返回一个ModelAndView 5.Dispathcher查询一个或多个

  • 详解java 中Spring jsonp 跨域请求的实例

    详解java 中Spring jsonp 跨域请求的实例 jsonp介绍 JSONP(JSON with Padding)是JSON的一种"使用模式",可用于解决主流浏览器的跨域数据访问的问题.由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的<script> 元素是一个例外.利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSO

  • Spring Boot全局统一异常处理器

    一.封装统一返回结果类 import com.jiusen.exercise.enums.ErrorEnum; import com.jiusen.exercise.exception.BusinessException; import lombok.Getter; import lombok.Setter; /** * @author: Lawson * @date: 2021/5/11 * @description: TODO 统一的返回结果 */ @Getter @Setter publi

  • 详解IDEA2020新建spring项目和c3p0连接池的创建和使用

    目录 前言 1.环境准备:maven配置 2.导入jar包:c3p0-0.9.5.4.jar和mysql-connector-java.jar 3.编写测试类测试连接 前言 C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展,目前使用它的开源项目有Hibernate,Spring等. 1.环境准备:maven配置 打开idea,点击"+"新建项目,选择Spring,点击next,填写项目的名称,点击finish,新的Spring项

  • 详解MySQL与Spring的自动提交(autocommit)

    1 MySQL的autocommit设置 MySQL默认是开启自动提交的,即每一条DML(增删改)语句都会被作为一个单独的事务进行隐式提交.如果修改为关闭状态,则执行DML语句之后要手动提交 才能生效. 查询当前会话的自动提交是否开启: mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit

  • Maven工程搭建spring boot+spring mvc+JPA的示例

    本文介绍了Maven工程搭建spring boot+spring mvc+JPA的示例,分享给大家,具体如下: 添加Spring boot支持,引入相关包: 1.maven工程,少不了pom.xml,spring boot的引入可参考官网: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId>

  • 详解SpringBoot如何实现统一后端返回格式

    目录 1.为什么要对SpringBoot返回统一的标准格式 1.1 返回String 1.2 返回自定义对象 1.3 接口异常 2.定义返回对象 3.定义状态码 4.统一返回格式 5.高级实现方式 5.1 ResponseBodyAdvice的源码 5.2 String类型判断 在前后端分离的项目中后端返回的格式一定要友好,不然会对前端的开发人员带来很多的工作量.那么SpringBoot如何做到统一的后端返回格式呢?今天我们一起来看看. 1.为什么要对SpringBoot返回统一的标准格式 在默

随机推荐