背景
メソッドに対するトランザクション処理を実装する。
@Transactionalアノテーションを使って実現しようとしたところ、アノテーションを付けるとBeanCreationExceptionが発生するようになった。
解決方法
トランザクション処理の対象メソッドを含むクラスに対しインターフェースを作成し、実装クラスのメソッドに対して@Transactionalアノテーションをつけるようにする。
これでエラーが出ず、無事にコンパイルできるようになりました。
public interface HogeProcessor extends Processor<Object, Object> {
Optional<Object> process(HogeEntity hogeEntity);
}
public class HogeProcessorImpl implements HogeProcessor {
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Optional<Object> process(HogeEntity hogeEntity) {
...
...
}
}
原因
自分で調べたもののわからなかったので、教えてもらいました。
インタフェースと実装クラスに分離して治ったら、これが原因。
http://stackoverflow.com/questions/15688689/unsatisfieddependencyexception-in-spring-illegal-arguments-for-constructor-a
AOPに使うライブラリの仕様が原因で、この辺に詳細は載っています。
http://d.hatena.ne.jp/minokuba/20110321/1300705068
教えてもらったページを読んでもわからなかったので、調べてみました。
ここから下はその調べた内容のまとめです。
SpringAOPとライブラリによる差異
Javaの@TransactionalアノテーションはSpringAOPを使って実現されていますが、このAOPのライブラリが今回の原因のようです。
AOPとは
アスペクト指向プログラミング。
アスペクトとは振る舞い、共通処理の単位を表す。
業務的には本質的ではない、システム的な共通処理を外出しすることができる仕組み。
今回は指定されたメソッドが呼び出されるタイミングでトランザクションを開始する処理を挟み込むために、AOPを利用します。
AOPは設定ファイルに以下のように記述することで有効化できるようです。
<!-- AOPを有効化 -->
<aop:aspectj-autoproxy/>
SpringAOPライブラリの種類
SpringAOPでは
・JDK DynamicProxy
・CGLib
の二種類が使える。
今回問題の起きたプロジェクトではJDK DynamicProxyのみが使われていました。
Proxy化
AOPのアスペクト対象となるクラスはJavaBeanでなければならない。
- Spring は実行時に、アスペクト対象クラスのプロキシオブジェクトを生成します。
- そして、プロキシオブジェクトの内部で、アスペクト対象クラスの処理を実行します。
- ジョインポイントとして、アスペクト対象クラスが指定されると、プロキシの中でインターセプトクラスのアドバイスメソッドが呼ばれます。
つまり、AOPライブラリはアスペクト対象クラスのBeanを作るとき、プロキシでラップしたProxyObjectを作る(Proxy化)。
AOPライブラリによるProxy化対象クラスの違い
JDK dynamic proxyでProxy化されたbeanのインスタンスを直接実装クラス指定でAutowired出来ない仕様らしいです。
JDK DynamicProxyを使う場合はインターフェースを実装したクラスがProxy化の対象になる。つまり、実装クラスを直接DIすることはできないらしい。
今回は実装クラスを直にDIしている状態で@TransactionalのAOPを使おうとしたため、JDK DynamicProxyのProxy化の対象にならず、BeanCreationExceptionが発生したというのが原因でした。
ということで、既存クラスに対して新たにインターフェースを作成して、そのインターフェースに対してDIをするように変更、既存クラスはインターフェースの実装クラスとすることで解決。
また、もう一つのAOPライブラリであるCGLibを使うことで実装クラスのProxy化ができるらしいので、今回は試していませんがこちらを利用する方法もあるようです。