开发中异常处理会出现多次抛出的情况
- 配合全局异常处理器在最外层捕获异常
- 这里我们使用自定义异常BusinessException,DataAccessException,异常的名字可以快速看出异常发生在mvc的哪一层。
- 每次重新抛出,都会在原先异常的基础上面加上Caused by 和at,也就是异常发生原因和异常发生位置。
- 查看异常时,从下往上看,最下层也就是异常发生的最根本位置。
- 每次抛出时都必须传递原始异常e,如果不传递,每次重新抛出时,原来异常发生的原因和位置就没了,只剩新抛出异常的原因和位置。最终导致堆栈断裂,无法定位到异常发生的最根本位置。
// 控制器层
public class UserController {
public void getUserInfo() {
userService.getUser(); // 最终抛出 BusinessException
}
}
// 业务服务层
public class UserService {
void getUser() {
try {
userDao.queryUser();
} catch (DataAccessException e) {
throw new BusinessException("用户查询失败", e); // 再次包装
}
}
}
// 数据访问层
public class UserDao {
void queryUser() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new DataAccessException("驱动加载失败", e); // 包装原始异常
}
}
}
Exception in thread "main" BusinessException: 用户查询失败
at UserService.getUser(UserService.java:10) // 最外层抛出点
at UserController.getUserInfo(UserController.java:5)
Caused by: DataAccessException: 驱动加载失败 // 中间层异常
at UserDao.queryUser(UserDao.java:6)
at UserService.getUser(UserService.java:8)
Caused by: ClassNotFoundException: com.mysql.jdbc.Driver // 根本原因
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at UserDao.queryUser(UserDao.java:4)
... 2 more
不使用全局异常处理器
分层显式捕获并记录日志
// 控制层
@RestController
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@GetMapping("/user/{id}")
public User getUser(@PathVariable String id) {
try {
return userService.getUser(id);
} catch (BusinessException e) {
logger.error("请求 /user/{} 失败", id, e); // 记录请求路径和参数
return null; // 或返回错误响应
}
}
}
// 业务服务层
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public User getUser(String userId) {
try {
return userDao.queryUser();
} catch (RepositoryException e) {
logger.error("用户 {} 查询失败", userId, e); // 记录业务上下文
throw new BusinessException("查询失败", e); // 包装并抛出
}
}
}
// 数据访问层
public class UserDao {
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
public User queryUser() {
try {
// 模拟数据库操作
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error("数据库驱动加载失败", e); // 记录原始异常
throw new RepositoryException("驱动错误", e); // 包装并抛出
}
// ... 其他数据库操作
}
}
日志输出结果
- 共输出3次日志,每次记录异常信息(如参数)和原始异常
- 看日志时看每一次的输出信息,查看每一层完整的参数信息。
ERROR UserDao - 数据库驱动加载失败
java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
at UserDao.queryUser(UserDao.java:6)
ERROR UserService - 用户 123 查询失败
com.example.RepositoryException: 驱动错误
at UserDao.queryUser(UserDao.java:8)
Caused by: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
at UserDao.queryUser(UserDao.java:6)
ERROR UserController - 请求 /user/123 失败
com.example.BusinessException: 查询失败
at UserService.getUser(UserService.java:10)
Caused by: com.example.RepositoryException: 驱动错误
at UserDao.queryUser(UserDao.java:8)