第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Spring Boot 異常處理

1. 前言

程序中出現(xiàn)異常是普遍現(xiàn)象, Java 程序員想必早已習(xí)慣,根據(jù)控制臺(tái)輸出的異常信息,分析異常產(chǎn)生的原因,然后進(jìn)行針對性處理的過程。

Spring Boot 項(xiàng)目中,數(shù)據(jù)持久層、服務(wù)層到控制器層都可能拋出異常。如果我們在各層都進(jìn)行異常處理,程序代碼會(huì)顯得支離破碎,難以理解。

實(shí)際上,異??梢詮膬?nèi)層向外層不斷拋出,最后在控制器層進(jìn)行統(tǒng)一處理。 Spring Boot 提供了全局性的異常處理機(jī)制,本節(jié)我們就分別演示下,默認(rèn)情況、控制器返回視圖、控制器返回 JSON 數(shù)據(jù)三種情況的異常處理方法。

2. Spring Boot 默認(rèn)異常處理機(jī)制

Spring Boot 開發(fā)的 Web 項(xiàng)目具備默認(rèn)的異常處理機(jī)制,無須編寫異常處理相關(guān)代碼,即可提供默認(rèn)異常機(jī)制,下面具體演示下。

2.1 使用 Spring Initializr 創(chuàng)建項(xiàng)目

Spring Boot 版本選擇 2.2.5 ,Group 為 com.imooc , Artifact 為 spring-boot-exception-default ,生成項(xiàng)目后導(dǎo)入 Eclipse 開發(fā)環(huán)境。

2.2 引入項(xiàng)目依賴

引入 Web 項(xiàng)目依賴即可。

實(shí)例:

		<!-- web項(xiàng)目依賴 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

2.3 Spring Boot 默認(rèn)異常處理

我們在啟動(dòng)項(xiàng)目, Spring Boot Web 項(xiàng)目默認(rèn)啟動(dòng)端口為 8080 ,所以直接訪問 http://127.0.0.1:8080 ,顯示如下:
圖片描述

Spring Boot 默認(rèn)異常信息提示頁面

如上圖所示,Spring Boot 默認(rèn)的異常處理機(jī)制生效,當(dāng)出現(xiàn)異常時(shí)會(huì)自動(dòng)轉(zhuǎn)向 /error 路徑。

3. 控制器返回視圖時(shí)的異常處理

在使用模板引擎開發(fā) Spring Boot Web 項(xiàng)目時(shí),控制器會(huì)返回視圖頁面。我們使用 Thymeleaf 演示控制器返回視圖時(shí)的異常處理方式,其他模板引擎處理方式也是相似的。

3.1 使用 Spring Initializr 創(chuàng)建項(xiàng)目

Spring Boot 版本選擇 2.2.5 ,Group 為 com.imooc , Artifact 為 spring-boot-exception-controller,生成項(xiàng)目后導(dǎo)入 Eclipse 開發(fā)環(huán)境。

3.2 引入項(xiàng)目依賴

引入 Web 項(xiàng)目依賴、熱部署依賴。此處使用 Thymeleaf 演示控制器返回視圖時(shí)的異常處理方式,所以引入 Thymeleaf 依賴。

實(shí)例:

		<!-- web項(xiàng)目依賴 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- 熱部署 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
		<!-- ThymeLeaf依賴 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

3.3 定義異常類

在異常處理之前,我們應(yīng)該根據(jù)業(yè)務(wù)場景具體情況,定義一系列的異常類,習(xí)慣性的還會(huì)為各種異常分配錯(cuò)誤碼,如下圖為支付寶開放平臺(tái)的公共錯(cuò)誤碼信息。

圖片描述

支付寶開放平臺(tái)錯(cuò)誤碼

本節(jié)我們?yōu)榱搜菔?,簡單的定義 2 個(gè)異常類,包含錯(cuò)誤碼及錯(cuò)誤提示信息。

實(shí)例:

/**
 * 自定義異常
 */
public class BaseException extends Exception {
	/**
	 * 錯(cuò)誤碼
	 */
	private int code;
	/**
	 * 錯(cuò)誤提示信息
	 */
	private String msg;

	public BaseException(int code, String msg) {
		super();
		this.code = code;
		this.msg = msg;
	}
	// 省略get set
}

實(shí)例:

/**
 * 密碼錯(cuò)誤異常
 */
public class PasswordException extends BaseException {
	public PasswordException() {
		super(10001, "密碼錯(cuò)誤");
	}
}

實(shí)例:

/**
 * 驗(yàn)證碼錯(cuò)誤異常
 */
public class VerificationCodeException extends BaseException {
	public VerificationCodeException() {
		super(10002, "驗(yàn)證碼錯(cuò)誤");
	}
}

3.4 控制器拋出異常

定義控制器 GoodsController ,然后使用注解 @Controller 標(biāo)注該類,類中方法的返回值即為視圖文件名。

在 GoodsController 類定義 4 個(gè)方法,分別用于正常訪問、拋出密碼錯(cuò)誤異常、拋出驗(yàn)證碼錯(cuò)誤異常、拋出未自定義的異常,代碼如下。

實(shí)例:

/**
 * 商品控制器
 */
@Controller
public class GoodsController {
	/**
	 * 正常方法
	 */
	@RequestMapping("/goods")
	public String goods() {
		return "goods";// 跳轉(zhuǎn)到resource/templates/goods.html頁面
	}

	/**
	 * 拋出密碼錯(cuò)誤異常的方法
	 */
	@RequestMapping("/checkPassword")
	public String checkPassword() throws PasswordException {
		if (true) {
			throw new PasswordException();// 模擬拋出異常,便于測試
		}
		return "goods";
	}

	/**
	 * 拋出驗(yàn)證碼錯(cuò)誤異常的方法
	 */
	@RequestMapping("/checkVerification")
	public String checkVerification() throws VerificationCodeException {
		if (true) {
			throw new VerificationCodeException();// 模擬拋出異常,便于測試
		}
		return "goods";
	}

	/**
	 * 拋出未自定義的異常
	 */
	@RequestMapping("/other")
	public String other() throws Exception {
		int a = 1 / 0;// 模擬異常
		return "goods";
	}
}

3.5 開發(fā)基于 @ControllerAdvice 的全局異常類

@ControllerAdvice 注解標(biāo)注的類可以處理 @Controller 標(biāo)注的控制器類拋出的異常,然后進(jìn)行統(tǒng)一處理。

實(shí)例:

/**
 * 控制器異常處理類
 */
@ControllerAdvice(annotations = Controller.class) // 全局異常處理
public class ControllerExceptionHandler {
	@ExceptionHandler({ BaseException.class }) // 當(dāng)發(fā)生BaseException類(及其子類)的異常時(shí),進(jìn)入該方法
	public ModelAndView baseExceptionHandler(BaseException e) {
		ModelAndView mv = new ModelAndView();
		mv.addObject("code", e.getCode());
		mv.addObject("message", e.getMessage());
		mv.setViewName("myerror");// 跳轉(zhuǎn)到resource/templates/myerror.html頁面
		return mv;
	}

	@ExceptionHandler({ Exception.class }) // 當(dāng)發(fā)生Exception類的異常時(shí),進(jìn)入該方法
	public ModelAndView exceptionHandler(Exception e) {
		ModelAndView mv = new ModelAndView();
		mv.addObject("code", 99999);// 其他異常統(tǒng)一編碼為99999
		mv.addObject("message", e.getMessage());
		mv.setViewName("myerror");// 跳轉(zhuǎn)到resource/templates/myerror.html頁面
		return mv;
	}
}

按照 ControllerExceptionHandler 類的處理邏輯,當(dāng)發(fā)生 BaseException 類型的異常時(shí),會(huì)跳轉(zhuǎn)到 myerror.html 頁面,并顯示相應(yīng)的錯(cuò)誤碼和錯(cuò)誤信息;當(dāng)發(fā)生其他類型的異常時(shí),錯(cuò)誤碼為 99999 ,錯(cuò)誤信息為相關(guān)的異常信息。

3.6 開發(fā)前端頁面

在 resource/templates 下分別新建 goods.html 和 myerror.html 頁面,作為正常訪問及發(fā)生異常時(shí)跳轉(zhuǎn)的視圖頁面。

實(shí)例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>goods.html頁面</title>
</head>
<body>
	<div>商品信息頁面</div>
</body>
</html>

實(shí)例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>myerror.html頁面</title>
</head>
<body>
	錯(cuò)誤碼:
	<span th:text="${code}"></span> 
	錯(cuò)誤信息:
	<span th:text="${message}"></span>
</body>
</html>

3.7 測試

啟動(dòng)項(xiàng)目,分別訪問控制器中的 4 個(gè)方法,結(jié)果如下:

圖片描述

訪問正常方法 /goods

圖片描述

訪問拋出自定義異常的方法 /checkPassword

圖片描述

訪問拋出自定義異常的方法 /checkVerification

圖片描述

訪問拋出未自定義異常的方法 /other

可見,當(dāng)控制器方法拋出異常時(shí),會(huì)按照全局異常類設(shè)定的邏輯統(tǒng)一處理。

4. 控制器返回 JSON 數(shù)據(jù)時(shí)的異常處理

在控制器類上添加 @RestController 注解,控制器方法處理完畢后會(huì)返回 JSON 格式的數(shù)據(jù)。

此時(shí),可以使用 @RestControllerAdvice 注解標(biāo)注的類 ,來捕獲 @RestController 標(biāo)注的控制器拋出的異常。

4.1 使用 Spring Initializr 創(chuàng)建項(xiàng)目

Spring Boot 版本選擇 2.2.5 ,Group 為 com.imooc , Artifact 為 spring-boot-exception-restcontroller,生成項(xiàng)目后導(dǎo)入 Eclipse 開發(fā)環(huán)境。

4.2 引入項(xiàng)目依賴

引入 Web 項(xiàng)目依賴、熱部署依賴即可。

實(shí)例:

		<!-- web項(xiàng)目依賴 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- 熱部署 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>

4.3 定義異常類

還是使用上文中定義的異常類即可。

4.4 統(tǒng)一控制器返回?cái)?shù)據(jù)格式

這時(shí)候,我們就需要思考一個(gè)問題了。前端請求后端控制器接口后,怎么區(qū)分后端接口是正常返回結(jié)果,還是發(fā)生了異常?

不論后端接口是正常執(zhí)行,還是中間發(fā)生了異常,最好給前端返回統(tǒng)一的數(shù)據(jù)格式,便于前端統(tǒng)一分析處理。

OK,此時(shí)我們就可以封裝后端接口返回的業(yè)務(wù)邏輯對象 ResultBo ,代碼如下:

實(shí)例:

/**
 * 后端接口返回的統(tǒng)一業(yè)務(wù)邏輯對象
 */
public class ResultBo<T> {

	/**
	 * 錯(cuò)誤碼 0表示沒有錯(cuò)誤(異常) 其他數(shù)字代表具體錯(cuò)誤碼
	 */
	private int code;
	/**
	 * 后端返回消息
	 */
	private String msg;
	/**
	 * 后端返回的數(shù)據(jù)
	 */
	private T data;

	/**
	 * 無參數(shù)構(gòu)造函數(shù)
	 */
	public ResultBo() {
		this.code = 0;
		this.msg = "操作成功";
	}

	/**
	 * 帶數(shù)據(jù)data構(gòu)造函數(shù)
	 */
	public ResultBo(T data) {
		this();
		this.data = data;
	}

	/**
	 * 存在異常的構(gòu)造函數(shù)
	 */
	public ResultBo(Exception ex) {
		if (ex instanceof BaseException) {
			this.code = ((BaseException) ex).getCode();
			this.msg = ex.getMessage();
		} else {
			this.code = 99999;// 其他未定義異常
			this.msg = ex.getMessage();
		}
	}
	// 省略 get set
}

4.5 控制器拋出異常

定義控制器 RestGoodsController ,并使用 @RestController 注解標(biāo)注。在其中定義 4 個(gè)方法,然后分別用于正常訪問、拋出密碼錯(cuò)誤異常、拋出驗(yàn)證碼錯(cuò)誤異常,以及拋出不屬于自定義異常類的異常。

實(shí)例:

/**
 * Rest商品控制器
 */
@RestController
public class RestGoodsController {
	/**
	 * 正常方法
	 */
	@RequestMapping("/goods")
	public ResultBo goods() {
		return new ResultBo<>(new ArrayList());// 正常情況下應(yīng)該返回商品列表
	}

	/**
	 * 拋出密碼錯(cuò)誤異常的方法
	 */
	@RequestMapping("/checkPassword")
	public ResultBo checkPassword() throws PasswordException {
		if (true) {
			throw new PasswordException();// 模擬拋出異常,便于測試
		}
		return new ResultBo<>(true);// 正常情況下應(yīng)該返回檢查密碼的結(jié)果true或false
	}

	/**
	 * 拋出驗(yàn)證碼錯(cuò)誤異常的方法
	 */
	@RequestMapping("/checkVerification")
	public ResultBo checkVerification() throws VerificationCodeException {
		if (true) {
			throw new VerificationCodeException();// 模擬拋出異常,便于測試
		}
		return new ResultBo<>(true);// 正常情況下應(yīng)該返回檢查驗(yàn)證碼的結(jié)果true或false
	}

	/**
	 * 拋出未自定義的異常
	 */
	@RequestMapping("/other")
	public ResultBo other() throws Exception {
		int a = 1 / 0;// 模擬異常
		return new ResultBo<>(true);
	}
}

4.6 開發(fā)基于 @RestControllerAdvice 的全局異常類

@RestControllerAdvice 注解標(biāo)注的類可以處理 RestController 控制器類拋出的異常,然后進(jìn)行統(tǒng)一處理。

實(shí)例:

/**
 * Rest控制器異常處理類
 */
@RestControllerAdvice(annotations = RestController.class) // 全局異常處理
public class RestControllerExceptionHandler {
	/**
	 * 處理BaseException類(及其子類)的異常
	 */
	@ExceptionHandler({ BaseException.class })
	public ResultBo baseExceptionHandler(BaseException e) {
		return new ResultBo(e);
	}

	/**
	 * 處理Exception類的異常
	 */
	@ExceptionHandler({ Exception.class })
	public ResultBo exceptionHandler(Exception e) {
		return new ResultBo(e);
	}
}

4.7 測試

啟動(dòng)項(xiàng)目,分別嘗試訪問控制器中的 4 個(gè)接口,結(jié)果如下。

圖片描述

訪問正常方法 /goods

圖片描述

訪問拋出異常的方法 /checkPassword

圖片描述

訪問拋出異常的方法 /checkVerification

圖片描述

訪問拋出異常的方法 /other

5. 小結(jié)

Spring Boot 的默認(rèn)異常處理機(jī)制,實(shí)際上只能做到提醒開發(fā)者 “這個(gè)后端接口不存在” 的作用,作用非常有限。

所以我們在開發(fā) Spring Boot 項(xiàng)目時(shí),需要根據(jù)項(xiàng)目的實(shí)際情況,定義各類異常,并站在全局的角度統(tǒng)一處理異常。

不管項(xiàng)目有多少層次,所有異常都可以向外拋出,直到控制器層進(jìn)行集中處理。

  • 對于返回視圖的控制器,如果沒發(fā)生異常就跳轉(zhuǎn)正常頁面,如果發(fā)生異常可以自定義錯(cuò)誤信息頁面。
  • 對于返回 JSON 數(shù)據(jù)的控制器,最好是定義統(tǒng)一的數(shù)據(jù)返回格式,便于前端根據(jù)返回信息進(jìn)行正?;蛘弋惓G闆r的處理。