`
hippoom
  • 浏览: 8449 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

如何设计JAVA自定义异常

 
阅读更多

 

一、受检异常还是非受检异常?

       在绝大多数的情况下,系统报告了错误,我们都很难处理,最简单(有时甚至是唯一的办法)就是直接把错误报告给用户,这时我推荐使用非受检异常(即直接或间接继承RuntimeException)。比如:在下单时,发现没有库存了,这个错误是由于不满足业务规则。

PlaceOrderService.java

if (noInventory(productCode, quantity)) {
    throw new InsufficientInventoryException(productCode, quantity);
}

 InsufficientInventoryException.java

public class InsufficientInventoryException extends RuntimeException {
    public InsufficientInventoryException(String productCode, int quantity) {
        super("库存不足" + quantity + ",产品代码=" + productCode);
    }
}

       这样PlaceOrderService.java的客户端(可能是个MVC Controller),只需要简单地捕获了所有的Exception并报告用户即可了。

       另外,如果异常是由系统比较低层级的组件抛出的,如果选择受检异常,那么该组件的客户端及该客户端的客户端(可能会有很多嵌套)都必须在相关的方法中声明会抛出该异常,造成添加了一个底层组件,要改动大量的高层组件,使得添加新的异常非常麻烦,所以有时候干脆选择将一些系统底层(可能是框架)抛出的受检异常包裹成非受检异常,比如Spring framework就将受检异常SQLException包裹在自定义的非受检异常DataAccessException中。

 

二、异常中要包含哪些信息

       之前看过一位高人的文章,觉得总结的很到位,一定要包括描述和上下文。你是否遇到这样的情况:有个任务要分析日志,检查系统运行情况,结果你在日志看到一句:

Error!Cannot cancel order!”然后就没了

这个时候,真心郁闷啊,既不知道是哪张订单,也不知道为什么不能取消订单,只能查看代码:

throw new CannotCancelOrderException();

 如果当初是这样就好了:

throw CannotCancelOrderException.hasBeenLocked(orderId);
public CannotCancelOrderException(String orderId) {
    super("order["+ orderId + "] has been locked");
}

 

如果你的异常是由其他异常导致的,可别把cause落下了:

} catch (SupplierAccessException e) {
    throw new CannotCancelOrderException(orderId, e);
}

 

public CannotCancelOrderException(String orderId, Throwable t) {
      super("Cannot cancel order[" + orderId + "], due to " + t.getMessage(), t);
}

 

三、怎么让异常帮助报告错误

       当发生错误时,怎么告知用户是什么原因呢?比如在一个MVC Controller中,调用了下单服务:

String orderId = placeOrderService.placeOrder(productCode, quantity, price); 

由于使用了Spring framework来提供声明式事务,所以如果下单时如果发生错误,placeOrder()会抛出异常。为了不让用户看到500报错及满屏的错误堆栈,我们使用try/catch块捕获错误:

try {
    orderId = placeOrderService.placeOrder(productCode, quantity, price); 
} catch (Exception e) {
    model.addAttribute("statusCode", UNKNOWN);
}

但是如果捕获Exception的话,我们就不知道是什么原因导致的错误了,但如果去捕获每个异常的话catch篇幅会很大,而且如果要新增一个异常,就要回来修改catch,维护起来很麻烦:

try {
    orderId = placeOrderService.placeOrder(productCode, quantity, price); 
} catch (InsufficientInventoryException e) {
    model.addAttribute("statusCode", INSUFFICIENT_INVENTORY);
} catch (ExpriedPriceException e) {
    model.addAttribute("statusCode", EXPIRED_PRICE);
} catch (NoSuchProductException e) {
    model.addAttribute("statusCode", NO_SUCH_PRODUCT);
} catch (Exception e) {
    model.addAttribute("statusCode", UNKNOWN);
}

 由于这些异常都是自定义的,我们可以将其设计成一个体系(Hierachy):

try {
    orderId = placeOrderService.placeOrder(productCode, quantity, price); 
} catch (UncheckedApplicationException e) {
    model.addAttribute("statusCode", e.getStatusCode);
} catch (Exception e) {
    model.addAttribute("statusCode", UNKNOWN);
}

 要新增异常的话,只需要让其继承UncheckedApplicationException并实现getStatusCode()即可。同样的,如果除了要告知statusCode,还需要返回本地化的提示信息,还可以加上getI18nCode()、getArgs()这样的方法:

try {
    orderId = placeOrderService.placeOrder(productCode, quantity, price); 
} catch (UncheckedApplicationException e) {
    model.addAttribute("statusCode", e.getStatusCode);
    model.addAttribute("message", messageSource.getMessage(e, locale));
} catch (Exception e) {
    model.addAttribute("statusCode", UNKNOWN);
    model.addAttribute("message", messageSource.getUnknownError(locale));
}

messageSource:

public String getMessage(UncheckedApplicationException e, Locale locale) {
    return getMessage(e.getI18nCode(), e.getArgs(), locale);
}

 

四、tell,don't ask

    合理的异常构造函数签名可以帮助简化message的拼装,比如在第二部分的

 

public CannotCancelOrderException(String orderId, Throwable cause) {
      super("Cannot cancel order[" + orderId + "], due to " + t.getMessage(), cause);
}
就是个不错的例子,它的客户端只需要传入orderId及cause即可,如果orderId能使用领域对象就更好了(可以防止误把其他String对象当成orderId传入),比如:

 

 

public CannotCancelOrderException(OrderIdentifier orderId, Throwable cause) {
而这个签名如果是这样的话,要抛出它的人就辛苦了些:

 

 

public CannotCancelOrderException(String message, Throwable cause) {
    super(message, cause);
}
Client:

 

 

throw new CannotCancelOrderException("Cannot cancel order[" + orderId + "] due to " + e.getMessage(), e);
如果有多个地方会抛出这个异常,那么除了拼装message麻烦外,不免还会有代码重复。此外对于单元测试来说,也不得不添加琐碎的代码,比如下面一段使用JMock做单元测试的代码:

 

 

final String message = "[" + username + "] login [failure] for ["
				+ cause + "]";
context.checking(new Expectations() {
    {
        oneOf(loginService).login(username, password);
        will(throwException(new CannotLoginException(message, cause)));
    }
});
 
好了,就介绍到这里,如果你有更好的想法,请务必更贴哦

 

 

 

 

 

分享到:
评论

相关推荐

    实际项目中java自定义异常

    在实际的Java开发项目中,自定义异常是提高代码可读性和可维护性的重要手段。异常处理是程序设计的关键部分,它有助于捕获并处理在程序执行过程中可能出现的错误或异常情况。Java提供了丰富的异常处理机制,包括预...

    java 自定义异常实例二

    下面我们将深入探讨Java自定义异常及其应用。 首先,自定义异常通常是通过扩展Java内置的`Exception`类或其子类来实现的。`Exception`类是所有可抛出异常的基类,它本身继承自`Throwable`类。创建自定义异常时,...

    Java自定义异常类_1.txt

    ### Java自定义异常类详解 #### 一、Java异常体系概览 在Java语言中,异常处理机制是一种用于处理程序运行时错误的重要机制。Java中的异常处理基于`java.lang.Throwable`类,它有两个重要的子类:`Exception`和`...

    异常类:自定义异常类

    本文将详细介绍如何在 Java 中创建自定义异常类,并通过一个示例来展示如何使用这些自定义异常。 #### 创建自定义异常类 自定义异常类是指开发者根据实际需求自定义的异常类型。在 Java 中,创建自定义异常类通常...

    Java自定义异常类_2.txt

    ### Java自定义异常类知识点详解 #### 一、概述 在Java编程中,自定义异常是一种常见的编程实践,它能够帮助开发者更精确地控制程序的行为并处理错误情况。通过创建自定义异常类,我们可以根据应用的具体需求来抛...

    浅谈Java自定义异常在教学中的教与学

    自定义异常是Java异常处理机制中的一个重要组成部分,其在教学和实际软件开发中的重要性不容忽视。 首先,了解自定义异常出现的背景是必要的。Java的标准异常库为开发者提供了丰富的异常类,例如空指针异常、数组...

    自定义异常

    如果这个组件支持异常处理,那么开发者可能会在自定义异常上下文中使用它,例如,当用户在界面上输入无效数据时抛出自定义异常。 总结来说,自定义异常是软件开发中的重要实践,它允许我们定制错误处理策略,使代码...

    Java异常处理-自定义异常类及课后练习

    课后练习通常包括针对自定义异常类的实战应用,例如编写不同场景的异常处理代码,模拟业务中的错误情况,以及设计和解答相关的笔试题。通过这些练习,可以加深对自定义异常理解和使用方法的掌握。 ### 三、小结与小...

    java自定义线程模型处理方法分享

    Java自定义线程模型在软件开发中扮演着重要的角色,特别是在高性能、高并发的应用场景,如游戏服务器。本文将深入探讨如何在Java中构建自定义线程模型,并分享一些实践经验。 首先,我们要明白为什么要自定义线程...

    JAVA实验九异常处理.pdf

    在Java中可以通过继承Exception类或其子类来创建自定义异常。例如在提供的内容中,sanjiao方法中,如果三角形的三边长度不符合勾股定理,会通过throw new IllegalArgumentException()抛出自定义异常。 4. finally块...

    如何创建和使用自定义异常

    ### 如何创建和使用自定义异常 在Java编程语言中,异常处理机制是十分重要的一个组成部分,它能够帮助开发者有效地捕获并处理程序运行时...在实际开发中,可以根据不同的业务需求和异常场景来设计和使用自定义异常。

    java 自定义Queue队列

    总的来说,自定义Java的Queue队列需要对数据结构有深入的理解,并根据具体的应用场景来设计和实现。这包括选择合适的基础数据结构(如ArrayList、LinkedList等),考虑性能和并发访问等因素,以及正确实现Queue接口...

    382.380.JAVA基础教程_异常处理-如何自定义异常(382).rar

    在这个“JAVA基础教程_异常处理-如何自定义异常”的教程中,我们将深入探讨Java异常处理机制以及如何创建自定义异常。 在Java中,异常是一种特殊的对象,表示在程序执行过程中发生的问题。Java使用了“try-catch-...

    Java《面向对象程序设计》实验报告六

    首先,自定义异常类`MyException`被创建,它继承自Java的`Exception`类。`Exception`类是所有检查异常(checked exception)的基类,它继承自`Throwable`。在`MyException`类中,只有一个带有字符串参数`msg`的构造...

    Java自定义过滤器

    本篇文章将围绕“Java自定义过滤器”这一主题展开,详细介绍自定义过滤器的设计与实现。 #### 二、基本概念 1. **过滤器接口**:`javax.servlet.Filter`是Java Web应用中的过滤器接口,所有自定义过滤器都必须实现...

    业务异常提示处理 springboot+Assert(自定义断言)

    7. **模块化设计**:项目名为"framework",暗示可能包含了一套基础框架,这样的设计可以将通用的功能模块化,如日志、异常处理、验证等,使得新项目的开发更加高效,同时也能保证各个组件的独立性和可扩展性。...

    实现一个自定义异常类IntegerException

    在深入探讨如何实现一个自定义异常类`IntegerException`之前,我们先理解一下在Java编程语言中异常处理的重要性。异常处理是程序设计中一个至关重要的部分,它允许程序员捕获和响应程序运行时可能发生的错误情况,...

    处理异常java程序实验报告

    ### 处理异常Java程序实验报告知识...通过本次实验,不仅深入理解了Java异常处理的基本原理,还学会了如何自定义异常类以及如何在实际代码中应用这些异常处理逻辑。这对于开发高质量、健壮的Java应用程序具有重要意义。

Global site tag (gtag.js) - Google Analytics