springBoot2.X配置全局捕獲異常的操作
springBoot2.X配置全局捕獲異常
先來(lái)看一段代碼:當(dāng)傳入的id是0的時(shí)候,就會(huì)報(bào)異常。
@RestController
public class HelloController {
@GetMapping("/getUser")
public String getUser(int id) {
int j = 1 / id;
return "SUCCESS" + j;
}
}
訪問(wèn)時(shí):

我們知道這個(gè)頁(yè)面要是給用戶看到,用戶可能不知道這是什么。
方法一:將異常捕獲
@GetMapping("/getUser")
public String getUser(int id) {
int j;
try {
j = 1 / id;
} catch (Exception e) {
return "系統(tǒng)異常";
}
return "SUCCESS" + j;
}
這種方法當(dāng)然可以,但是當(dāng)我們有很多方法時(shí),需要在每個(gè)方法上都加上。
哎,太雞肋了吧。
那么都沒(méi)有全局的攔截處理呢?
當(dāng)然了
方法二:通過(guò)@ControllerAdvice注解配置
/**
* @Author 劉翊揚(yáng)
* @Date 2020/9/30 11:39 下午
* @Version 1.0
*/
@ControllerAdvice(basePackages = "com.yiyang.myfirstspringdemo.controller")
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
@ResponseBody
public Map<String,Object> errorResult() {
Map<String, Object> map = new HashMap<>();
map.put("errorCode", "500");
map.put("errorMsg", "全局捕獲異常");
return map;
}
}
@ExceptionHandler表示攔截異常@ControllerAdvice是 controller 的一個(gè)輔助類,最常用的就是作為全局異常處理的切面類@ControllerAdvice可以指定掃描范圍
注意:下面還需要在啟動(dòng)類上加上,否則誒呦效果
package com.yiyang.myfirstspringdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = {"com.yiyang.myfirstspringdemo.error", "com.yiyang.myfirstspringdemo.controller"})
public class MyFirstSpringDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MyFirstSpringDemoApplication.class, args);
}
}
在啟動(dòng)類上,將掃描包范圍controller和全局異常處理類,加上去。

這樣當(dāng)我們?cè)谠L問(wèn)的時(shí)候,出現(xiàn)的異常提示信息就是我們?cè)谌之惓L幚碇性O(shè)置的返回值。
springboot2.x 全局異常處理的正確方式
在web項(xiàng)目中,異常堆棧信息是非常敏感的。因此,需要一個(gè)全局的異常處理,捕獲異常,給客戶端以友好的錯(cuò)誤信息提示?;?Spring boot 很容易實(shí)現(xiàn)全局異常處理。
相關(guān)jar依賴引入
<!-- Spring Boot 啟動(dòng)父依賴 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Spring Boot Web 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
全局異??刂破?/h3>
package com.yb.demo.common.handler;
import com.yb.demo.common.enums.CodeEnum;
import com.yb.demo.common.exception.BizException;
import com.yb.demo.pojo.response.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ValidationException;
import java.util.StringJoiner;
/**
* 全局異常處理
* <p>
* 規(guī)范:流程跳轉(zhuǎn)盡量避免使用拋 BizException 來(lái)做控制。
*
* @author daoshenzzg@163.com
* @date 2019-09-06 18:02
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 處理自定義異常
*
* @param ex
* @return
*/
@ExceptionHandler(BizException.class)
public Result handleBizException(BizException ex) {
Result result = Result.renderErr(ex.getCode());
if (StringUtils.isNotBlank(ex.getRemark())) {
result.withRemark(ex.getRemark());
}
return result;
}
/**
* 參數(shù)綁定錯(cuò)誤
*
* @param ex
* @return
*/
@ExceptionHandler(BindException.class)
public Result handleBindException(BindException ex) {
StringJoiner sj = new StringJoiner(";");
ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(sj.toString());
}
/**
* 參數(shù)校驗(yàn)錯(cuò)誤
*
* @param ex
* @return
*/
@ExceptionHandler(ValidationException.class)
public Result handleValidationException(ValidationException ex) {
return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(ex.getCause().getMessage());
}
/**
* 字段校驗(yàn)不通過(guò)異常
*
* @param ex
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
StringJoiner sj = new StringJoiner(";");
ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(sj.toString());
}
/**
* Controller參數(shù)綁定錯(cuò)誤
*
* @param ex
* @return
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(ex.getMessage());
}
/**
* 處理方法不支持異常
*
* @param ex
* @return
*/
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
return Result.renderErr(CodeEnum.METHOD_NOT_ALLOWED);
}
/**
* 其他未知異常
*
* @param ex
* @return
*/
@ExceptionHandler(value = Exception.class)
public Result handleException(Exception ex) {
log.error(ex.getMessage(), ex);
return Result.renderErr(CodeEnum.SERVER_ERR);
}
}
個(gè)性化異常處理
package com.yb.demo.common.handler;
import com.yb.demo.common.enums.CodeEnum;
import com.yb.demo.common.exception.BizException;
import com.yb.demo.pojo.response.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ValidationException;
import java.util.StringJoiner;
/**
* 全局異常處理
* <p>
* 規(guī)范:流程跳轉(zhuǎn)盡量避免使用拋 BizException 來(lái)做控制。
*
* @author daoshenzzg@163.com
* @date 2019-09-06 18:02
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 處理自定義異常
*
* @param ex
* @return
*/
@ExceptionHandler(BizException.class)
public Result handleBizException(BizException ex) {
Result result = Result.renderErr(ex.getCode());
if (StringUtils.isNotBlank(ex.getRemark())) {
result.withRemark(ex.getRemark());
}
return result;
}
/**
* 參數(shù)綁定錯(cuò)誤
*
* @param ex
* @return
*/
@ExceptionHandler(BindException.class)
public Result handleBindException(BindException ex) {
StringJoiner sj = new StringJoiner(";");
ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(sj.toString());
}
/**
* 參數(shù)校驗(yàn)錯(cuò)誤
*
* @param ex
* @return
*/
@ExceptionHandler(ValidationException.class)
public Result handleValidationException(ValidationException ex) {
return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(ex.getCause().getMessage());
}
/**
* 字段校驗(yàn)不通過(guò)異常
*
* @param ex
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
StringJoiner sj = new StringJoiner(";");
ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(sj.toString());
}
/**
* Controller參數(shù)綁定錯(cuò)誤
*
* @param ex
* @return
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(ex.getMessage());
}
/**
* 處理方法不支持異常
*
* @param ex
* @return
*/
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
return Result.renderErr(CodeEnum.METHOD_NOT_ALLOWED);
}
/**
* 其他未知異常
*
* @param ex
* @return
*/
@ExceptionHandler(value = Exception.class)
public Result handleException(Exception ex) {
log.error(ex.getMessage(), ex);
return Result.renderErr(CodeEnum.SERVER_ERR);
}
}
自定義異常
在實(shí)際web開(kāi)發(fā)過(guò)程中,往往會(huì)遇到在某些場(chǎng)景下需要終止當(dāng)前流程,直接返回。那么,通過(guò)拋出自定義異常,并在全局異常中捕獲,用以友好的提示客戶端。
/**
* 業(yè)務(wù)異常跳轉(zhuǎn)。
*
* @author daoshenzzg@163.com
* @date 2019-09-09 14:57
*/
@Data
public class BizException extends RuntimeException {
private static final long serialVersionUID = 1L;
private CodeEnum code;
private String remark;
public BizException(CodeEnum code) {
super(code.getMessage());
this.code = code;
}
public BizException withRemark(String remark) {
this.remark = remark;
return this;
}
}
使用方式如下:
/**
* 添加學(xué)生
*
* @param student
* @return
*/
public Student1DO addStudent(Student1DO student) {
if (StringUtils.isNotBlank(student.getStudName())) {
// 舉例扔個(gè)業(yè)務(wù)異常,實(shí)際使用過(guò)程中,應(yīng)該避免扔異常
throw new BizException(CodeEnum.REQUEST_ERR).withRemark("studName不能為空");
}
student1Mapper.insert(student);
return student;
}
返回效果如下:
{
"code": -400,
"msg": "請(qǐng)求錯(cuò)誤(studName不能為空)",
"data": {},
"ttl": 0
}
根據(jù)阿里巴巴規(guī)范,流程控制還是不要通過(guò)拋異常的方式。在正常開(kāi)發(fā)過(guò)程中,應(yīng)避免使用這種方式。
【強(qiáng)制】異常不要用來(lái)做流程控制,條件控制,因?yàn)楫惓5奶幚硇时葪l件分支低。
使用 Spring validation 完成數(shù)據(jù)后端校驗(yàn)
定義實(shí)體類,加上validation相關(guān)注解
package com.yb.demo.pojo.model.db1;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
/**
* @author daoshenzzg@163.com
* @date 2019-08-05 17:58
*/
@Data
@TableName("student")
public class Student1DO {
private Long id;
@Size(max = 8, message = "studName長(zhǎng)度不能超過(guò)8")
private String studName;
@Min(value = 12, message = "年齡不能低于12歲")
private Integer studAge;
private String studSex;
@TableField(fill = FieldFill.INSERT)
private Integer createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Integer updateTime;
}
在Controller 方法上加上 @Validated 注解
@PostMapping("/student/add")
public Result addStudent(@Validated @RequestBody Student1DO student) {
student = studentService.addStudent(student);
return Result.renderOk(student);
}
實(shí)際效果如下:
{
"code": -400,
"msg": "請(qǐng)求錯(cuò)誤(年齡不能低于12歲)",
"data": {},
"ttl": 0
}
結(jié)束語(yǔ)
具體代碼見(jiàn):https://github.com/daoshenzzg/springboot2.x-example
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java使用位運(yùn)算實(shí)現(xiàn)加減乘除詳解
這篇文章主要為大家詳細(xì)介紹了Java如何使用位運(yùn)算實(shí)現(xiàn)加減乘除,文中的示例代碼講解詳細(xì),對(duì)我們深入了解Java有一定的幫助,感興趣的可以了解一下2023-05-05
一次 Java 服務(wù)性能優(yōu)化實(shí)例詳解
這篇文章主要介紹了一次 Java 服務(wù)性能優(yōu)化實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07
詳解直接插入排序算法與相關(guān)的Java版代碼實(shí)現(xiàn)
這篇文章主要介紹了直接插入排序算法與相關(guān)的Java版代碼實(shí)現(xiàn),需要的朋友可以參考下2016-05-05
SpringBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的多種方式
優(yōu)雅停機(jī)(Graceful Shutdown)在現(xiàn)代微服務(wù)架構(gòu)中是非常重要的,它幫助我們確保在應(yīng)用程序停止時(shí),不會(huì)中斷正在進(jìn)行的請(qǐng)求或?qū)е聰?shù)據(jù)丟失,讓我們以通俗易懂的方式來(lái)講解這個(gè)概念以及如何在 Spring Boot 中實(shí)現(xiàn)它,需要的朋友可以參考下2025-01-01
SpringBoot實(shí)現(xiàn)類似鉤子函數(shù)的方法
這篇文章主要給大家介紹了關(guān)于SpringBoot實(shí)現(xiàn)類似鉤子函數(shù)的方法,文中通過(guò)代碼示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-04-04
JAVA中使用openoffice將Excel轉(zhuǎn)PDF再轉(zhuǎn)圖片功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了JAVA中使用openoffice將Excel轉(zhuǎn)PDF再轉(zhuǎn)圖片功能實(shí)現(xiàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12
詳解log4j.properties的簡(jiǎn)單配置和使用
本篇文章主要介紹了詳解log4j.properties的簡(jiǎn)單配置和使用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
java實(shí)現(xiàn)隨機(jī)驗(yàn)證碼圖片生成
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)隨機(jī)驗(yàn)證碼圖片生成,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11

