WicketのComponentにオブジェクトをインジェクトする
私はWicketというフレームワークが大好きです。
それからJavaEE6の新仕様であるCDIも大好きです。
だったらWicketの中でCDIを使いたくなるのは、ごく自然な流れ。
一応WicketとCDIを統合するライブラリもあるにはあるようですが、まともに動いたことがないんですよね。。。
そんなわけで今回はこれを自作してみました。
public class MyApplication extends WebApplication { @Override public Class<? extends Page> getHomePage() { return HomePage.class; } @Override protected void init() { try { final BeanManager beanManager = InitialContext.doLookup("java:comp/BeanManager"); getComponentInstantiationListeners().add(new IComponentInstantiationListener() { @Override public void onInstantiation(final Component pComponent) { inject(beanManager, pComponent); } }); } catch (final NamingException e) { throw new IllegalStateException(e); } } @SuppressWarnings({ "rawtypes", "unchecked" }) protected void inject(final BeanManager pBeanManager, final Component pComponent) { final Class pType = pComponent.getClass(); final Bean<Object> bean = (Bean<Object>) pBeanManager.resolve(pBeanManager.getBeans(pType)); final CreationalContext<Object> cc = pBeanManager.createCreationalContext(bean); final AnnotatedType<Object> at = pBeanManager.createAnnotatedType(pType); final InjectionTarget<Object> it = pBeanManager.createInjectionTarget(at); it.inject(pComponent, cc); } }
やってることは単純で、IComponentInstantiationListenerを使ってComponentがインスタンス化されるタイミングをフックして、そこでBeanManagerで明示的にインジェクトしてあげているだけです。
改善点はもちろんあります。JNDIが使えない環境では動かない、とか全てのComponentじゃなくてWebPageにだけDIすれば充分なんじゃない?とか。
でもアイデアの核は上記コードに凝縮されています。
CDIに勝ってる?
実はこの方法、本家CDIより機能的に上回っている部分があります。それはnewで生成したComponentにもDI可能なこと。
これはWicketのIComponentInstantiationListenerのおかげです。WicketのIComponentListenerListenerはCompnentクラスのコンストラクタの中から呼び出されるので、こんなことが可能になります。
以下がその部分のコードです。
public Component(final String id, final IModel<?> model) { setId(id); getApplication().getComponentInstantiationListeners().onInstantiation(this); ...(以下省略) }
初めてこのコードを見たときは「なるほど!」と膝を打ってしまいました。
Wicketって、本当におもしろい。