SpringBoot是为了简化Spring应用的创建、运行、调试、部署等一系列问题而诞生的产物,

自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖就可以轻易的搭建出一个 WEB 工程

实际项目开发中,程序往往会发生各式各样的异常情况,特别是身为服务端开发人员的我们,

总是不停的编写接口提供给前端调用,分工协作的情况下,避免不了异常的发生,如果直接将错误的信息直接暴露给用户,

这样的体验可想而知,且对黑客而言,详细异常信息往往会提供非常大的帮助…

初窥异常

一个简单的异常请求的接口

@GetMapping("/test1")
public String test1() {
// 这里只是模拟异常,假设业务处理的时候出现错误了,或者空指针了等等...
int i = 10 / 0;
return "test1";
}

打开浏览器访问它的时候发现

又或者是用postman等模拟工具

如果这接口是给第三方调用或者是自己公司的系统,看到这种错误估计得暴走吧….

笨方法(极其不建议)

采用try-catch的方式,手动捕获异常信息,然后返回对应的结果集,相信很多人都看到过类似的代码(如:封装成Result对象);

该方法虽然间接性的解决错误暴露的问题,同样的弊端也很明显,增加了大量的代码量,当异常过多的情况下对应的catch层愈发的多了起来,

很难管理这些业务异常和错误码之间的匹配,所以最好的方法就是通过简单配置全局掌控….

@GetMapping("/test2")
public Map<String, String> test2() {
Map<String, String> result = new HashMap<>(16);
// TODO 直接捕获所有代码块,然后在 cache
try {
int i = 10 / 0;
result.put("code", "200");
result.put("data", "具体返回的结果集");
} catch (Exception e) {
result.put("code", "500");
result.put("message", "请求错误");
}
return result;
}

具体代码

通过上面的阅读大家也大致能了解到为啥需要对异常进行全局捕获了,接下来就看看Spring Boot提供的解决方案

导入依赖

pom.xml中添加上spring-boot-starter-web的依赖即可

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

自定义异常

在应用开发过程中,除系统自身的异常外,不同业务场景中用到的异常也不一样,为了与标题轻松搞定全局异常更加的贴切,定义个自己的异常,看看如何捕获…

/**
* 自定义异常
*/
public class CustomException extends RuntimeException {
private static final long serialVersionUID = 4564124491192825748L; private int code; public CustomException() {
super();
}
public CustomException(int code, String message) {
super(message);
this.setCode(code);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}

异常信息模板

定义返回的异常信息的格式,这样异常信息风格更为统一

/**
* 异常信息模板
*/
public class ErrorResponseEntity { private int code;
private String message; public ErrorResponseEntity(int code, String message) {
this.code = code;
this.message = message;
}
// 省略 get/set
}

控制层

仔细一看是不是和平时正常写的代码没啥区别,不要急,接着看….

import com.winterchen.exception.CustomException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
public class ExceptionController { @GetMapping("/test3")
public String test3(Integer num) {
// TODO 演示需要,实际上参数是否为空通过 @RequestParam(required = true) 就可以控制
if (num == null) {
throw new CustomException(400, "num不能为空");
}
int i = 10 / num;
return "result:" + i;
}
}

异常处理(关键)

注解概述

  • @ControllerAdvice捕获Controller层抛出的<wiz_tmp_highlight_tag class="cm-searching">异常,如果添加@ResponseBody返回信息则为JSON格式。
  • @RestControllerAdvice相当于@ControllerAdvice@ResponseBody的结合体。
  • @ExceptionHandler统一处理一种类的<wiz_tmp_highlight_tag class="cm-searching">异常,减少代码重复率,降低复杂度。

创建一个GlobalExceptionHandler类,并添加上@RestControllerAdvice注解就可以定义出异常通知类了,然后在定义的方法中添加上@ExceptionHandler即可实现异常的捕捉…

import com.winterchen.exception.CustomException;
import com.winterchen.exception.ErrorResponseEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /**
* 全局异常处理
*/
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { /**
* 定义要捕获的异常 可以多个 @ExceptionHandler({})
* @param request request
* @param e exception
* @param response response
* @return 响应结果
*/
@ExceptionHandler(CustomException.class)
public ErrorResponseEntity customExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
CustomException exception = (CustomException) e;
return new ErrorResponseEntity(exception.getCode(), exception.getMessage());
} /**
* 捕获 RuntimeException 异常
* 如果你觉得在一个 exceptionHandler 通过 if (e instanceof xxxException) 太麻烦
* 那么你还可以自己写多个不同的 exceptionHandler 处理不同异常
* @param request request
* @param e exception
* @param response response
* @return 响应结果
*/
@ExceptionHandler(RuntimeException.class)
public ErrorResponseEntity runtimeExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
RuntimeException exception = (RuntimeException) e;
return new ErrorResponseEntity(400, exception.getMessage());
} /**
* 通用的接口映射异常处理方
*/
@Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
if (ex instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException exception = (MethodArgumentNotValidException) ex;
return new ResponseEntity<>(new ErrorResponseEntity(status.value(), exception.getBindingResult().getAllErrors().get(0).getDefaultMessage()), status);
}
if (ex instanceof MethodArgumentTypeMismatchException) {
MethodArgumentTypeMismatchException exception = (MethodArgumentTypeMismatchException) ex;
logger.error("参数转换失败,方法:" + exception.getParameter().getMethod().getName() + ",参数:" + exception.getName()
+ ",信息:" + exception.getLocalizedMessage());
return new ResponseEntity<>(new ErrorResponseEntity(status.value(), "参数转换失败"), status);
}
return new ResponseEntity<>(new ErrorResponseEntity(status.value(), "参数转换失败"), status);
}
}

主函数

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
public class SpringBootExceptionApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootExceptionApplication.class, args);
}
}

测试

完成准备事项后,启动Chapter17Application,通过下面的测试结果可以发现,真的是so easy,代码变得整洁了,扩展性也变好了…

访问http://localhost:8080/test3

{"code":400,"message":"num不能为空"}

访问http://localhost:8080/test3?num=0

{"code":400,"message":"/ by zero"}

访问http://localhost:8080/test3?num=5

result:2

转载链接:http://blog.battcn.com/2018/06/01/springboot/v2-other-exception/

SpringBoot-@ControllerAdvice 拦截异常并统一处理的更多相关文章

  1. @ControllerAdvice 拦截异常并统一处理(转载)

    在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder.@ModelAttribute,并应用到所有@Requ ...

  2. SpringBoot 之 @ControllerAdvice 拦截异常并统一处理

    在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder.@ModelAttribute,并应用到所有@Requ ...

  3. Spring Boot 系列(八)@ControllerAdvice 拦截异常并统一处理

    在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder.@ModelAttribute,并应用到所有@Requ ...

  4. @ControllerAdvice 拦截异常并统一处理

    在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder.@ModelAttribute,并应用到所有@Requ ...

  5. Spring Boot 系列 @ControllerAdvice 拦截异常并统一处理

    ControllerAdvice用法解析 简介 通过@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置. 注解了@Controller的类的方法可以使用@Exception ...

  6. SpringBoot - @ControllerAdvice 处理异常

    在Spring 3.2中,新增了@ControllerAdvice.@RestControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder.@Mode ...

  7. Spring-@ControllerAdvice 拦截异常并统一处理

    在spring 3.2中,新增了@ControllerAdvice 注解, 可以用于定义@ExceptionHandler.@InitBinder.@ModelAttribute,并应用到所有@Req ...

  8. @ControllerAdvice全局异常拦截

    @ControllerAdvice 拦截异常并统一处理 在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder ...

  9. (入门SpringBoot)SpringBoot配置全局异常(五)

    Spring的全局异常,用于捕获程序员没有捕获的异常.具体请看下面代码: 1.ControllerAdvice拦截异常,统一处理.通过Spring的AOP来管理. @ControllerAdvicep ...

随机推荐

  1. 10、Fiddler中设置断点修改Response

    当然Fiddler中也能修改Response 第一种:打开Fiddler 点击Rules-> Automatic Breakpoint  ->After Response  (这种方法会中 ...

  2. Java报错:java.math.BigDecimal cannot be cast to java.lang.String

    从数据库取数字,转为string,报错: java.math.BigDecimal cannot be cast to java.lang.String 错误代码 Integer.parseInt(( ...

  3. Python中字符的编码与解码

    1 文本和字节序列 我们都知道字符串,就是由一些字符组成的序列构成串,那么字符又是什么呢?计算机只能识别二进制的东西,那么计算机又为什么会显示我们的汉字,或者是某个字母呢? 由于最早发明使用计算机是美 ...

  4. MySql配置主从模式 Last_IO_Error: Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs; these UUIDs must be different for replication to work.

    今天在学习MyCat环境搭建的时候,在配置MySql的主从模式,发现slave在配置完毕后,配置的内容全部正确的情况下,报错了? Last_IO_Error: Fatal error: The sla ...

  5. 死磕 java同步系列之ReentrantLock VS synchronized——结果可能跟你想的不一样

    问题 (1)ReentrantLock有哪些优点? (2)ReentrantLock有哪些缺点? (3)ReentrantLock是否可以完全替代synchronized? 简介 synchroniz ...

  6. 从程序集加载类型,遇到 ReflectionTypeLoadException 的处理办法

    处理办法 catch ReflectionTypeLoadException ,然后从里面读取 Types 数据(成功加载的类型)就可以了. 参考 ReflectionTypeLoadExceptio ...

  7. Z从壹开始前后端分离【 .NET Core2.0/3.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    本文梯子 本文3.0版本文章 代码已上传Github+Gitee,文末有地址 大神反馈: 零.今天完成的深红色部分 一.AOP 之 实现日志记录(服务层) 1.定义服务接口与实现类 2.在API层中添 ...

  8. 浅谈Java面向对象思想

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  9. Latex学习笔记 第一章

    1.使用空行分段. 空行只起分段的作用,使用过多的空行并不起增大段间间距的作用. 2.段前不用打空格,LateX会自动完成文字的缩进. 即使打了也会被自动忽略. 3.通常汉字后面的空格会被忽略,其他符 ...

  10. python BeautifulSoup 爬虫运行出现 exited with code -1073741571

    首先,exited with code -1073741571意思是栈溢出.具体可以看https://blog.csdn.net/vblittleboy/article/details/6613815 ...