paopao/docs/tasks/任务5-Sprint-1-实现统一异常处理.md
2024-05-12 16:13:08 +08:00

197 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 任务名称: Restful接口回应数据的统一封装
### 目标
- 掌握Spring Boot框架的异常处理
### 预备知识
- java的异常机制
### 操作步骤
#### 1. 自定义异常的基类
实现一个项目的异常基类,设计为抽象类避免直接使用此类。基类继承自RuntimeException并添加一个code属性为每个异常类型分配一个唯一的code值。
自定义异常时定义一个基类可以带来多个好处,以下是一些主要优势:
> **代码复用**:基类可以包含所有自定义异常的通用属性和方法,减少了代码重复。
>
> **统一管理**:通过基类可以统一管理异常处理,比如定义一个通用的异常处理机制,简化异常的捕获和处理。
>
> **清晰的层次结构**:基类提供了一个清晰的异常层次结构,使得异常更加有组织,易于理解和使用。
>
> **维护方便**:当需要对异常进行修改或添加新特性时,只需在基类中进行一次修改,所有派生异常都会自动获得这些更改。
>
> **异常编码**如你所提到的基类可以包含一个错误码code属性为每个异常类型分配一个唯一值这有助于快速识别和处理特定类型的异常。
>
> **信息丰富**:基类可以定义一些通用的构造函数,允许派生异常类在创建时提供额外的信息,如错误消息、原始异常等。
>
> **控制异常的传播**基类可以决定异常是否应该被检查checked或不被检查unchecked。通过继承 `RuntimeException`,可以创建非检查型异常,这通常用于程序运行时错误,调用者不需要强制捕获。
```java
package com.lk.paopao.exception;
/**
* 定义一个基础异常类,用于封装业务异常信息。
* 这个类是 RuntimeException 的子类意味着它是一个非检查Unchecked异常。
* 它添加了一个错误码code来详细描述异常的类型。
*/
public abstract class BaseException extends RuntimeException{
// 异常的错误码
private final int code;
/**
* 构造函数,用于创建一个带有错误码和消息的基础异常实例。
*
* @param code 异常的错误码,用于标识异常的类型。
* @param message 异常的详细信息,帮助理解异常的原因。
*/
public BaseException(int code, String message){
// 调用父类构造函数,将消息传递给父类,以初始化异常的详细信息。
super(message);
this.code = code;
}
/**
* 构造函数,用于创建一个带有错误码、消息和导致异常的原因的基础异常实例。
*
* @param code 异常的错误码,用于标识异常的类型。
* @param message 异常的详细信息,帮助理解异常的原因。
* @param cause 导致异常的原因,便于问题追踪和处理。
*/
public BaseException(int code, String message, Throwable cause) {
// 调用父类构造函数,将消息和原因传递给父类,以初始化异常的详细信息和原因。
super(message, cause);
this.code = code;
}
public int getCode() {
return code;
}
}
```
#### 2. 实现三个具体的自定义异常类
ResourceNotFoundException: 找不到资源,用于统一处理找不到资源的情况。
```java
package com.lk.paopao.exception;
public class ResourceNotFoundException extends BaseException{
public ResourceNotFoundException(String resourceName, String condition){
super(404, "根据条件:【"+condition+"】无法找到"+resourceName+"资源");
}
}
```
ResourceExistedException: 资源已存在,用于统一处理资源已存在的情况。
```java
package com.lk.paopao.exception;
public class ResourceExistedException extends BaseException{
public ResourceExistedException(String resourceName, String condition){
super(409, "根据条件:【"+condition+"】可找到"+resourceName+"资源");
}
}
```
AuthFailedException: 认证失败,用于统一处理认证失败的情况。
```java
package com.lk.paopao.exception;
public class AuthFailedException extends BaseException{
public AuthFailedException(String message) {
super(401, message);
}
}
```
#### 3. 实现异常的统一处理
在Spring框架中`@ControllerAdvice` 是一个核心的类级别的注解用于定义一个全局异常处理器。当一个Spring Boot应用中的控制器即一个带有`@RestController`或`@Controller`注解的类抛出异常时Spring会检查所有的`@ControllerAdvice`注解的类,寻找匹配的异常处理器。
以下是`@ControllerAdvice`注解的一些关键点:
1. **全局异常处理**`@ControllerAdvice` 注解的类可以包含一个或多个 `@ExceptionHandler` 方法,这些方法可以捕获并处理控制器层抛出的异常。
2. **注解方法**:在`@ControllerAdvice`类中,通过使用`@ExceptionHandler`注解的方法可以指定处理特定类型的异常。方法的参数可以是异常类型或者是`Throwable`类型。
3. **响应体**`@ControllerAdvice`类中的方法通常使用`@ResponseBody`注解这表明方法的返回值将直接作为HTTP响应的正文返回给客户端。
4. **日志记录**:在异常处理方法中,通常会记录异常信息,这有助于开发人员调试和监控应用。
5. **统一响应格式**`@ControllerAdvice` 允许你定义一个统一的响应格式,无论哪种异常发生,客户端都会收到相同结构的响应,这有助于客户端统一处理不同异常情况。
6. **顺序**:如果有多个`@ControllerAdvice`注解的类,它们可以通过`@Order`注解或`Ordered`接口来指定处理顺序。
7. **作用域**`@ControllerAdvice`注解的类可以是单例的singleton也可以是请求作用域的request具体取决于异常处理器的需要。
```java
package com.lk.paopao.exception;
import com.lk.paopao.dto.rest.response.Result;
import com.lk.paopao.dto.rest.response.ResultUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 全局异常处理器负责处理Controller层抛出的异常提供统一的异常响应格式。
*/
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理自定义异常BaseException返回相应的错误结果。
*
* @param ex 自定义异常BaseException实例
* @return 返回封装了错误信息的Result对象
*/
@ExceptionHandler(BaseException.class)
@ResponseBody
public Result handleBaseException(BaseException ex) {
// 记录未预期异常信息
log.info(ex.getMessage(),ex);
return ResultUtil.fail(ex.getCode(),ex.getMessage());
}
/**
* 处理未预期的异常,返回通用错误结果。
*
* @param ex 异常实例
* @return 返回封装了错误信息的Result对象
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public Result handleUnexpectedException(Exception ex) {
// 记录未预期异常信息
log.warn(ex.getMessage(),ex);
// 返回通用错误结果, 500表示服务器内部错误
return ResultUtil.fail(500,ex.getMessage());
}
}
```
### 技术/工具需求:
- [列出完成任务所需的技术栈、工具、软件版本等。]
### 成功标准:
- [明确完成任务的评判标准,如代码功能实现、性能指标、测试通过条件等。]
### 扩展学习(可选):
- [提供一些额外学习资源或挑战性任务,鼓励学有余力的学生进一步探索。]
### 评估与反馈:
- [说明如何提交作业、代码审查的标准、或任何反馈收集机制。]
### 时间估算:
- [给出预计完成该任务所需的时间,帮助学生合理安排学习计划。]