那使用全局异常处理器可以么?
是的,使用全局异常处理器是一个非常好的选择,因为它可以将异常处理逻辑集中化,避免在 Service
层或 Controller
层中重复编写异常处理代码。以下是使用全局异常处理器来处理添加用户时 username
唯一约束冲突的完整实现方案:
1. 全局异常处理器的优势
- 集中管理:所有异常处理逻辑集中在一个地方,便于维护和扩展。
- 代码简洁:
Service
层和Controller
层只需关注业务逻辑,不需要处理异常。 - 统一响应格式:可以确保所有异常返回的响应格式一致,提升 API 的规范性。
2. 实现步骤
步骤 1:定义自定义异常
为了更好地区分业务异常,可以定义一个自定义异常类。
java
换行复制代码
1public class BusinessException extends RuntimeException {
2 public BusinessException(String message) {
3 super(message);
4 }
5}
步骤 2:在 Service
层抛出异常
在 UserService
层中,如果检测到 username
已存在,抛出 BusinessException
。
java
换行复制代码
1import org.springframework.beans.factory.annotation.Autowired;
2import org.springframework.dao.DuplicateKeyException;
3import org.springframework.stereotype.Service;
4
5@Service
6public class UserServiceImpl implements UserService {
7
8 @Autowired
9 private UserMapper userMapper;
10
11 @Override
12 public void addUser(User user) {
13 try {
14 int affectedRows = userMapper.addOneUser(user);
15 if (affectedRows != 1) {
16 throw new RuntimeException("添加用户失败,影响行数为 " + affectedRows);
17 }
18 } catch (DuplicateKeyException e) {
19 // 捕获唯一约束异常
20 throw new BusinessException("用户名已存在");
21 }
22 }
23}
步骤 3:实现全局异常处理器
使用 @ControllerAdvice
和 @ExceptionHandler
注解,创建一个全局异常处理器,统一处理 BusinessException
和其他异常。
java
换行复制代码
1import org.springframework.http.HttpStatus;
2import org.springframework.http.ResponseEntity;
3import org.springframework.web.bind.annotation.ControllerAdvice;
4import org.springframework.web.bind.annotation.ExceptionHandler;
5import org.springframework.web.bind.annotation.ResponseBody;
6
7@ControllerAdvice
8public class GlobalExceptionHandler {
9
10 // 处理自定义业务异常
11 @ExceptionHandler(BusinessException.class)
12 @ResponseBody
13 public ResponseEntity<Map<String, Object>> handleBusinessException(BusinessException e) {
14 Map<String, Object> response = new HashMap<>();
15 response.put("code", 409); // 409 Conflict
16 response.put("message", e.getMessage());
17 return new ResponseEntity<>(response, HttpStatus.CONFLICT);
18 }
19
20 // 处理其他异常
21 @ExceptionHandler(Exception.class)
22 @ResponseBody
23 public ResponseEntity<Map<String, Object>> handleGenericException(Exception e) {
24 Map<String, Object> response = new HashMap<>();
25 response.put("code", 500); // 500 Internal Server Error
26 response.put("message", "系统错误,请稍后重试");
27 return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
28 }
29}
步骤 4:在 Controller
层调用 Service
层
在 Controller
层中,直接调用 UserService
的方法,无需处理异常。
java
换行复制代码
1import org.springframework.beans.factory.annotation.Autowired;
2import org.springframework.http.ResponseEntity;
3import org.springframework.web.bind.annotation.PostMapping;
4import org.springframework.web.bind.annotation.RequestBody;
5import org.springframework.web.bind.annotation.RequestMapping;
6import org.springframework.web.bind.annotation.RestController;
7
8@RestController
9@RequestMapping("/users")
10public class UserController {
11
12 @Autowired
13 private UserService userService;
14
15 @PostMapping
16 public ResponseEntity<String> addUser(@RequestBody User user) {
17 userService.addUser(user);
18 return ResponseEntity.ok("用户添加成功");
19 }
20}
3. 完整流程
Controller
层 调用UserService
的addUser
方法。UserService
层 执行INSERT
操作:- 如果成功,返回
void
。 - 如果因为
username
唯一约束失败,抛出BusinessException
。
- 如果成功,返回
- 全局异常处理器 捕获
BusinessException
,返回 409 状态码和错误信息。 Controller
层 根据UserService
的返回值或全局异常处理器的响应,返回最终的 HTTP 响应。
4. 示例响应
添加成功
json
换行复制代码
1{
2 "code": 200,
3 "message": "用户添加成功"
4}
用户名已存在
json
换行复制代码
1{
2 "code": 409,
3 "message": "用户名已存在"
4}
系统错误
json
换行复制代码
1{
2 "code": 500,
3 "message": "系统错误,请稍后重试"
4}
5. 优点
- 代码简洁:
Controller
层和Service
层只需关注业务逻辑,异常处理由全局异常处理器统一管理。 - 可扩展性强:如果需要处理更多类型的异常,只需在全局异常处理器中添加新的
@ExceptionHandler
方法。 - 统一响应格式:所有异常返回的响应格式一致,便于前端处理。
6. 注意事项
- 异常分类:根据业务需求,可以定义更多的自定义异常类(如
ValidationException
、NotFoundException
等),并在全局异常处理器中分别处理。 - 日志记录:在全局异常处理器中记录异常日志,便于排查问题。
- 国际化:如果需要支持多语言,可以在全局异常处理器中根据请求头或用户偏好返回相应的错误信息。
通过这种方式,可以构建一个健壮且易于维护的异常处理机制。