Java:在范围退出时自动调用函数(如 c++ 析构函数)
Posted
技术标签:
【中文标题】Java:在范围退出时自动调用函数(如 c++ 析构函数)【英文标题】:Java: call a function automatically at scope exit (like c++ destructors) 【发布时间】:2011-10-27 14:26:28 【问题描述】:对于 mysql 连接,我有一个连接对象并使用事务机制 connection.startTransaction()
、connection.commitTransaction()
、connection.rollbackTransaction()
。
对于每个startTransaction()
,必须始终调用commitTransaction()
或rollbackTransaction()
。错过这样一个电话或同时拨打这两个电话会破坏我的交易系统。
所以我按以下方式使用它们:
boolean i_am_in_a_transaction=true;
try
connection.startTransaction();
...
i_am_in_a_transaction=false;
connection.commitTransaction();
finally
if(i_am_in_a_transaction)
connection.rollbackTransaction();
这确保了声明的调用顺序,但它很费力,因为我必须在使用事务的任何地方都写这行。
在 C++ 中,如果调用了 commit()
函数,我将使用一个事务对象检查其析构函数,否则调用 rollback()
:
class Transaction
public:
Transaction()
:am_in_transaction(false)
~Transaction()
if(_am_in_transaction)
rollback();
void startTransaction()
_am_in_transaction=true;
...start Transaction...
void commit()
_am_in_transaction=false;
...commit Transaction...
void rollback()
_am_in_transaction=false;
...rollback Transaction...
private:
bool _am_in_transaction;
这样我就可以在一个地方实现逻辑并且可以非常简单地使用它:
Transaction my_transaction;
my_transaction.startTransaction;
...
my_transaction.commit();
这段代码比上面带有 try/finally 块的 java 代码要简单得多。
有没有办法在 java 中实现这种行为,而无需将逻辑专用于调用者并让他实现 try/finally 块?
在范围退出时自动调用函数之类的方法会对我有所帮助。
【问题讨论】:
Does java have a using clause? 的可能重复项 @sgusc Java 7 会自动关闭资源但不会自动调用未知函数 @JohnVint 是的,好点。我忘记了using
的语义。我把它等同于我脑海中 Python 的with
语句。感谢您的澄清!
Java 7 Automatic Resource Management JDBC的可能重复
【参考方案1】:
与 C++ 中的析构函数类似(非常重要)的是方法finalize()
。不同之处在于无法保证垃圾收集器何时真正调用它,因此不建议依赖它。
否则,你能做的最好的就是try/finally
块。
【讨论】:
更多,最多调用一次。所以即使你下次救出对象也不会执行finalize。 finalize() 不是析构函数。 JVM负责调用它。如果需要,它会在需要时调用它。因此,强烈建议不要将任何逻辑基于 finalize()。它只能用于后备。【参考方案2】:Java 没有析构函数。 但是有几个“标准”解决方案可以帮助您。
1.使用名为“模板方法”的模式。
abstract class Base
public query()
openTransaction();
doQuery();
closeTransaction();
protected abstract doQuery();
现在您应该在您创建的每个子类中实现doQuery()
,并通过从基类调用“query()”来使用它。
2.使用面向方面的编程
3.使用装饰器(包装器)模式。
4. 使用一种流行的 ORM 框架(Hibernate、iBatis 等)为您解决所有这些问题,并且完全不处理低级 JDBC 的东西。
【讨论】:
我会添加 #5,使用 Spring 的内置机制,它是 #2、#3 和 #4 的组合。 @Dave Newton,我同意你的观点,但我认为它是#4 的一部分 有点,但它们可以在 Spring 之外使用,因此有区别:)【参考方案3】:想到了几个解决方案...
正如@Dark Falcon 指出的那样,这对于try with resources 调用将是一个很好的用例,它将在尝试结束时自动清理您的资源。不幸的是,这仅在 Java 7 中可用。
Java 类确实定义了一个 finalize()
方法,当对象被垃圾回收时可以调用该方法,但重写此方法几乎从来都不是正确的做法。
如果你迷上了“在函数返回时执行代码”这个想法,我认为你唯一的选择就是使用Aspect Oriented Programming。如果您阅读了诸如AspectJ 之类的一些包或查看使用AOP with Spring,您可以做一些配置魔术,通过拦截调用来获取函数返回时执行的代码。这是 an example 在函数返回时使用 Spring AOP 执行其他代码。
【讨论】:
【参考方案4】:如果可以选择更新到 java 7,则新的 try with resources
将在 Closable
实现中执行方法 close
。
【讨论】:
【参考方案5】:我只有一种方法,而且只做一次。
public static void update(Connection connection, String updateSQL)
PreparedStatement update = null;
try
try
connection.startTransaction();
update = connection.prepareStatement(updateString);
update.executeUpdate();
finally
connection.rollbackTransaction();
connection.commitTransaction();
finally
if(update != null) update.close();
稍后
update(connection, updateSQL1);
update(connection, updateSQL2);
// etc.
【讨论】:
我不太了解你的代码(最后总是被执行——即使没有抛出异常),但我想我明白了你的想法。这在事务中只有一个 sql 语句时有效。但一个事务通常由多个语句组成。 在这种情况下,您可以传递一个 List 或String...
的 SQL 语句。
除了它们相互依赖
不确定更新如何相互依赖。有些是根据其他是否失败有条件地执行,但你不能用 SQL 写这个?但是如果你需要这个,你可以重复调用 update()。
你是对的 - 当只有更新语句时,这种方法有效。【参考方案6】:
我知道这是一个老问题,但为了他人的利益:
您可以使用实现接口的匿名内部类来完成这项工作,类似于 Java 比较器和列表排序机制的工作方式。这允许您将内部类视为执行范围。
例如修改你原来的例子:
class Transaction
boolean i_am_in_a_transaction=false;
interface AutoRollback
void runQueries() throws Throwable;
void startTransaction()
i_am_in_a_transaction=true;
...start Transaction...
void commit()
i_am_in_a_transaction=false;
...commit Transaction...
void rollback()
i_am_in_a_transaction=false;
...rollback Transaction...
public void execute(AutoRollback work)
try
work.runQueries();
catch ( Throwable t )
rollback();
throw t;
然后是如何使用它的示例:
void test() throws WhateverException
Transaction my_transaction;
my_transaction.startTransaction();
my_transaction.execute( new AutoRollback() public void runQueries() throws Throwable
... perform your queries: can be more than one, complex code, etc. ...
... local variables from the enclosing scope can be used as long as they are final...
);
my_transaction.commit();
如果您有 Java 8,使用 lambdas 会变得更漂亮,因为它保存了 new AutoRollback
语法。
如果您没有 Java 8 并且过多的标点符号仍然困扰着您,您应该能够使用注解处理器和代码注入使其读起来更漂亮。 编译时注解设置为 LOCAL_VARIABLE 的目标是您想要的,然后将其应用于my_transaction
。
...假设您的工作场所允许使用 apt 或预处理器等注释处理器,并且您希望为语法糖做那么多工作。
【讨论】:
以上是关于Java:在范围退出时自动调用函数(如 c++ 析构函数)的主要内容,如果未能解决你的问题,请参考以下文章