WicketとCayenneとGuice
Guiceを使うための設定を
getComponentInstantiationListeners().add(new GuiceComponentInjector(this, new GuiceModule()));
このように書いていた。
ここで何が行われているかというと
- GuiceComponentInjector
- 初期化、GuiceFieldValueFactoryを生成する
- GuiceFieldValueFactory
- フィールドにインジェクションするためのクラス
- GuiceProxyTargetLocatorを引数にLazyInitProxyFactoryを使ってオブジェクトを生成する
- GuiceProxyTargetLocator
- LazyInitProxyFactory
- 生成する型がインターフェースならJdkHandlerを引数にリフレクションのProxy.newProxyInstanceを実行して返却する
- 実装クラスならCGLibInterceptorを引数にCGLibでプロキシを生成する
- JdkHandler
- CGLibInterceptor
- JdkHandlerとだいたい同じ
- ProxyReplacement
- readResolveを実装し、その中でLazyInitProxyFactoryを使って新しいオブジェクトを返却している
シリアライズ時にオブジェクトはProxyReplacementに置換され、デシリアライズ時にはGuiceのlocatorを引数に再び新しいプロキシを返却する。つまり、デシリアライズ時にもGuiceからインスタンスが取得されることになる。
この動作によって、Serializableでないクラスもページやコンポーネントに保持できるようにしている。
ところがCayenneはデシリアライズ時に専用の処理が入るので新しいObjectContextを生成するとうまくいかない。
簡単な解決方法は、Applicationのinitで
getComponentInstantiationListeners().add( new GuiceComponentInjector( this, Guice.createInjector( usesDeploymentConfig() ? Stage.PRODUCTION : Stage.DEVELOPMENT, new GuiceModule()), false));
このように三番目の引数をfalseにして呼び出す。これはプロキシを使うかどうかのフラグで、falseにするとLazyInitProxyFactoryを使わない。
これは全体の設定なのでCayenne用インスタンス取得以外に@Injectを使う場合はめんどくさいことになるかもしれない。
プロキシを使わず、コンポーネントやページに持つフィールドは全てSerializableにするなら昨日書いたCayenneのデメリットの一番問題だった箇所は解決する。
または、GuiceFieldValueFactoryを独自実装して(これを呼び出すGuiceComponentInjectorも)Cayenne関連クラスかどうかをチェックしてプロキシを使わない条件分岐を入れると良いかも。こっちはめんどくさいが。
引き続きもう少し考える。