いくら@Componentが便利だかといって、
@Component public interface TestMock{ public void test(); }
勢いでインターフェースに@Componentつけても、Springではコンポーネント登録されない。
従って、こいつに@Aspectを引っ掛けようとしても、うまく引っかからない。
やっぱり実体を登録しないとだめだ==どこかでうまくBeanFactoryに介入しないと、という話になる。
BeanFactory自体を拡張するのは容易だが、そうするとBeanFactory自体の設定に手間がかかる場合(例えばSpringMVCを使うときなんか)だとイマイチなので、出来れば設定ファイルに1行書いただけで済むようなやり方が好ましい。
そこで期待されるのがBeanPostProcessor。このインターフェースをつけたクラスをSpringの設定ファイルに書いておくと、Beanが初期化される前後に呼び出しを受けることができる。
実際、@Autowirednアノテーションに対してBeanをインジェクションしているのはAutowiredAnnotationBeanPostProcessorというクラスで、大まかに書くと
1、BeanFactory(or ApplicationContxt)#getBean()を呼ぶ。 ↓ 2、登録済みのBeanを探す。 ↓ 3、Bean作成 ↓ 4、AutowiredAnnotationBeanPostProcessorを呼び出す。 ↓ 5、Beanのメソッドとフィールドを検査し、@Autowiredアノテーションのついているものにインジェクションする。 ↓ 6、Bean完成! ↓ 7、ねこびーん!
という流れで処理が行われる。
BeanFactoryの処理の所々でBeanPostProcessorの処理を呼んでくれるので、これを使えばBeanFactoryの挙動を変える事が出来そうだ。
ここで、「4、AutowiredAnnotationBeanPostProcessorを呼び出す」は細かくいくつかのステージに分かれており、AutowiredAnnotationBeanPostProcessorのメソッドが
postProcessBeforeInstantiation() postProcessAfterInstantiation() postProcessBeforeInitialization() postProcessAfterInitialization()
の順番でBeanFactoryから呼び出される。
実際に5の処理を行うのは、「postProcessAfterInstantiation()」の呼び出しがあった時点なので、「postProcessBeforeInstantiation()」が呼ばれてた時点で対象Beanを検査し、例えば「@Autowiredがついていて、かつ引数がインターフェースで@Mockがついているメソッド」を見つけたら、その引数の型をProxyを使ってBeanをインスタンス化して先にBeanFactoryに登録しておくことで、その後に行われるインジェクション処理時に、BeanFactoryからBeanを取り出すことが出来るようになる。
つまり上記処理を加味したBeanPostProcessorのクラス名をSpringの定義ファイルに書いておくことで、
@Mock public interface TestMock{ public void test(); } @Component public class TestAction{ private TestMock testMock; @AutoWired public void setTestMock(TestMock mock){ this.testMock = mock; } public void execute(){ testMock.test(); } }
のTestAction#setTestMock()に対して、TestMockのProxyインスタンスをセットできるようになる。
さて、このやり方でいいかどうか、検証してみようじゃないか。