【发布时间】:2023-03-15 04:45:01
【问题描述】:
我最近惊讶地发现这个播放框架控制器动作代码中的 finally 块只在异常之后被调用,但在调用实际成功时从未被调用。
try {
InputStream is = getInputStreamMethod();
renderBinary(is, "out.zip");
catch (Exception e) {
e.printStackTrace();
} finally {
cleanUp();
}
也许当调用 renderBinary() 时线程被终止或发生了什么,但对我来说,这是不直观的。我怀疑其他 render() 调用也会发生同样的事情,但我没有验证它。
我通过将 renderBinary() 移到 try/catch 之后解决了这个问题。进一步的调查显示 play 提供了一个 @Finally 注释来创建一个在控制器操作执行后执行的方法。这里需要注意的是,这将在控制器中执行任何操作后被调用,因此它可能并不总是一个好的选择。
我的问题是:为什么 finally 块在 renderBinary() 之后没有被执行,这在任何地方都有记录吗?我在播放文档中找不到任何参考。
澄清导致这一发现的事件顺序:
由于 finally 块而应该被删除的文件没有被删除。
认为这不可能是由非执行 finally 块引起的,我更改了方法以使用 Amazon SQS 消息队列在 finally 块中发送消息 - 一个单独的作业接收消息并删除相关文件。
消息未发送。
我在代码中设置了断点,并确定正在调用 renderBinary,但 finally 块没有得到执行。
为了安全起见,我在 finally 子句中添加了日志消息,但这些也不存在。
我已经重复了几次调试练习,每次都没有执行 finally 子句。
(请注意,实际的代码与上面的看起来并不完全一样。这是一个非常简化的示例,只是为了说明情况。)
【问题讨论】:
-
我所知道的唯一可以阻止 finally 运行的是
System.exit()、JVM 崩溃或中断线程。renderBinary()是否有任何你知道的线程停止? -
我不知道。这就是我希望有人能回答的问题。当它发生时,我感到非常惊讶——最终我们的云服务器上出现了一堆本应被删除的文件!
-
当文件没有被删除时,我通过断点确定finally子句没有被执行。不过感谢您的意见。
-
但是您是否也验证过您的
try块是否已完成?这是没有执行finally块的主要原因。除了代码永远挂起的可能性,正如 CodeChimp 已经说过的那样,JVM 终止也可能阻止finally块的执行。 -
我在上面的消息中添加了导致此发现的步骤。 try 发生了,renderBinary 发生了,finally 没有发生。
标签: java playframework try-catch-finally finally try-finally