最近、Javassistなるものを少しさわる機会があったので、
簡単に紹介をしてみようかと。。。
1.Javassistとは?
Javassistとは、Javaバイトコードを変換するクラスライブラリのことです。簡単に言ってしまえば、クラスを読み込んで、その中身の構造を編集するものです。
何はともあれ、簡単なサンプルソースを用意したので見て、動かして体感してみてください。
2.サンプル紹介
<環境>
OS:WindowsXP SP2
JRE:1.4.2_10
eclipse3.1.1を使用
Javassist3.0を使用
<サンプルの内容>
指定したメソッドの最初にコードを挿入する
まず、Javassistで中身を操作する対象のクラスを作成する。
HelloWorldという名前のクラスを作成。
"HelloWorld!!"というメッセージをコンソールに出力するhelloメソッドを定義。
HelloWorld.java
-------------------------------------------------------------------------
package jp.co.ilovex.javassist.sample;
public class HelloWorld {
public void hello() {
System.out.println("Hello World!!");
}
}
-------------------------------------------------------------------------
Javassistを利用して、HelloWorldクラスのhelloメソッドの先頭に
処理を埋め込む処理を行うクラスを作成する。
SampleJavassistクラスでは、HelloWorldクラスのhelloメソッドの先頭で、
"helloメソッドの先頭で呼び出されるはずです。"というメッセージを
コンソールに出力する処理を追加しています。
SampleJavassist.java
-------------------------------------------------------------------------
package jp.co.ilovex.javassist.sample;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
public class SampleJavassist {
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("jp.co.ilovex.javassist.sample.HelloWorld");
CtMethod m = cc.getDeclaredMethod("hello");
m.insertBefore("System.out.println(\"helloメソッドの先頭で呼び出されるはずです。\");");
cc.writeFile();
Class clazz = cc.toClass();
HelloWorld obj = (HelloWorld)clazz.newInstance();
obj.hello();
}
}
-------------------------------------------------------------------------
SampleJavassistの実行結果
-------------------------------------------------------------------------
helloメソッドの先頭で呼び出されるはずです。
Hello World!!
-------------------------------------------------------------------------
あら不思議、既存のコードを変更せずに処理が追加されてしまいました。
最後に、ものすごく簡単に SampleJavassist のコードの説明をします。
-------------------------------------------------------------------------
ClassPool cp = ClassPool.getDefault();
-------------------------------------------------------------------------
ClassPoolという、クラスパスを管理し、クラスファイルをディスク等から実際に読み込む作業を行う、Javassistの要になるオブジェクトを取得しています。
ClassPoolを取得しなければ何もはじまりません。
-------------------------------------------------------------------------
CtClass cc = cp.get("jp.co.ilovex.javassist.sample.HelloWorld");
-------------------------------------------------------------------------
ClassPoolを通して、HelloWorldクラスの定義情報を表すCtClassオブジェクトを取得しています。
CtClassオブジェクトを通して、対象のクラスの構造を編集することになります。
-------------------------------------------------------------------------
CtMethod m = cc.getDeclaredMethod("hello");
m.insertBefore("System.out.println(\"helloメソッドの先頭で呼び出されるはずです。\");");
-------------------------------------------------------------------------
CtClassオブジェクトを通して、HelloWorldクラスのhelloメソッドの情報を表すCtMethodオブジェクトを取得し、CtMethodオブジェクトのinsertBeforeメソッドにより、helloメソッドの先頭に、新たなコードを埋め込む処理を行っています。
-------------------------------------------------------------------------
cc.writeFile();
-------------------------------------------------------------------------
編集した内容をCtClassオブジェクトを通して、HelloWorldクラスに反映する処理行っています。
-------------------------------------------------------------------------
Class clazz = cc.toClass();
HelloWorld obj = (HelloWorld)clazz.newInstance();
obj.hello();
-------------------------------------------------------------------------
編集した内容が反映されたCtClassオブジェクトから、HelloWorldクラスをロードし、インスタンス化してHelloWorldクラスのhelloメソッドを呼び出しています。
編集した内容が反映されたHelloWorldクラスを取得するできるのは、CtClassオブジェクトからのみです。
例えば、下記のように普通にHelloWorldクラスをインスタンス化して実行しても、追加した処理は実行されません。
-------------------------------------------------------------------------
HelloWorld obj = new HelloWorld();
obj.hello();
--実行結果--
Hello World!!
-------------------------------------------------------------------------
JavassistのAPIを利用し編集された内容は、JavassistのAPIを利用してクラスをロードした場合のみ反映されることがわかります。
これにより、Javassistがクラスロード時にバイトコードを変換していることがわかるかと思います。