【问题标题】:Implementing AutoCloseable - How do I know if an exception occurred in the try block?实现 AutoCloseable - 我如何知道 try 块中是否发生异常?
【发布时间】:2023-03-26 13:30:02
【问题描述】:

我们已经编写了一个类,它打开了与服务器的连接。完成后,如果一切顺利,您需要告诉它commit,或者如果出现问题,则告诉它rollback。所以现在我们的代码中有很多地方看起来像这样:

OurConnectionClass conn = null;
try {
    conn = OurConnectionClass(parameters);
    // Do some stuff here...
    conn.commit();
} catch (Throwable t) {
    if (conn != null) {
        conn.rollback();
    }
    throw t;
}

如果您忘记提交或回滚,不会立即出现问题,但最终您会耗尽连接池,然后必须找出错误的位置。

我想找到一种方法让OurConnectionClass 实现AutoClosable,所以我可以这样做:

try (OurConnectionClass conn = new OurConnectionClass(parameters)) {
    // Do some stuff here...
}

我觉得必须有办法做到这一点,但我没有看到它。 AutoCloseable 仅调用 close 方法,没有传递任何参数。据我所知,无法知道是否调用 close 是因为成功到达 try 块的末尾还是抛出了异常。

【问题讨论】:

  • 既然我已经把这一切都写完了,我想知道我是否应该使用匿名函数而不是尝试使用try-with-resources?我可以让OurConnectionClass 接收它并尝试运行它,如果它成功,它会在之后运行commit(),如果它失败,它将在之后运行rollback()...我可以让这是一个静态方法什么都不返回,而不是返回一个实例。这听起来可能比我想象的要复杂一些……在尝试之前,我会等着看其他人对此的看法……
  • AutoClosable 你的意思是AutoCloseable ?你能具体说明你指的是什么接口吗?
  • 注意,如果构造函数失败并抛出异常,则不会调用 close()。
  • 根据JLS,您可以在处理异常的try-with-resource-block之后定义catch-block。
  • @Leviand - 我认为从上下文(我说的是 Java 的 try-with-resource 的事实)应该很明显,我的意思是标准 Java 库中的 AutoCloseable 接口,而不是第三方的东西。无论如何,你是对的,我在几个地方拼错了这个名字。我已经修好了。

标签: java try-with-resources autocloseable


【解决方案1】:

当执行这个 sn-p.

try (OurConnectionClass conn = new OurConnectionClass(parameters)) {
    // Do some stuff here...
    conn.commit();
}

OurConnectionClass.close() 将始终在创建实例后调用。因此,您可以添加一些逻辑来检查是否进行了提交。例如。带有boolean 标志。然后,您可以检查close() 方法是应该和平关闭连接还是应该回滚:

public class OurConnectionClass implements AutoCloseable{

    private boolean committed; // initialized to false

    public void commit(){
         // commit
         committed = true;
    }

    public void close() throws Exception{
         if(!committed){
             // rollback
         }
    }
}

【讨论】:

  • 大概是想做committedvolatile
  • @Bohemian:为什么?
  • @Bohemian 我不太确定,但try-with-resources-finally 块中的逻辑是否在不同的线程中执行?因为如果我正确理解了这个问题,twr 块内不应该有多个线程访问conn
  • 是的...这听起来确实比我们现在正在做的事情有所改进,但是当开发人员忘记提交并且不知道发生了什么时,我会担心浪费时间。我可以让它在没有提交的情况下调用 close 调用回滚并引发异常。我从来没有见过在没有发生异常的情况下使用回滚。我想我会深入研究使用 lambdas 来代替......
【解决方案2】:

我认为您想要的语义是事务在关闭时回滚,除非使用 OurConnectionClass 显式调用 OurConnectionClass.commit() 的代码。

那么你没有任何问题,因为你的close方法,那么只需要测试是否有一个打开的事务。如果有回滚,并记录错误。

【讨论】:

    【解决方案3】:

    两者都做!

    try (OurConnectionClass conn = new OurConnectionClass(parameters)) {
        // Do some stuff here...
        conn.commit();
    } catch (Throwable t) {
        conn.rollback();
        throw t;
    }
    

    如果发生爆炸,可关闭对象仍会自动关闭(在隐式 finally 块中)。

    顺便说一句,最好抛出域异常:

    throw new MyStuffExploded(t);
    

    因为重新抛出连接异常会让实现细节通过方法契约泄露出去,这是一种耦合形式,很糟糕。

    【讨论】:

    • 应该只使用 Throwable t 还是应该重新抛出它?
    猜你喜欢
    • 2013-02-23
    • 1970-01-01
    • 2016-04-20
    • 1970-01-01
    • 2020-11-10
    • 2017-09-04
    • 1970-01-01
    • 2011-04-17
    • 2020-11-19
    相关资源
    最近更新 更多