ログ日記

作業ログと日記とメモ

WicketとCayenneとGuice

Guiceを使うための設定を

        getComponentInstantiationListeners().add(new GuiceComponentInjector(this, new GuiceModule()));

このように書いていた。


ここで何が行われているかというと

  • GuiceComponentInjector
    • 初期化、GuiceFieldValueFactoryを生成する
  • GuiceFieldValueFactory
    • フィールドにインジェクションするためのクラス
    • GuiceProxyTargetLocatorを引数にLazyInitProxyFactoryを使ってオブジェクトを生成する
  • GuiceProxyTargetLocator
  • LazyInitProxyFactory
    • 生成する型がインターフェースならJdkHandlerを引数にリフレクションのProxy.newProxyInstanceを実行して返却する
    • 実装クラスならCGLibInterceptorを引数にCGLibでプロキシを生成する
  • JdkHandler
    • invokeで色々な処理を入れている
    • 関係あるのはwriteReplaceを実装していること
      • writeReplaceメソッドはシリアライズのときにオブジェクトを置換する
      • 置換するクラスはProxyReplacement
  • 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関連クラスかどうかをチェックしてプロキシを使わない条件分岐を入れると良いかも。こっちはめんどくさいが。




引き続きもう少し考える。