http://www.csg.is.titech.ac.jp/~chiba/notes/javapress03/index.html
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()); } }
リフレクションAPIのClassオブジェクトはJava仮想機械(JVM)にロード済みのクラスを表し、JavassistのCtClass?オブジェクトはまだロードされていない、クラスファイルの状態のクラスを表す。
String getName()
void setName(String name)
int getModifiers()
void setModifiers(int m)
CtClass? getSuperclass()
void setSuperclass(CtClass? c)
CtClass?[] getInterfaces()
void setInterfaces(CtClass?[] i)
CtField?[] getFields()
void addField(CtField? f)
CtMethod?[] getMethods()
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(); } }
CtConstructor?[] getConstructors()
void addConstructor(CtConstructor? c)
void writeFile()
Class toClass()
byte[] toBytecode()
String getName()
void setName(String name)
int getModifiers()
void setModifiers(int m)
void setBody(String src)
void instrument(ExprEditor? e)
void insertBefore(String src)
変数m がCtMethod? オブジェクトを表しているとすると、
m.insertBefore("System.out.println(\"OK\");");
で m が表すメソッドの冒頭に println メソッドの呼び出しを挿入できます。
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(); } }
上のプログラムでは、見つかった sayメソッドの呼び出し式を下の文で置換します。
$0.hi();
これは同じオブジェクトのhiメソッドの呼び出しを意味します。$0はJavassistが提供する特殊な変数で、メソッド呼び出し式を置換する場合、元のメソッド呼び出しの相手オブジェクトを表します。
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は、置換される元の式に書かれていた処理を実行するために使います。
$_ = $proceed($$);
元のコードの内容や、メソッドの名前、シグネチャにかかわらず、常に同じです。
$1、$2、… を使っても同じ結果が得られますが、$$を使うと実引数列の個数にかかわらず常に$$と書けます。上の例でも、sayメソッドの引数はないので、$proceed() と明示的に引数を書かないようにしてもよいのですが、$proceed($$)と書いておけばJavassistが実引数の個数を考慮して適切にコンパイルしてくれます。
$_は、置換される元のコードの計算結果を表す特殊変数です。上のブロックの実行終了後のこの変数の値が、置換後の新しいコードの計算結果となります。say メソッドの場合は戻り値の型がvoidなので、この変数に何を代入しても無視されますが、変数自体は利用可能です。$_の型は置換される元の式の型と同じです。元の式の型がvoid型の場合はObject型です。またこの場合、$proceedの戻り値はnullです。
Object result = m.invoke($args);
$_ = ($r)result;
もし戻り値の型($_の型でもある)がintのような基本データ型のときは、Integerのようなwrapperクラスからint型へ値の変換をおこないます。
void insertAfter(String src, boolean asFinally)