web-dev-qa-db-ja.com

春-@Transactional-バックグラウンドで何が起こるか?

@Transactionalでメソッドに注釈を付けたときに実際に何が起こるか知りたいですか?もちろん、Springがそのメソッドをトランザクションでラップすることを知っています。

しかし、私は次の疑問を持っています。

  1. Springはプロキシクラスを作成すると聞きましたか?誰かがこれをもっと詳しく説明できますか深さ実際にそのプロキシクラスに存在するものは何ですか?実際のクラスはどうなりますか?そして、Springが作成したプロキシクラスをどのように見ることができますか
  2. また、Springのドキュメントで次のことを読みました。

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

ソース: http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

外部メソッド呼び出しのみがトランザクションの下にあり、自己呼び出しメソッドではないのはなぜですか?

284
peakit

これは大きなトピックです。 Springのリファレンスドキュメントでは、複数の章を取り上げています。 Springの宣言的なトランザクションサポートはAOPを基盤として使用しているため、 アスペクト指向プログラミング および トランザクション を読むことをお勧めします。

しかし、非常に高いレベルで、Springはクラス自体またはメンバーで@ Transactionalを宣言するクラスのプロキシを作成します。プロキシは、実行時にはほとんど見えません。これは、Springが、プロキシされるオブジェクトにメソッド呼び出しの前、後、または前後に動作を注入する方法を提供します。トランザクション管理は、フックできる動作の一例にすぎません。セキュリティチェックも別の方法です。また、ロギングなどのために独自のものを提供することもできます。そのため、@ Transactionalを使用してメソッドに注釈を付けると、Springは、注釈を付けているクラスと同じインターフェースを実装するプロキシを動的に作成します。クライアントがオブジェクトを呼び出すと、呼び出しがインターセプトされ、動作がプロキシメカニズムを介して注入されます。

ところで、EJBのトランザクションは同様に機能します。

ご覧のとおり、プロキシメカニズムは、外部オブジェクトから呼び出しがあった場合にのみ機能します。オブジェクト内で内部呼び出しを行う場合、実際にはプロキシをバイパスする「this」参照を介して呼び出しを行っています。ただし、この問題を回避する方法があります。 このフォーラム投稿 で1つのアプローチを説明します。ここでは、BeanFactoryPostProcessorを使用して、プロキシのインスタンスを「self-実行時にクラスを参照します。この参照を「me」というメンバー変数に保存します。次に、スレッドのトランザクションステータスの変更を必要とする内部呼び出しを行う必要がある場合は、呼び出しをプロキシ経由で送信します(例: "me.someMethod() "。)フォーラムの投稿で詳しく説明しています。 BeanFactoryPostProcessorコードは、Spring 1.xの時間枠で書き戻されたため、今では少し異なることに注意してください。しかし、うまくいけば、それはあなたにアイデアを与える。おそらく更新できるバージョンを持っています。

223
Rob H

SpringがBean定義をロードし、@ Transactionalアノテーションを検索するように構成されている場合、実際のBeanの周囲にこれらのプロキシオブジェクトが作成されます。これらのプロキシオブジェクトは、実行時に自動生成されるクラスのインスタンスです。メソッドが呼び出されるときのこれらのプロキシオブジェクトのデフォルトの動作は、「ターゲット」Bean(つまり、Bean)で同じメソッドを呼び出すだけです。

ただし、プロキシにはインターセプターを提供することもでき、存在する場合、これらのインターセプターはターゲットBeanのメソッドを呼び出す前にプロキシによって呼び出されます。 @Transactionalアノテーションが付けられたターゲットBeanの場合、SpringはTransactionInterceptorを作成し、生成されたプロキシオブジェクトに渡します。したがって、クライアントコードからメソッドを呼び出すときは、最初にTransactionInterceptor(トランザクションを開始する)を呼び出すプロキシオブジェクトのメソッドを呼び出します。これにより、ターゲットBeanのメソッドが呼び出されます。呼び出しが終了すると、TransactionInterceptorはトランザクションをコミット/ロールバックします。クライアントコードに対して透過的です。

「外部メソッド」に関しては、Beanが独自のメソッドの1つを呼び出す場合、プロキシを介してそれを行いません。 SpringはBeanをプロキシでラップしますが、Beanはそれを認識しません。 Beanの「外部」からの呼び出しのみがプロキシを通過します。

それは役立ちますか?

172
skaffman

視覚的な人間として、プロキシパターンのシーケンス図を検討するのが好きです。矢印の読み方がわからない場合は、最初の矢印を読んでください:ClientProxy.method()を実行します。

  1. クライアントは自分の観点からターゲットのメソッドを呼び出し、プロキシによって静かにインターセプトされます
  2. Beforeアスペクトが定義されている場合、プロキシはそれを実行します
  3. 次に、実際のメソッド(ターゲット)が実行されます
  4. 戻り後とスロー後は、メソッドが戻った後やメソッドが例外をスローした場合に実行されるオプションの側面です。
  5. その後、プロキシはafterアスペクトを実行します(定義されている場合)
  6. 最後に、プロキシは呼び出し元のクライアントに戻ります

Proxy Pattern Sequence Diagram (私はその起源に言及したことを条件に写真を投稿することを許可されました。著者:Noel Vaes、ウェブサイト:www.noelvaes.eu)

34
progonkpa

最も簡単な答えは、 @Transactionalを宣言するメソッドで、メソッドの完了時にトランザクションの境界が開始し、境界が終了することです。

JPA呼び出しを使用している場合、すべてのコミットはこのトランザクション境界内で行われます。 entity1、entity2、およびentity3を保存するとします。これで、entity3の保存中にenitiy1とentity2が同じトランザクションに入ると例外が発生するため、entity1とentity2はentity3でロールバックされます。

トランザクション:(entity1.save、entity2.save、entity3.save)。例外があると、DBとのすべてのJPAトランザクションがロールバックされます。 JPAトランザクションはSpringによって内部的に使用されます。

13