2006.04.02

Javassistを利用してみる

最近、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がクラスロード時にバイトコードを変換していることがわかるかと思います。

コメントを投稿

(いままで、ここでコメントしたことがないときは、コメントを表示する前にこのブログのオーナーの承認が必要になることがあります。承認されるまではコメントは表示されません。そのときはしばらく待ってください。)

photo
ykato