Java 行为附加到注释的设计问题
Java design issue where behavior is attached to annotations
假设我通过使用 @transactions
注释来使用 JPA
。
因此,为了在事务下使用任何方法 运行,我在事务下添加了一个 @transaction
注释和 BINGO 我的方法 运行。
为了实现上述目标,我们需要 class 有一个 interface
并且该实例由某个容器管理。
此外,我应该始终从接口引用中调用该方法,以便代理对象可以启动事务。
所以我的代码看起来像:
class Bar {
@Inject
private FooI foo;
...
void doWork() {
foo.methodThatRunUnderTx();
}
}
class FooImpl implements FooI {
@Override
@Transaction
public void methodThatRunUnderTx() {
// code run with jpa context and transaction open
}
}
interface FooI {
void methodThatRunUnderTx();
}
很好很好
现在假设methodThatRunUnderTx
做两个逻辑运算
[1] 调用一些服务(长 request/response 周期假设 5 秒)并获取结果
[2] 执行一些 jpa 实体修改
现在由于这个方法调用很长而且我们不想长时间保持交易打开,所以我们更改代码以便 [2] 发生在单独的 tx 而 methodThatRunUnderTx
不 运行 在交易中
所以我们将从 methodThatRunUnderTx
中删除 @Transaction
并在 class 中添加另一个方法 @transaction
假设新方法是 methodThatRunUnderTx2
,现在要从 methodThatRunUnderTx
调用此方法,我们必须将其注入自身并向接口添加一个方法,以便通过代理对象进行调用。
所以现在我们的代码将如下所示:
class Bar {
@Inject
private FooI foo;
...
void doWork() {
foo.methodThatRunUnderTx();
}
}
class FooImpl implements FooI {
@Inject
private FooI self;
@Override
//@Transaction -- remove transaction from here
public void methodThatRunUnderTx() {
...
self.methodThatRunUnderTx2();// call through proxy object
}
@Override
@Transaction //add transaction from here
public void methodThatRunUnderTx2() {
// code run with jpa context and transaction open
}
}
interface FooI {
void methodThatRunUnderTx();
void methodThatRunUnderTx2();
}
现在的问题
我们通过interface
.[=27将methodThatRunUnderTx2()
变成了public =]
但这不是我们想要公开的 FooI
的 api 并且不打算从外部调用..
有什么解决的建议吗?
正如您所说,如果您在同一个 bean 上调用一个方法,它将不会被代理,因此不会发生事务管理,要解决这个问题,您可以在 Bean Managed Transaction 中手动启动和停止事务:
class FooImpl implements FooI {
@Resource
private UserTransaction userTransaction;
@Override
//@Transaction -- remove transaction from here
public void methodThatRunUnderTx() {
...
self.methodThatRunUnderTx2();// call through proxy object
}
@Override
//@Transaction -- remove transaction from here too, because now you'll manage the transaction
public void methodThatRunUnderTx2() {
userTransaction.start();
// code run with jpa context and transaction open
userTransaction.commit(); // Commit or rollback do all the handling, i'm not writing it because its just an example
}
}
这样你就不会向 public api 暴露任何额外的东西,但你会有一些额外的代码来管理交易。
遵循接口隔离原则,将两个逻辑操作分离到两个接口:一个fetcher和一个modifier。将两者都注入 class Bar
。这允许两个逻辑实现相互独立地改变,例如允许一个是事务性的而另一个不是。第二个接口不必是 public class.
这个问题是关于处理交易部分的一个非常有效的问题。但是,如果您试图隐藏一项功能而不是其他功能,则需要考虑这些:
选项 1:
考虑 - 您需要公开执行调用者所需的全部功能的方法
在这种交易处理的情况下,建议您暂时保持交易开启,直到交易完成
选项 2:
- 正在考虑 - 您需要有效地管理交易
- 根据功能
IModifyFoo
和 ISelectFoo
分别修改和 select 拆分接口的方法,并实现这些方法并在所需方法上用 @Transactional
注释
- 接口设计为 public,这意味着您需要了解需要向外部世界公开什么。在这种情况下,您将选择原则而不是技术挑战。
我只能想到这些选项,我们正在努力解决您在此处遇到的基于 java 基础知识的技术挑战。好好想想。
如果您希望该方法 ThatRunUnderTx2 不会成为 public 将其设为私有方法并删除 @Override 注释并将其从接口中删除。
这就是现代容器不需要实现任何接口的原因 - 然后通过动态子classing 或使用字节码工具创建代理。
因此,您的设计问题的解决方案很简单:实现一个包含事务方法的助手 class,并将其注入实现接口的 class(以及任何其他 class 可以从中受益)。
您必须接受基于事务的注释不适用于私有方法。因此,您根本无法隐藏(设为私有)应该是此类注释主题的方法。
您可以摆脱接口(即 EJB 世界中的@LocalBean),但是您仍然不能使用私有方法...
可以肯定的是,这个问题的解决方案是正确的。他们将允许从 public void methodThatRunUnderTx()
的主体中删除 self.methodThatRunUnderTx2()
方法调用。这个问题的答案很可能对您有所帮助:Aspectj and catching private or inner methods
但是我不确定方面对于这个问题是否不是太大的问题,因为它们增加了代码的复杂性和可读性。我宁愿考虑以这样一种方式更改代码的体系结构,这样您的问题就无关紧要了。
假设我通过使用 @transactions
注释来使用 JPA
。
因此,为了在事务下使用任何方法 运行,我在事务下添加了一个 @transaction
注释和 BINGO 我的方法 运行。
为了实现上述目标,我们需要 class 有一个 interface
并且该实例由某个容器管理。
此外,我应该始终从接口引用中调用该方法,以便代理对象可以启动事务。
所以我的代码看起来像:
class Bar {
@Inject
private FooI foo;
...
void doWork() {
foo.methodThatRunUnderTx();
}
}
class FooImpl implements FooI {
@Override
@Transaction
public void methodThatRunUnderTx() {
// code run with jpa context and transaction open
}
}
interface FooI {
void methodThatRunUnderTx();
}
很好很好
现在假设methodThatRunUnderTx
做两个逻辑运算
[1] 调用一些服务(长 request/response 周期假设 5 秒)并获取结果
[2] 执行一些 jpa 实体修改
现在由于这个方法调用很长而且我们不想长时间保持交易打开,所以我们更改代码以便 [2] 发生在单独的 tx 而 methodThatRunUnderTx
不 运行 在交易中
所以我们将从 methodThatRunUnderTx
中删除 @Transaction
并在 class 中添加另一个方法 @transaction
假设新方法是 methodThatRunUnderTx2
,现在要从 methodThatRunUnderTx
调用此方法,我们必须将其注入自身并向接口添加一个方法,以便通过代理对象进行调用。
所以现在我们的代码将如下所示:
class Bar {
@Inject
private FooI foo;
...
void doWork() {
foo.methodThatRunUnderTx();
}
}
class FooImpl implements FooI {
@Inject
private FooI self;
@Override
//@Transaction -- remove transaction from here
public void methodThatRunUnderTx() {
...
self.methodThatRunUnderTx2();// call through proxy object
}
@Override
@Transaction //add transaction from here
public void methodThatRunUnderTx2() {
// code run with jpa context and transaction open
}
}
interface FooI {
void methodThatRunUnderTx();
void methodThatRunUnderTx2();
}
现在的问题
我们通过interface
.[=27将methodThatRunUnderTx2()
变成了public =]
但这不是我们想要公开的 FooI
的 api 并且不打算从外部调用..
有什么解决的建议吗?
正如您所说,如果您在同一个 bean 上调用一个方法,它将不会被代理,因此不会发生事务管理,要解决这个问题,您可以在 Bean Managed Transaction 中手动启动和停止事务:
class FooImpl implements FooI {
@Resource
private UserTransaction userTransaction;
@Override
//@Transaction -- remove transaction from here
public void methodThatRunUnderTx() {
...
self.methodThatRunUnderTx2();// call through proxy object
}
@Override
//@Transaction -- remove transaction from here too, because now you'll manage the transaction
public void methodThatRunUnderTx2() {
userTransaction.start();
// code run with jpa context and transaction open
userTransaction.commit(); // Commit or rollback do all the handling, i'm not writing it because its just an example
}
}
这样你就不会向 public api 暴露任何额外的东西,但你会有一些额外的代码来管理交易。
遵循接口隔离原则,将两个逻辑操作分离到两个接口:一个fetcher和一个modifier。将两者都注入 class Bar
。这允许两个逻辑实现相互独立地改变,例如允许一个是事务性的而另一个不是。第二个接口不必是 public class.
这个问题是关于处理交易部分的一个非常有效的问题。但是,如果您试图隐藏一项功能而不是其他功能,则需要考虑这些:
选项 1:
考虑 - 您需要公开执行调用者所需的全部功能的方法
在这种交易处理的情况下,建议您暂时保持交易开启,直到交易完成
选项 2:
- 正在考虑 - 您需要有效地管理交易
- 根据功能
IModifyFoo
和ISelectFoo
分别修改和 select 拆分接口的方法,并实现这些方法并在所需方法上用@Transactional
注释 - 接口设计为 public,这意味着您需要了解需要向外部世界公开什么。在这种情况下,您将选择原则而不是技术挑战。
我只能想到这些选项,我们正在努力解决您在此处遇到的基于 java 基础知识的技术挑战。好好想想。
如果您希望该方法 ThatRunUnderTx2 不会成为 public 将其设为私有方法并删除 @Override 注释并将其从接口中删除。
这就是现代容器不需要实现任何接口的原因 - 然后通过动态子classing 或使用字节码工具创建代理。
因此,您的设计问题的解决方案很简单:实现一个包含事务方法的助手 class,并将其注入实现接口的 class(以及任何其他 class 可以从中受益)。
您必须接受基于事务的注释不适用于私有方法。因此,您根本无法隐藏(设为私有)应该是此类注释主题的方法。
您可以摆脱接口(即 EJB 世界中的@LocalBean),但是您仍然不能使用私有方法...
可以肯定的是,这个问题的解决方案是正确的。他们将允许从 public void methodThatRunUnderTx()
的主体中删除 self.methodThatRunUnderTx2()
方法调用。这个问题的答案很可能对您有所帮助:Aspectj and catching private or inner methods
但是我不确定方面对于这个问题是否不是太大的问题,因为它们增加了代码的复杂性和可读性。我宁愿考虑以这样一种方式更改代码的体系结构,这样您的问题就无关紧要了。