Spring Beanのプライベートメソッドに @ Transactional -annotationがある場合、注釈は効果がありますか?
@Transactional
注釈がパブリックメソッドにある場合、それは機能し、トランザクションを開きます。
public class Bean {
public void doStuff() {
doPrivateStuff();
}
@Transactional
private void doPrivateStuff() {
}
}
...
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
質問はプライベートでもパブリックでもありません。質問は、どのように呼び出され、どのAOP実装を使用するかです。
(デフォルト)Spring Proxy AOPを使用する場合、Springが提供するすべてのAOP機能(@Transational
など)は、呼び出しがプロキシを通過する場合にのみ考慮されます。 -これは通常、注釈付きメソッドがanotherBeanから呼び出される場合です。
これには2つの意味があります。
@Transactional
アノテーションは考慮されません。@See Spring Reference:Chapter 9.6 9.6 Proxying mechanism
私見では、Spring Proxiesの代わりにaspectJモードを使用する必要があります。これは問題を克服します。また、AspectJ Transactional Aspectsはプライベートメソッドにも組み込まれています(Spring 3.0で確認済み)。
あなたの質問に対する答えは「いいえ」です。@Transactional
は、プライベートメソッドに注釈を付けるために使用しても効果がありません。プロキシジェネレーターはそれらを無視します。
これは Spring Manual 10.5.6 で文書化されています:
メソッドの可視性と
@Transactional
プロキシを使用する場合は、
@Transactional
注釈をパブリック可視性を持つメソッドにのみ適用する必要があります。@Transactional
注釈を使用して、保護されたメソッド、プライベートメソッド、またはパッケージから見えるメソッドに注釈を付けた場合、エラーは発生しませんが、注釈付きメソッドは構成済みのトランザクション設定を示しません。非パブリックメソッドに注釈を付ける必要がある場合は、AspectJ(以下を参照)の使用を検討してください。
デフォルトでは、@Transactional
属性は、applicationContextから取得した参照で注釈付きメソッドを呼び出す場合にのみ機能します。
public class Bean {
public void doStuff() {
doTransactionStuff();
}
@Transactional
public void doTransactionStuff() {
}
}
これにより、トランザクションが開きます。
Bean bean = (Bean)appContext.getBean("bean");
bean.doTransactionStuff();
これはしません:
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
注:プロキシモード(デフォルト)では、プロキシを介して着信する「外部」メソッド呼び出しのみがインターセプトされます。つまり、呼び出されたメソッドに
@Transactional
のマークが付いていても、「自己呼び出し」、つまりターゲットオブジェクト内のメソッドがターゲットオブジェクトの他のメソッドを呼び出すと、実行時に実際のトランザクションにつながりません。自己呼び出しもトランザクションでラップされることが予想される場合は、AspectJモードの使用を検討してください(以下を参照)。この場合、そもそもプロキシはありません。代わりに、
@Transactional
をあらゆる種類のメソッドの実行時の動作に変換するために、ターゲットクラスが「ウィーブ」されます(つまり、そのバイトコードが変更されます)。
はい、プライベートメソッドで@Transactionalを使用することは可能ですが、他の人が述べたように、これはそのままでは機能しません。 AspectJを使用する必要があります。動作させる方法を見つけるのに時間がかかりました。結果を共有します。
ロード時のウィービングの代わりにコンパイル時のウィービングを使用することを選択しました。これは全体的に優れたオプションだと思うからです。また、Java 8を使用しているため、いくつかのパラメーターを調整する必要があります。
まず、aspectjrtの依存関係を追加します。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
次に、AspectJプラグインを追加して、Mavenで実際のバイトコードウィービングを行います(これは最小限の例ではない場合があります)。
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
最後にこれを設定クラスに追加します
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
これで、プライベートメソッドで@Transactionalを使用できるようになります。
このアプローチの1つの注意:IDEを設定して、AspectJを認識させる必要があります。そうしないと、たとえばEclipseを介してアプリを実行する場合、動作しない可能性があります。健全性チェックとして、直接Mavenビルドに対してテストしてください。
トランザクション内でプライベートメソッドをラップする必要があり、aspectjを使用したくない場合は、 TransactionTemplate を使用できます。
@Service
public class MyService {
@Autowired
private TransactionTemplate transactionTemplate;
private void process(){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
processInTransaction();
}
});
}
private void processInTransaction(){
//...
}
}
答えはノーです。 Spring Reference:Using @Transactional を参照してください:
@Transactional
注釈は、インターフェース定義、インターフェースのメソッド、クラス定義、またはクラスのpublicメソッドの前に配置できます。
Spring Docsはそれを説明しています
プロキシモード(デフォルト)では、プロキシを介して着信する外部メソッド呼び出しのみがインターセプトされます。つまり、ターゲットオブジェクトの別のメソッドを呼び出すターゲットオブジェクト内のメソッドは、実際には、呼び出されたメソッドが@Transactionalでマークされていても、実行時に実際のトランザクションにつながりません。
自己呼び出しもトランザクションでラップされることが予想される場合は、AspectJモードの使用を検討してください(下の表のmode属性を参照)。この場合、そもそもプロキシはありません。代わりに、@ Transactionalをあらゆる種類のメソッドの実行時動作に変換するために、ターゲットクラスが織り込まれます(つまり、そのバイトコードが変更されます)。
別の方法はユーザーですBeanSelfAware
@ loonisを推奨 と同じ方法で TransactionTemplate を使用して、このヘルパーコンポーネント(Kotlin)を使用できます。
@Component
class TransactionalUtils {
/**
* Execute any [block] of code (even private methods)
* as if it was effectively [Transactional]
*/
@Transactional
fun <R> executeAsTransactional(block: () -> R): R {
return block()
}
}
使用法:
@Service
class SomeService(private val transactionalUtils: TransactionalUtils) {
fun foo() {
transactionalUtils.executeAsTransactional { transactionalFoo() }
}
private fun transactionalFoo() {
println("This method is executed within transaction")
}
}
TransactionTemplate
は既存のトランザクションを再利用するかどうかわかりませんが、このコードは間違いなく利用します。