[[JAVAの記事一覧]]

*目次 [#j514af7a]
#contents

*Javassist -- Java バイトコードを操作するクラスライブラリ -- 入門 [#pf664185]

http://www.csg.is.titech.ac.jp/~chiba/notes/javapress03/index.html

*JavassistのClassPoolオブジェクト [#j516dffd]
**クラスパスを管理し、クラスファイルをディスク等から実際に読み込む [#m3f47e75]
 import javassist.*;
 
 public class List2 {
    public static void main(String[] args) throws Exception {
        ClassPool cp = ClassPool.getDefault();
        CtClass cc = cp.get("java.awt.Point");
        CtClass superClass = cc.getSuperclass();
        System.out.println(superClass.getName());
    }
 }

*JavassistのCtClass [#a437cb87]
リフレクションAPIのClassオブジェクトはJava仮想機械(JVM)にロード済みのクラスを表し、JavassistのCtClassオブジェクトはまだロードされていない、クラスファイルの状態のクラスを表す。
**クラス名を得る [#he7950bd]
String getName()                
**クラス名を変更 [#hf67f4e1]
void setName(String name)      
**修飾子を得る [#k574dbbe]
int getModifiers()              
**修飾子を変更 [#sa693832]
void setModifiers(int m)  
**スーパークラスを得る [#sf62e79b]
CtClass getSuperclass()         
**スーパークラスを変更 [#xfde989f]
void setSuperclass(CtClass c)   
**インタフェースを得る [#kd329d91]
CtClass[] getInterfaces()       
**インタフェースを変更 [#yc174348]
void setInterfaces(CtClass[] i) 
**全フィールドを得る [#ia40cad6]
CtField[] getFields()           
**フィールドを追加 [#sa02b3ba]
void addField(CtField f)        
**全メソッドを得る [#tf8408b3]
CtMethod[] getMethods()         
**メソッドを追加 [#a6638446]
***サンプルコード [#y4f81cde]
 void addMethod(CtMethod m)  
 
 import javassist.*;
 
 public class List5 {
    public static void main(String[] args) throws Exception {
        ClassPool cp = ClassPool.getDefault();
        CtClass p3 = cp.get("Point3D");
        CtClass p2 = cp.get("java.awt.Point");
        p3.setSuperclass(p2);
        CtMethod m = CtNewMethod.make(
            "public String toString() {" +
            "  return \"(\" + x + \",\" + y + \",\" + z + \")\"; }", p3);
        p3.addMethod(m);
        p3.writeFile();
    }
 }
**コンストラクタを得る [#i404d14d]
CtConstructor[] getConstructors()       
**コンストラクタを追加 [#vbe49b80]
void addConstructor(CtConstructor c)    
**クラス定義をファイルに保存 [#z050b3ff]
void writeFile()                
**クラスをロード [#z0888b55]
Class toClass()                 
**クラス定義の中身を得る [#o35623b3]
byte[] toBytecode()



*CtMethod のメソッド (抜粋) [#we65d04f]

**メソッド名を得る [#pb6d9d76]
String getName()                
**メソッド名を変更 [#j3fc6ab9]
void setName(String name)       
** 修飾子を得る [#w00f0a87]
int getModifiers()             
**修飾子を変更 [#ief61df1]
void setModifiers(int m)        
** メソッド本体を変更 [#lf344b6d]
void setBody(String src)       
**メソッドの一部を変更 [#w1417102]
void instrument(ExprEditor e)   
**メソッドの最初にコードを挿入 [#j4e7e1d9]
void insertBefore(String src)   
***サンプルコード [#ve07cf9a]
変数m がCtMethod オブジェクトを表しているとすると、

 m.insertBefore("System.out.println(\"OK\");");
で m が表すメソッドの冒頭に println メソッドの呼び出しを挿入できます。

**メソッドの呼び出しを、別メソッドの呼び出しに置換 [#c2818bee]
***サンプルコード [#za8925ef]
say メソッドの呼び出しを、hi メソッドの呼び出しに置換
 public class Hello {
    public void say() {
        System.out.println("Hello");
    }
    public void hi() {
        System.out.println("Hi");
    }
    public static void main(String[] args) {
        System.out.println("start...");
        new Hello().say();
    }
 }

 import javassist.*;
 
 import javassist.*;
 import javassist.expr.*;
 
 public class List8 {
    public static void main(String[] args) throws Exception {
        ClassPool cp = ClassPool.getDefault();
        CtClass cc = cp.get("Hello");
        CtMethod m = cc.getDeclaredMethod("main");
        m.instrument(new ExprEditor() {
            public void edit(MethodCall m) throws CannotCompileException {
                if (m.getClassName().equals("Hello")
                        && m.getMethodName().equals("say"))
                    m.replace("$0.hi();");
            }
        });
        cc.writeFile();
    }
 }
***元のメソッド呼び出しの相手オブジェクトを表す特殊変数 $0 [#i48930ac]
上のプログラムでは、見つかった sayメソッドの呼び出し式を下の文で置換します。

 $0.hi();
これは同じオブジェクトのhiメソッドの呼び出しを意味します。$0はJavassistが提供する特殊な変数で、メソッド呼び出し式を置換する場合、元のメソッド呼び出しの相手オブジェクトを表します。
**メソッドを呼び出す前にメッセージを表示する [#kb78994b]
 public class Hello {
    public void say() {
        System.out.println("Hello");
    }
    public void hi() {
        System.out.println("Hi");
    }
    public static void main(String[] args) {
        System.out.println("start...");
        new Hello().say();
    }
 }


 import javassist.*;
 
 import javassist.*;
 import javassist.expr.*;
 
 public class List8 {
    public static void main(String[] args) throws Exception {
        ClassPool cp = ClassPool.getDefault();
        CtClass cc = cp.get("Hello");
        CtMethod m = cc.getDeclaredMethod("main");
        m.instrument(new ExprEditor() {
            public void edit(MethodCall m) throws CannotCompileException {
                if (m.getClassName().equals("Hello")
                        && m.getMethodName().equals("say"))
                    m.replace(”{ System.out.println("** say() **");” + 
                                      "$_ = $proceed($$); }”);
            }
        });
        cc.writeFile();
    }
 }

***置換される元の式に書かれていた処理を実行 $proceed [#m012b152]
$proceedは、置換される元の式に書かれていた処理を実行するために使います。
    
**置換される元のコードの処理を実行 [#ra8ea8d7]
 $_ = $proceed($$);
元のコードの内容や、メソッドの名前、シグネチャにかかわらず、常に同じです。
*** 元のメソッド呼び出し式の実引数列を表す特殊変数 $$ [#i1328d0d]
$1、$2、… を使っても同じ結果が得られますが、$$を使うと実引数列の個数にかかわらず常に$$と書けます。上の例でも、sayメソッドの引数はないので、$proceed() と明示的に引数を書かないようにしてもよいのですが、$proceed($$)と書いておけばJavassistが実引数の個数を考慮して適切にコンパイルしてくれます。

***置換される元のコードの計算結果を表す特殊変数 [#i4ff7c48]
$_は、置換される元のコードの計算結果を表す特殊変数です。上のブロックの実行終了後のこの変数の値が、置換後の新しいコードの計算結果となります。say メソッドの場合は戻り値の型がvoidなので、この変数に何を代入しても無視されますが、変数自体は利用可能です。$_の型は置換される元の式の型と同じです。元の式の型がvoid型の場合はObject型です。またこの場合、$proceedの戻り値はnullです。

**実引数列を使って、別なメソッドをリフレクションAPIで呼び出す [#jd174938]
 Object result = m.invoke($args); 
***実引数列をObject型の配列に変換 $args [#r80c859a]
***キャストに置換前の型を入れる [#re3ec7b7]
 $_ = ($r)result;
もし戻り値の型($_の型でもある)がintのような基本データ型のときは、Integerのようなwrapperクラスからint型へ値の変換をおこないます。
**メソッドの末尾にコードを挿入 [#k97f1caf]
void insertAfter(String src, boolean asFinally)

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS