【问题标题】:Java design issue where behavior is attached to annotations行为附加到注释的 Java 设计问题
【发布时间】:2015-09-29 07:27:33
【问题描述】:

假设我通过使用@transactions 注释来使用JPA

所以要让任何方法在事务下运行,我添加了一个@transaction 注释,然后我的方法在事务下运行。

要实现上述目标,我们需要为类提供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] 调用一些服务(长请求/响应周期,比如说 5 秒)并获取结果

[2] 执行一些jpa实体修改

现在由于这个方法调用很长,我们不想让事务长时间保持打开状态,所以我们更改代码,使 [2] 发生在单独的 tx 中,methodThatRunUnderTx 不会在事务中运行

所以我们将从methodThatRunUnderTx 中删除@Transaction 并在类中添加另一个方法@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();
}

现在的问题

我们已通过interfacemethodThatRunUnderTx2() 公开

但这不是我们想要公开为 FooI 的 api 的,也不打算从外部调用..

有什么解决办法吗?

【问题讨论】:

    标签: java dependency-injection transactions annotations jpa-2.0


    【解决方案1】:

    这就是现代容器不需要实现任何接口的原因 - 然后通过动态子类化或使用字节码检测来创建代理。

    因此,您的设计问题的解决方案很简单:实现一个包含事务方法的辅助类,并将其注入实现接口的类(以及任何其他可以从中受益的类)。

    【讨论】:

      【解决方案2】:

      遵循接口分离原则,将两个逻辑操作分成两个接口:一个fetcher和一个modifier。将两者都注入到Bar 类中。这允许两个逻辑实现相互独立地改变,例如允许一个是事务性的,而另一个不是。第二个接口不必是公共类。

      【讨论】:

        【解决方案3】:

        这个问题是关于处理事务部分的一个非常有效的问题。但是,如果您试图隐藏一种功能而不是其他功能,则需要考虑这些:

        选项 1:

        1. 考虑 - 您需要公开执行调用者所需的全部功能的方法

        2. 在这种事务处理的情况下,我建议你暂时保持事务打开直到它完成

        选项 2:

        1. 考虑 - 您需要有效地管理交易
        2. 根据功能IModifyFooISelectFoo拆分接口的方法,分别修改和选择并实现方法并在所需方法上使用@Transactional进行注释
        3. 接口设计为公开的,这意味着您需要了解需要向外部世界公开哪些内容。在这种情况下,您需要选择 Principle 而不是技术挑战。

        我只能想到这些选项,我们正在尝试解决您在 Java 基础上的技术挑战。值得考虑。

        【讨论】:

          【解决方案4】:

          正如你所说,如果你在同一个 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
             }
          }
          

          这样您就不会向公共 api 公开任何额外的东西,但您将有一些额外的代码来管理事务。

          【讨论】:

          • 我们开始使用基于注释的事务来管理我们自己的 userTransaction...这违背了使用基于注释的事务的目的
          • 是的,我知道,通常当你遇到这个问题时,你可以解决改变你的类的设计,但有时它必须按照你发布的那样工作,在这种情况下,你的选择就是你所做的或者我发布的那个
          【解决方案5】:

          如果您希望该 methodThatRunUnderTx2 不公开,请将其设置为私有方法并删除 @Override 注释并将其从接口中删除。

          【讨论】:

            【解决方案6】:

            您必须接受基于事务的注释不适用于私有方法。所以你根本不能隐藏(私有)一个应该是那种注释的主题的方法。

            您可以摆脱接口(即 EJB 世界中的 @LocalBean),但仍然不能使用私有方法...

            【讨论】:

              【解决方案7】:

              这个问题的解决方案肯定是acpects。他们将允许从public void methodThatRunUnderTx() 的主体中删除self.methodThatRunUnderTx2() 方法调用。这个问题的答案很可能对您有所帮助:Aspectj and catching private or inner methods

              但是我不确定方面是否对于这个问题来说不是太大的枪,因为它们会增加代码的复杂性和可读性。我宁愿考虑以这样一种方式改变你的代码架构,这样你的问题就无关紧要了。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-08-19
                • 1970-01-01
                • 1970-01-01
                • 2018-04-21
                • 2016-11-04
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多