@Transactionalアノテーションはどこに置くべきですか:インターフェース定義または実装クラスですか?


91

コードのタイトルからの質問:

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

vs

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}

回答:


121

http://static.springsource.org/spring/docs/2.0.x/reference/transaction.htmlから

Springチームの推奨事項は、@Transactionalインターフェースに注釈を付けるのではなく、具体的なクラスにのみ注釈を付けることです。確かに、@Transactionalアノテーションをインターフェイス(またはインターフェイスメソッド)に配置できますが、これは、インターフェイスベースのプロキシを使用している場合に期待されるとおりにのみ機能します。アノテーションが継承さないという事実は、クラスベースのプロキシを使用している場合、トランザクション設定がクラスベースのプロキシインフラストラクチャによって認識されず、オブジェクトがトランザクションプロキシにラップされないことを意味します(これは明らかに悪いことです) 。したがって、Springチームのアドバイスを受けて、具象クラス(および具象クラスのメソッド)にのみ注釈を付けてください@Transactional

注:このメカニズムはプロキシに基づいているため、プロキシを介して着信する「外部」メソッド呼び出しのみがインターセプトされます。つまり、「自己呼び出し」、つまりターゲットオブジェクト内のメソッドがターゲットオブジェクトの他のメソッドを呼び出すと、呼び出されたメソッドが@Transactional!でマークされていても、実行時に実際のトランザクションが発生することはありません。

(最初の文に強調が追加され、元の文から他の強調が追加されました。)


Big +1ところで、私は自由に引用を引用するので、人々は混乱しません。そして、元のイタリックなどを追加し直しました。
TJ Crowder 2010年

Spring docuで以前にこのステートメントを読んだことがありますが、それでもトランザクション設定がクラスベースのプロキシインフラストラクチャによって認識されない理由を理解できませんか?個人的には、これはSpringの実装上の制限であり、基盤となるプロキシインフラストラクチャの問題ではないと思います。
dma_k 2010

Springソースを見るとJdkDynamicAopProxy、各メソッド呼び出しですべてのBeanアドバイザを通過することがわかります(も参照DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice())。これには、宣言型トランザクション設定の場合はが含まれBeanFactoryTransactionAttributeSourceAdvisorます。次にTransactionAttributeSourcePointcut#matches()呼び出され、トランザクション関連情報を収集する必要があります。ターゲットクラスに渡され、このクラスが実装するすべてのインターフェイスを常にトラバースできます。さて、なぜこれが確実に機能しないのですか?
dma_k 2010

2
@ dma_k-Javaランタイムは、スーパークラスから継承されたクラスアノテーション、または継承のないメソッドアノテーションにのみ直接アクセスできます。したがって、Pointcut.matches()は、実際にすべてのインターフェイスを再帰的にトラバースし、リフレクションを使用して「実際の」オーバーライドされたメソッドを見つけ、ブリッジメソッドを処理し、オーバーロード、可変引数、ジェネリック、プロキシを見つけ、もちろん高速に実行する必要があります。次に、ダイヤモンドの継承に対処する必要があります-おそらく多くの継承された@Transactional注釈のどれが適用されますか?それで、それはすべて可能ですが、私は春を非難しません。jira.springsource.org/browse/SPR-975
Peter Davis

9

それらをインターフェースに配置することはできますが、場合によってはトランザクションが発生しない可能性があることに注意してください。Springドキュメントのセクション10.5.6の2番目のヒントを参照してください。

Springは、インターフェースにアノテーションを付けるのではなく、@ Transactionalアノテーションで具象クラス(および具象クラスのメソッド)にのみアノテーションを付けることをお勧めします。確かに、@ Transactionalアノテーションをインターフェイス(またはインターフェイスメソッド)に配置できますが、これは、インターフェイスベースのプロキシを使用している場合に期待されるとおりにのみ機能します。Javaアノテーションがインターフェースから継承されないという事実は、クラスベースのプロキシ(proxy-target-class = "true")またはウィービングベースのアスペクト(mode = "aspectj")を使用している場合、トランザクション設定は次のようになることを意味します。プロキシおよびウィービングインフラストラクチャによって認識されず、オブジェクトはトランザクションプロキシにラップされないため、明らかに問題が発生します。

このため、実装に配置することをお勧めします。

また、私には、トランザクションは実装の詳細のように見えるので、実装クラスに含める必要があります。トランザクションである必要のないロギングまたはテスト実装(モック)用のラッパー実装があると想像してください。


9

Springの推奨事項は、インターフェースではなく具体的​​な実装に注釈を付けることです。インターフェイスでアノテーションを使用することは誤りではありません。その機能を誤用して、@ Transaction宣言を誤ってバイパスする可能性があります。

インターフェイスでトランザクションをマークしてから、Springの他の場所でその実装クラスのいずれかを参照した場合、springが作成するオブジェクトが@Transactionalアノテーションを尊重しないことは明らかではありません。

実際には、次のようになります。

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}

1

具体的なクラスで@Transactionalをサポートする:

私は一般的に、API、実装、Web(必要な場合)の3つのセクションでソリューションを設計することを好みます。依存関係を最小限に抑えることで、APIを可能な限り軽量/シンプル/ POJOに保つように最善を尽くしています。APIを頻繁に共有する必要がある分散/統合環境でプレイする場合は特に重要です。

@Transactionalを配置するには、APIセクションにSpringライブラリが必要ですが、IMHOは効果的ではありません。したがって、トランザクションが実行されている実装に追加することを好みます。


0

IFCの予測可能なすべての実装者がTXデータを気にする限り、それをインターフェースに配置することは問題ありません(トランザクションはデータベースだけが処理する問題ではありません)。メソッドがTXを気に​​しない場合(ただし、Hibernateなどのためにそこに配置する必要があります)、implに配置します。

また、@Transactionalインターフェイスのメソッドに配置する方が少し良いかもしれません。

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.