この記事に書いてあること
前回の記事はこの記事を書くための前知識として作成しました。本題はこちらです。
SpringとLombokの組み合わせで、@allargsconstructorを使っているときにも「NoUniqueBeanDefinitionException」が発生する可能性があります。その時の対応をメモ。
※@allargsconstructorを使っていなくても、abstractクラスを使った際に起こります。
ちなみに、コンストラクタを使ってのインジェクションはSpringの推奨のインジェクション方式となっており、実は@Autowiredはあまり使わない方がいいとされています。
@allargsconstructorはlombokの機能です。この辺に詳しく書いてくれています。
前提
- Spring Framework 4.xx
- java8
事象
以下を見てください。これは前回の記事のクラス図を修正したものです。この辺の記事にあるように「仕組み部分」を共通化しようとした時にこんなコードになります。
※図の赤字のクラス名を誤りました。AbstractLogicに読み替えてください。
抽象クラスのAbstractLogicはcommonメソッドを持っていて、これはインターフェースのrunメソッドから呼ばれることを想定しています。この時、AbstractLogicではcommon()に必要なフィールド変数をインジェクションする必要があります。
@Allargsconstructor
public Abstract AbstractLogic {
//extendsする側は、こいつをコンストラクタのから渡してやらないといけない
private SampleUtil sampleUtil;
private SampleClass sampleClass;
public void common(){
//省略
}
}
上記のようなコードがあった時、これをextendsするSampleLogicImple2は以下のようになります。
@Component
public class SampleLogicImple2 extends AbstractLogic implements SampleLogic {
/**
* コンストラクタ
*/
public SampleLogicImple2(SampleUtil sampleUtil, SampleClass sampleClass) {
//親クラスに引き渡してやる必要がある
super(sampleUtil, sampleClass);
}
}
で、この時に例えばコンストラクタの引数にしているSampleUtilがインタフェースで、実装クラスが複数ある場合に上記コードを実行すると「NoUniqueBeanDefinitionException」が発生します。
原因
前回の記事と同様です。
インスタンス生成時にどの実体を使えばいいかがわからないというふうに言われているだけです。
対策
こちらも同様に@Qualifierで解決できます。コンストラクタには@Qualifierが使えるのです。以下のようになります。
@Component
public class SampleLogicImple2 extends AbstractLogic implements SampleLogic {
/**
* コンストラクタ
*/
public SampleLogicImple2(@Qualifier("sampleUtilImpl") SampleUtil sampleUtil, SampleClass sampleClass) {
//親クラスに引き渡してやる必要がある
super(sampleUtil, sampleClass);
}
}
面白いですね。ここに書いた通り、クラス生成などの処理の間にSpringFrameworkが入ってくるのでこのように@Qualifierがコンストラクタの引数にも使えるということですね。
この情報を残そうと思った経緯
後輩がこの問題にぶつかった時に、コンストラクタによるインジェクションを諦めて@Autwiredと@Qualifierを使う方式に作り替えようとしていたので、いやいやコンストラクタでも@Qualifier使えるよと。
意外と知らない人いそうだったので、啓蒙のために。