企业级应用中的异常处理策略

在企业级应用开发过程中,异常处理是一个至关重要的环节。传统的异常处理方法虽然在简单应用或测试中可行,但在企业级应用中,由于代码量的增加和复杂性的提高,这些方法往往显得力不从心。本文将探讨一种更为结构化和高效的异常处理方法。

在传统的企业级应用开发中,开发者可能会遇到多种问题,这些问题随着应用规模的扩大而变得更加突出。以下是一些关键因素:

在模块化的应用中,相同的异常处理代码可能会被重复编写,这使得跟踪和维护代码变得非常困难。

如果处理的目的相同,为什么不使用同一段代码来处理不同的异常呢?这不仅可以减少代码冗余,还可以提高代码的可重用性。

当前的处理代码通常只负责记录异常。在未来,可能需要发送邮件、将错误信息发布到JMS队列、执行回滚操作,或者执行所有这些操作的组合。在传统方法中,由于处理代码与异常源紧密耦合,很难根据需求修改代码。

在catch块中,常常包含一些复杂的代码,这不仅增加了方法的长度,也降低了代码的可读性。

在遗留应用中,追踪异常处理流程可能非常困难。在传统方法中,开发者需要花费大量时间来理解代码流程。

以上问题非常普遍,每个开发者都至少遇到过一次。

策略

本框架的核心策略是将处理逻辑与异常源分离,使它们之间的关系尽可能松散,并在需要时将处理逻辑挂钩。分离处理逻辑并不难,可以创建一个处理类并将逻辑放在其中。但主要的问题是如何挂钩,以提高代码的可重用性、可维护性、灵活性,并且简单易用。

以下是一个简单的Java示例,演示了如何验证用户登录:

public void checkLogin() { try { // Statement 1 which may throw new UserNotExistException("User not Exist."); // Statement 2 which may throw new InvalidCredentialException("Invalid Credentials."); // Statement 3 which may throw new DBException("Failed to retrieve data."); // Statement 4 which may throw new Exception("General Exception."); } catch (UserNotExistException uex) { // To do } catch (InvalidCredentialException fex) { // To do } catch (DBException dex) { // To do } catch (Exception ex) { // To do } }

在这个示例中,方法可能会抛出四种类型的异常。同时,也有相应的catch块来捕获这些异常。这是一个非常熟悉的场景。

改进后的异常处理

根据要求,如果发生任何异常,应用程序可能会采取以下处理动作(单个动作或组合动作)来处理每种类型的异常(为了示例目的,选择了四种处理动作):

  • 记录异常
  • 将错误详情发送到某个JMS队列
  • 发送邮件
  • 执行一些默认工作

现在重写catch块,添加上述处理程序:

public boolean checkLogin() { try { // Statement 1 which may throw new UserNotExistException("User not Exist."); // Statement 2 which may throw new InvalidCredentialException("Invalid Credentials."); // Statement 3 which may throw new DBException("Failed to retrieve data."); // Statement 4 which may throw new Exception("General Exception."); } catch (UserNotExistException uex) { // To do // want to log exception // want to do some default work } catch (InvalidCredentialException fex) { // To do // want to log exception // want to send mail // want to do some default work } catch (DBException dex) { // To do // want to log exception // want to send error details to some jms queue // want to send mail // want to do some default work } catch (Exception ex) { // To do // want to log exception } return ; }

当在模块化应用中工作时,可以理解维护代码库的痛苦。现在来看以下示例,它与上述示例做了相同的事情,但以更有效的方式:

@HandleExceptions({ @HandleException(exceptionType = UserNotExistException.class, handlers = { "logExceptionHandler", "defaultExceptionHandler" }), @HandleException(exceptionType = InvalidCredentialException.class, handlers = { "logExceptionHandler", "sendEmailExceptionHandler", "defaultExceptionHandler" }), @HandleException(exceptionType = DBException.class, handlers = { "logExceptionHandler", "JMSExceptionHandler", "sendEmailExceptionHandler", "defaultExceptionHandler" }), @HandleException(exceptionType = Exception.class, handlers = { "logExceptionHandler" }) }) public void checkLogin() { // Statement 1 which may throw new UserNotExistException("User not Exist."); // Statement 2 which may throw new InvalidCredentialException("Invalid Credentials."); // Statement 3 which may throw new DBException("Failed to retrieve data."); // Statement 4 which may throw new Exception("General Exception."); }

如果忽略注解部分(稍后介绍),任何人都可以很容易地理解它比上述示例更简单、更清晰。可以看到方法体内部没有catch块。现在该方法更清晰、更易读,并且只关注它应该做的事情。

深入了解代码库

现在开始深入代码库以理解框架。在Spring框架之上创建了这个框架。

这个框架的关键思想是在异常抛出之后但在被捕获之前拦截异常,并执行所需的操作。为了实现这一点,在Spring应用程序中使用了Spring AOP的@AfterThrowing建议。(如果不熟悉AOP(面向切面编程),请访问http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html)。

代码细节

现在来看一下代码库。在com.sm.exceptionhandler.annotation包中有三个用户定义的注解:

  • Handlers.java
  • HandleException.java
  • HandleExceptions.java

在这个框架中,有两种方法可以将处理程序与异常绑定。一种是:

@Handlers({ "fileExceptionHandler", "defaultExceptionHandler", "logExceptionHandler", "JMSExceptionHandler" }) public class UserNotExistException extends BaseException {

第二种是:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface HandleException { static final String DEFAULT_HANDLER = "defaultExceptionHandler"; public Class exceptionType() default Exception.class; public String[] handlers() default {DEFAULT_HANDLER}; }

它有两个属性。为了响应特定的异常,用户需要指定异常类型exceptionType和处理此异常的处理程序列表handlers。这里的handlers是Spring bean id。一个特定的异常可以指定多个处理程序。

如果有可能抛出多种类型的异常,并且想要单独处理它们,那么exceptionType参数将帮助解决这个问题。

@HandleExceptions({ @HandleException(exceptionType = FileNotExistException.class, handlers = { "defaultExceptionHandler", "logExceptionHandler", "JMSExceptionHandler" }), @HandleException(exceptionType = UserNotExistException.class, handlers = { "logExceptionHandler", "JMSExceptionHandler" }) }) public Integer testExceptionOne(int type) { }

或者可以编写最简单的一个,它将对每个异常起作用。

@HandleException(handlers = { "fileExceptionHandler", "defaultExceptionHandler", "logExceptionHandler" }) public void testExceptionTwo() { throw new RuntimeException("Io Exception"); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485