行为附加到注释的Java设计问题
假设我通过使用行为附加到注释的Java设计问题,java,dependency-injection,transactions,annotations,jpa-2.0,Java,Dependency Injection,Transactions,Annotations,Jpa 2.0,假设我通过使用@transactions注释来使用JPA 因此,为了让任何方法在事务下运行,我添加了一个@transaction注释,并在事务下运行BINGO我的方法 为了实现上述目标,我们需要为类提供一个接口,实例由某个容器管理。 此外,我应该始终从接口引用调用该方法,以便代理对象可以启动事务。 因此,我的代码如下所示: class Bar { @Inject private FooI foo; ... void doWork() { foo.methodT
@transactions
注释来使用JPA
因此,为了让任何方法在事务下运行,我添加了一个@transaction
注释,并在事务下运行BINGO我的方法
为了实现上述目标,我们需要为类提供一个接口
,实例由某个容器管理。此外,我应该始终从接口引用调用该方法,以便代理对象可以启动事务。 因此,我的代码如下所示:
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();
}
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();
}
很好
现在让我们假设在Tx下运行的方法执行两个逻辑操作
[1] 调用一些服务(长的请求/响应周期,比如说5秒)并获取结果
[2] 执行一些jpa实体修改
现在,由于这个方法调用很长,我们不想让事务长时间处于打开状态,所以我们更改了代码,使[2]在单独的tx中发生,而在tx下运行的方法不在事务中运行
因此,我们将从Tx下运行的方法中删除@Transaction
,并在类中添加另一个@Transaction
方法,假设新方法是Tx2下运行的方法,现在,要从运行在Tx下的方法调用此方法,我们必须将其注入自身,并向接口添加一个方法,以便通过代理对象进行调用
因此,现在我们的代码如下所示:
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();
}
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();
}
现在问题解决了
我们通过接口
将在TX2()下运行的方法
公开
但这并不是我们想要公开为FooI
的api,也不是为了从外部调用它
有没有解决问题的建议?正如您所说,如果您在同一个bean上调用一个方法,它将不会被代理,因此不会发生事务管理,要解决这个问题,您可以在手动启动和停止事务的情况下使用bean管理的事务:
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
}
}
这样,您就不会向公共api公开任何额外的内容,但您将有一点额外的代码来管理事务。遵循接口分离原则,将两个逻辑操作分离为两个接口:一个获取程序和一个修改器。将两者都注入类栏。这允许两个逻辑实现相互独立地更改,例如,允许一个是事务性的,而另一个不是事务性的。第二个接口不必是公共类。关于处理事务部分,这个问题非常有效。但是,如果您试图隐藏一个功能,则需要考虑以下事项:
选项1:
考虑-您需要公开完成调用者所需全部功能的方法
在这种情况下的交易处理,我会建议您保持交易开放的时间,直到它完成
选项2:
考虑-您需要高效地管理事务
基于功能性IModifyFoo
和ISelectFoo
拆分接口方法,分别修改和选择并实现方法,并在所需方法上使用@Transactional
进行注释
接口设计为公共的,这意味着您需要知道需要向外部世界公开什么。在这个场景中,您需要选择原则而不是技术挑战李>
我可以想到这些选项,我们正试图解决您在java基础上面临的技术挑战。考虑一下很好。如果您希望在TX2下运行的方法不会成为公共方法,请将其设置为私有方法并删除@Override注释,然后将其从接口中删除 这就是为什么现代容器不需要实现任何接口——然后通过动态子类化创建代理,或者使用字节码插装
因此,您的设计问题的解决方案很简单:实现一个包含事务方法的helper类,并将其注入实现接口的类(以及可以从中受益的任何其他类)。您必须接受基于事务的注释在私有方法上不起作用。因此,您不能简单地隐藏(使私有)一个应该是这种注释主题的方法
您可以去掉接口(即EJB世界中的@LocalBean),但仍然不能使用私有方法…确保此问题的解决方案是acpect。它们将允许从public void methodthattrunundertx()
的主体中除去self.methodthattrunundertx2()
方法调用。这个问题的答案很可能会帮助您:
然而,我不确定方面对于这个问题来说是否太重要,因为它们增加了代码的复杂性和可读性。我宁愿考虑以这样一种方式更改代码的体系结构,这样您的问题就不重要了。我们开始使用基于注释的事务,而不是自己管理用户事务。。。这违背了使用基于注释的事务处理的目的是的,我知道,通常当你面对这个问题时,你可以通过改变类的设计来解决,但有时你必须按照你发布的方式工作,在这种情况下,你的选项就是你做过的或者我发布过的