よく見る気持ち悪い(気持ち悪かった)サンプルコード
Qiitaや参考書なんかでSpringのアノテーションベースConfigurationのサンプルコードを見るとたまに出くわすこんなコード。
@Configuration
public class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2(bean1());//Bean1を2回newしてない???
}
}
Bean2はBean1を引数にとってインスタンス化するのですが、Bean1を取得する手段がメソッド呼び出し。。。
初めて見たとき、「あれ?これコンテナに詰めたBean1とBean2のコンストラクタに渡したBean1が同一じゃ無いけどいいの???」って思ってそれから最近までずっとモヤモヤしてたんですが。。。
ごん、お前だったのか
メソッドbean2の中でコンテナからBean1を引っ張って標準出力してみる遊びをしたところ、
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2(Bean1 bean1) {
System.out.println(bean1);//コンテナからとってきたBean1
System.out.println(bean1());//メソッドbean1のなかで再びnewしていると思われるBean1
return new Bean2(bean1());
}
com.example.demo.Bean1@332b70df
com.example.demo.Bean1@332b70df
同じインスタンスじゃないか。確かに、メソッドbean2で呼び出したメソッドbean1内でnewしているはずなのに。
まさに野良狐だと思って撃ったら「ごん」だった兵十の気持ちです。
何が起こっていたのか
じゃあメソッドbean1の中で何が起こっているのか。
試しにConfigのコンストラクタでthisを標準出力してみましょう。
public Config() {
System.out.println(this);
}
com.example.demo.Config$$EnhancerBySpringCGLIB$$74a9b1d8@6ac168f5
EnhancerBySpringCGLIBとは何でしょう、調べてみるとProxyのことらしいですね。
実は実行時に動くConfigインスタンスはSpringが勝手に(?)作ったProxyクラスのインスタンスでメソッドbean1はその独自クラスの中でOverrideされてるっぽいです。
つまり、オリジナルのメソッドbean1は1度しか呼ばれず、2回目以降は上書きされたメソッドがコンテナからBean1を返却していたということです。
サンプルコードは間違ってなかった
気持ち悪いとか言ってしまい大変申し訳ありませんでした。