引言
Java编程中,异常情况是不可避免的,优秀的异常处理机制是保证程序稳定性和可靠性的重要因素,当程序出现异常的时候,如果没有进行适当的处理,可能导致程序崩溃,丢失数据等严重问题。全局异常捕获与处理可以提高代码可读性,减少代码冗余,利于后期维护,保证系统稳定性。从上篇文章Java中的自定义异常,我们可以看出,如果每个异常单独进行捕获和处理会导致我们产生大量繁琐重复的工作,时间成本也随之增加。本文将深入探讨Java中的全局异常捕获与处理
全局异常捕获与处理原理
全局异常处理是一种在整个应用程序中捕获和处理异常的机制,它可以捕获没有被处理的异常并进行统一处理。原理:通过在应用程序 的入口点设置异常处理器,来捕获所有未被处理的异常。当程序发生异常时,如果它没有被局部的 try-catch 块捕获并处理,那么它将会被抛出到调用栈的上一层,如果调用栈的任何一层都没有对应的异常处理器,则它最终会被全局异常处理器捕获并进行统一处理
实现全局异常捕获与处理
try-catch
程序入口点使用 try-catch 块,上一篇文章就是这样处理的,存在代码冗余,耦合度高的问题
注解
Spring框架提供了@ControllerAdvice和@ExceptionHandler来实现全局异常捕获和处理,@ControllerAdvice用于定义一个全局控制器增强类,@ExceptionHandler用于定义处理特定类型异常的方法,由于我们现在的编程风格是RESTful,所以@ControllerAdvice还需要结合@ResponseBody来使用,或者直接使用@RestControllerAdvice
创建存放处理器的包
创建全局异常处理器
/**
* 全局异常处理器
* @author muze
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CustomException.class)
public SaResult customException(CustomException customException) {
// 返回友好提示信息
return SaResult.error(customException.getMessage());
}
}
修改用户控制层
移除自定义异常捕获和处理
/**
* 用户控制层
* @author muze
*/
@RestController
@RequestMapping("/user")
public class UserController {
/**
* 注入用户业务接口层
*/
@Autowired
private IUserService userService;
/**
* 登录
* @param userLoginDTO 用户登录请求实体
* @return 响应码 + 响应消息 + 响应数据
*/
@PostMapping("/login")
public SaResult login(@RequestBody UserLoginDTO userLoginDTO) {
return SaResult.ok(userService.login(userLoginDTO));
}
}
修改登录接口实现
由于我们全局异常捕获和处理中没有打印日志,为了在程序发生异常时快速定位问题,我们在抛出自定义异常前打印日志
/**
* 用户业务实现层
* @author muze
*/
//************************ 新增日志打印 ************************//
@Slf4j
@Service
public class UserServiceImpl implements IUserService {
/**
* 注入用户数据层
*/
@Autowired
private UserMapper userMapper;
@Override
public String login(UserLoginDTO userLoginDTO) {
// 取出用户名和密码
String username = userLoginDTO.getUsername();
String password = userLoginDTO.getPassword();
// 构建查询条件
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<User>().eq(User::getUsername, username);
// 查询用户
User user = userMapper.selectOne(userLambdaQueryWrapper);
//************************ 新增密码解密 ************************//
String decryptPassword;
try {
decryptPassword = RSAUtil.decryptByPrivateKey(password);
} catch (Exception e) {
//************************ 打印异常信息 ************************//
log.error("使用私钥解密密码时异常:", e);
//************************ 抛出自定义异常 ************************//
throw new CustomException("用户名或密码错误");
}
// 如果用户为空或者解密后的输入密码与用户密码不匹配则返回:用户名或密码错误
if (user == null || !decryptPassword.equals(user.getPassword())) return "用户名或密码错误";
// 使用SaToken的工具类StpUtil调用登录方法login,入参:用户id
StpUtil.login(user.getId());
// 返回:登录成功
return "登录成功";
}
}
测试
运行程序,输入未用公钥加密密码进行登录,程序会在密码解密时抛出自定义异常,控制台和接口调试工具可以看到如下结果
到这里我们就一起完成了Java中的全局异常捕获和处理,相信你已经掌握了,赶快去试试吧,希望这篇文章能对你有所帮助!
彩蛋:细心的你肯定发现了,我们之前通过SQL插入了一个用户,而不是通过功能实现的,并且密码是明文,下篇文章我们一起实现用户账号注册并通过sha256哈希算法计算出密码的哈希值后入库的功能吧,有兴趣的小伙伴可以先动手试试,敬请期待!