#はじめに
javassistというメタプログラミングが出来るライブラリを使用して
実際に作成したクラスを書き換えてみました。
ライブラリを使っていてクソだと思った時に使えます。
(謎のログが出力されるなど...)
#ライブラリをインポート
gradleを使用しています。
build.gradle
...
dependencies {
...
compile group: 'org.javassist', name: 'javassist', version: '3.15.0-GA'
}
#実際に書き換える
実際にUserという用意したクラスを書き換えて、
moneyというフィールドを追加します。
data/User.java
package data;
public class User {
private String name;
private int age;
private float height;
private float width;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getUserInfo() {
return "name: " + name + ", age: " + age;
}
}
Main.java
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
public class Test {
public static void main(String[] args) {
try {
// 全てのクラスのプールを取得。
ClassPool pool = ClassPool.getDefault();
// パッケージ名とクラス名のフルで data.User を検索
// 見つからない場合は例外が出ます。
CtClass ctc = pool.get("data.User");
// 引数にフィールドの型、フィールド名、宣言先を指定。
CtField field = new CtField(CtClass.intType, "money", ctc);
// フィールドの修飾子を指定
// 複数の修飾子がある場合は | で追加していく。
// field.setModifiers(Modifier.STATIC | Modifier.PRIVATE);
field.setModifiers(Modifier.PRIVATE);
// フィールドを追加。
ctc.addField(field);
// 定義されているメソッドを取得。
CtMethod method = ctc.getDeclaredMethod("getUserInfo");
// メソッドの中を置き換えて money が出力されるように変更。
method.setBody("return \"name: \" + name + \", age: \" + age + \", money: \" + money;");
// クラスローダーのクラスを上書き。
Class cls = ctc.toClass(ClassLoader.getSystemClassLoader(), AsmTest.class.getProtectionDomain());
// リフレクションでクラスをインスタンスを生成。
User ins = (User) cls.newInstance();
ins.setName("Bob");// name にBobを指定。
// コンパイル後に money が追加されているのでリフレクションで取得。
Field f = cls.getDeclaredField("money");
// アクセシビリティを変更。
// private や protectedの場合にはアクセスできるように true にしてください。
f.setAccessible(true);
// moneyフィールドを int で 1500 に変更。
f.setInt(ins, 1500);
// 実際に出力。
System.out.println("name: " + ins.getName());
System.out.println("money: " + f.getInt(ins));
System.out.println(ins.getUserInfo());
} catch (Exception e) {
// エラーを出力。
System.out.println(e);
}
}
}
#実行すると
結果は...
name: Bob
money: 1500
name: Bob, age: 0, money: 1500
なんと、moneyというフィールドが増えていて、
値を変更することが出来るようになっています。
#まとめ
今回のフィールドの追加のみ紹介しましたが他にも様々な機能があるので
是非使って見てください。