javassist入門(しようとした話)

8日夜、Seasar Conference 2008 Autumn夜の部のレポートを書こうと思っていたのですが、会社的な飲みで自棄酒モードになり気持ち悪かったのと、なぜかふとEclipseを立ち上げたくなってSeasarConで栗原さん(id:masataka_k)に軽くいじられた好奇心のみのjavassist探索など始めてしまったので、その経緯を簡単に。

try {
    ClassPool cp = ClassPool.getDefault();
    CtClass ctc = cp.get("_tips.apipractice.javassist.Bean2");
    CtMethod newMethod = CtNewMethod.make("public String getValExt() { return \"abcde:\" + getVal();}", ctc);
    ctc.addMethod(newMethod);
    Class c = ctc.toClass();

    Object o = c.newInstance();
    Method setter = c.getMethod("setVal", String.class);
    setter.invoke(o, "aaa");
    Method getter = c.getMethod("getValExt", null);
    assertEquals("abcde:aaa", getter.invoke(o, null));

} catch (Exception e) {
    e.printStackTrace();
    fail();
}

まず、こんなコードを書いてみてJUnitで動かしてみると、結果はAllGreen。

じゃこんなのどうかな?と好奇心オンリーで以下のようなコードを書いてみると。。。

try {
    ClassPool cp = ClassPool.getDefault();
    CtClass ctc = cp.get("_tips.apipractice.javassist.Bean3");
    CtMethod newMethod1 = CtNewMethod.make("public String getValExt1() { return \"abcde:\" + getVal();}", ctc);
    ctc.addMethod(newMethod1);
    // Class load
    Class c = ctc.toClass();
    CtMethod newMethod2 = CtNewMethod.make("public String getValExt2() { return \"abcde:\" + getVal();}", ctc);
    try {
        // 2nd
        ctc.addMethod(newMethod2);
    } catch (Exception e) {
        // Nothing
        System.err.println("ダメだってよ");
    }
    // 3rd
    if (ctc.isFrozen()) {
        ctc.defrost();
        
    }
    ctc.addMethod(newMethod2);
    c = ctc.toClass();
    Method[] methods = c.getDeclaredMethods();
    assertEquals(4, methods.length);
    assertEquals("getVal", methods[0].getName());
    assertEquals("getValExt1", methods[0].getName());
    assertEquals("getValExt2", methods[0].getName());
    assertEquals("setVal", methods[1].getName());
} catch (Exception e) {
    e.printStackTrace();
    fail();
}

java.lang.RuntimeException: _tips.apipractice.javassist.Bean3 class is frozen
at javassist.CtClassType.(ry

なんてExceptionが。


javassistJavaDocでは「Once this method is called, further modifications are not allowed any more. To load the class, this method uses the context class loader of the current thread.」なんて書かれてて、Excite先生に聞いたら「もうやめて!そのClassのライフはもうゼロよっ」という事なんだそうで。

id:Yamashiro0217先生曰く「そりゃそうだろ、動いている最中で同じクラスの動作が整合とれなくなったら困るじゃん、jk」と言われ納得。

という訳で、java-jaな人たちとSkypeで「ClassLoader自作してtoClass()したクラスを一回Unloadしないとダメなんじゃん?」とか、「ってゆーか、そんなことやる必然性ってどんだけー」と若干盛り上がったのでありました。


id:t_yanoからH2 Databaseなんかもクラスファイルをコマンドでインストールしてるんだぜ、という話題からOSGiとJava7の話になったのと、スタート地点がただの好奇心なので、これをどこまで頑張るの?っていうのもありながら、なんとはなしに胃がむかむかしているのでとりあえずここらでいったんストップ。


きっとこの辺は僕の理解度が足らないからなんだと思うので、引き続き勉強をしつつ酔いが醒めるのを待つ感じですね。もし、あまりに恥ずかしい間違え(例えば#defrost()って使い方これで良いの?とか)があったら、しばらくへこんだ後、なんとかします><