動的プロキシとシリアライズ
一般的なDIコンテナに大抵標準装備されている機能に,AOPがある。例えば,あるオブジェクトのあるメソッドが呼び出された際に,そのメソッドを処理する前に何らかの処理を挟み込む,などがAOPである。この機構を実現するためには,動的プロキシと呼ばれる機能が必要となる。 デザインパターンのProxyパターンは,あるオブジェクトと同等のインタフェースを持つProxyクラスを作成し,そのProxyクラスの中から本来のオブジェクトを呼びだすようにする。クライアントは本来のオブジェクトを触っている感覚なのだが,実際にはProxyクラスという「代理」のものが応答し,本来のオブジェクトを本当に呼び出すかどうかの判断とか,引数の内容を変更してしまったり,本来のオブジェクトからスローされる例外の処理をProxyクラス内でしてしまう,などの機能拡張をProxyクラスで行うというパターンである。クライアント側は,Proxy暮らすと本来のオブジェクトのクラスとインタフェースが同様なため,Proxyオブジェクトなのか本来のオブジェクトなのかを意識する必要がない。 デザインパターンのProxyパターンでは,予めProxyクラスを静的に作成しておく想定であるが,このProxyクラスを実行時に動的に作成する機構が,動的プロキシと呼ばれるものである。 つまり,
のProxyクラスを実行時に作成して,SubjectImplクラスのメソッド呼び出しをインターセプトし,ClientクラスやSubjectImplクラスを一切変更することなく,さらに事前にProxyクラスを作成することもなく,前処理や後処理を実行時に与えられるようにするのが,動的プロキシである。 さて,動的プロキシを作り出すためには,そのための機構が必要となる。よく使われる代表的なものとしては,
-
JavaSE標準のjava.lang.reflect.Proxyクラスを利用する。
-
cglibを利用する。
-
Javassistを利用する。 があげられる。ちょっと前まではcglibを使うことが多かったが,最近はすっかりJavassisを使うことが多くなった。 動的プロキシの機構を使えば,(わざと不正確な表現を使えば)abstractなクラスのインスタンスも生成できるようになる。abstractなクラスが持つabstractなメソッドがいざ呼び出された際には,そのメソッド呼び出しをインターセプトして処理を与えることができるようになるからだ。 動的プロキシの機構によって作成されたオブジェクトは,もちろん動的に作成されたクラスを元に生成される。そのクラスは完全にランタイムに作成されたものであり,一般的には例えばJavaVMが終了してしまえば,そのクラスも消滅する。 これは何を意味しているかというと,動的プロキシによってランタイムに作成されたクラスを元に生成されたインスタンスは,普通に考えたらシリアライズができないことを意味する。シリアライズができたとしても,デシリアライズ時にシリアライズ時に存在していたクラスが消滅しているために,NoClassDefFoundErrorが発生してしまう。 いろいろと(新婚旅行中にも)上記の実現方法を試してみたが,とりあえず実現可能なやり方を見つけたので,次のエントリから紹介してみたいと思う。ざっくりと言うと,
-
Javassistでの動的プロキシの作り方。
-
writeReplace()/readResolve()メソッドの使い方。
-
動的プロキシのシリアライズ/デシリアライズ方法の紹介。 という感じでエントリしていく予定である。 これがわかると,かなりいろいろな場面で思い切った処理を行うことができるようになる。簡単なAOP機構を持つDIコンテナを自作することさえできるようになるので,Javaプログラマならぜひ押さえておきたい知識と言えよう。