BeanPostProcessorの脅威

いくら@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インスタンスをセットできるようになる。


さて、このやり方でいいかどうか、検証してみようじゃないか。