抛弃丑陋的 try-catch,优雅处理异常
随着业务逻辑变得越来越复杂,我们在编写代码时会遇到各种异常情况,这时就需要使用 try-catch 语句来捕获异常并进行处理。但是,大量的 try-catch 语句会让代码变得臃肿,不易维护,因此,我们需要一种优雅的方式来统一处理异常,减少代码中的 try-catch 语句。
比较下面两张图,看看您现在编写的代码属于哪一种风格?然后哪种编码风格您更喜欢?
丑陋的 try catch 代码块:
优雅的 Controller:
那么问题来了,我们改如何实现第二种异常处理方式,如何优雅的处理各种异常?
异常处理的基本原则
在讲解如何减少 try-catch 语句之前,我们先来了解一下异常处理的基本原则。
首先,异常应该被及时捕获并进行处理。如果异常未被捕获,程序将崩溃并抛出未处理的异常,影响系统的稳定性。因此,我们需要在代码中合理地使用 try-catch 语句来捕获异常,并在 catch 块中进行处理。
异常应该被分类处理。不同类型的异常需要采取不同的处理方式,比如,对于业务逻辑异常,我们需要将异常信息返回给客户端,而对于系统异常,我们需要记录日志并通知管理员。
最后,异常处理应该是统一的。在一个应用程序中,我们可能会遇到很多不同的异常类型,如果每个异常都需要单独处理,会使代码变得很冗长。因此,我们需要将异常处理的逻辑抽象出来,实现统一的异常处理。
Spring Boot 中的异常处理
Spring Boot 提供了很多种方式来处理异常,比如使用 @ControllerAdvice 注解来定义一个全局的异常处理类,使用 @ExceptionHandler 注解来处理特定的异常类型等。下面,我们将介绍如何使用 @ControllerAdvice 注解来实现统一的异常处理。
定义异常处理类
我们可以使用 @ControllerAdvice 注解来定义一个全局的异常处理类,该类中的方法会被自动调用来处理异常。下面是一个示例:
在上面的代码中,我们定义了一个全局的异常处理类 GlobalExceptionHandler,该类中定义了两个方法:handleBusinessException 和 handleException。其中,handleBusinessException 方法用来处理业务逻辑异常,handleException 方法用来处理系统异常。@ExceptionHandler 注解指定了处理的异常类型,@ResponseBody 注解将返回结果序列化成 JSON 格式,方便客户端进行处理。
自定义异常类
在上面的代码中,我们使用了 BusinessException 和 Exception 两个异常类来区分业务逻辑异常和系统异常。因此,我们需要定义这两个异常类。下面是一个示例:
在上面的代码中,我们定义了一个 BusinessException 类,继承自 RuntimeException。该类中包含了一个 code 字段,用来标识业务逻辑异常的类型。
抛出异常
在我们的业务逻辑中,如果遇到了异常情况,我们需要抛出对应的异常。下面是一个示例:
在上面的代码中,如果根据 id 未找到对应的用户,我们就会抛出 BusinessException 异常,该异常包含了错误码 1001 和错误信息"用户不存在"。
优雅的异常处理方式
通过上面的示例,我们已经实现了一个基本的统一异常处理,但是在实际开发中,我们还可以优化异常处理的方式,使代码更加优雅。
使用枚举类定义错误码
在上面的示例中,我们在 BusinessException 中定义了错误码,但是错误码的值可能会有重复,而且不太容易管理。因此,我们可以使用枚举类来定义错误码。下面是一个示例:
在上面的代码中,我们定义了一个 ErrorCode 枚举类,包含了多个错误码及其对应的错误信息。
使用自定义注解来简化代码
在上面的示例中,我们需要在每个抛出异常的方法中手动创建 BusinessException 对象,并指定错误码和错误信息。这样做的代码量比较大,而且不太优雅。因此,我们可以使用自定义注解来简化代码。下面是一个示例:
在上面的代码中,我们定义了一个 CheckUser 注解,用来标注需要检查用户的方法。该注解包含了一个 id 属性,用来指定用户的 id。
下面是一个使用该注解的示例:
在上面的代码中,我们使用了 @CheckUser 注解,指定了 id 为 1,表示需要检查 id 为 1 的用户是否存在。在 CheckUserAspect 切面中,我们会根据该注解的值来进行业务逻辑的处理。
使用 AOP 实现统一异常处理
在上面的示例中,我们使用了 @ExceptionHandler 注解来实现异常的处理。但是,如果我们有很多的 Controller 方法,每个方法都需要加上该注解,这样就会使代码量变得很大,而且不太优雅。因此,我们可以使用 AOP 来实现统一异常处理。下面是一个示例:
在上面的代码中,我们定义了一个 ExceptionAspect 切面,用来处理所有 Controller 方法抛出的异常。@Pointcut 注解用来定义切入点,表示所有 public 方法。@Around 注解用来定义环绕通知,表示在目标方法执行前后都要执行该通知。在该通知中,我们可以处理抛出的异常,然后返回处理结果。
总结
通过上面的示例,我们实现了一个简单的统一异常处理。在实际开发中,我们可以根据需求进行一些优化,使代码更加简洁、优雅。异常处理是一个很重要的功能,需要我们在开发过程中认真对待,避免出现漏洞,保证系统的稳定性和安全性。
评论