7

私はjavassistで立ち往生しています。実行時にオブジェクトクラスに新しいメソッドを追加しました。

私のオブジェクトクラス:

package tmp3;

public class Car {
    public Car(){}
}

私のテストクラス:

package tmp3;

import java.lang.reflect.Method;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;

public class TestMain {
    public static void main(String[] args) {
        try {

            CtClass ctclass = ClassPool.getDefault().get("tmp3.Car");
            CtMethod newmethod = CtNewMethod.make("public void testPrint() { System.out.println(\"test ok\"); }",ctclass);
            ctclass.addMethod(newmethod);
            ctclass.writeFile();

            for(Method me: ctclass.toClass().getDeclaredMethods()){ //test print, ok
                System.out.println(me.getName());
            }

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

    }
}

しかし、それ以降は、どのように呼び出す(呼び出す)のかわかりません。javassistにはメソッドを呼び出す機能がないことを読みました。次に、javassistで追加したメソッドを呼び出すにはどうすればよいですか?

私は2日間でたくさんのことを試しましたが、成功しませんでした。これを手伝ってくれませんか。

4

2 に答える 2

9

Javaクラスには静的インターフェースがあります。つまり、ご存知かもしれませんが、Javaはデフォルトで実行時にクラスにメソッドを追加するように設計されていないため、少し注意が必要ですが、目的を達成するのはそれほど難しくありません。

バイトコード修飾子フレームワークであるJavassistを使用して、コンパイルされたクラスを設計し、新しいメソッドを表すバイトコードを追加しました。次の2つのシナリオのいずれかを使用できます。

シナリオ1:注入前にCarクラスと一緒にコンパイルされるコード

この場合、コードがコンパイルされているとき、JavaコンパイラはCarインジェクションなしでインターフェースのみを認識します。したがって、次のように、注入されたメソッドを直接呼び出すことはできません。

 Car car = new Car();
 car.testPrint();

@Scorpionが正しくコメントしたように、リフレクションによってそれを行う必要があります。

 Car car = new Car();
 Method method = car.getClass().getMethod("testPrint", new Class[]{});
 method.invoke(car,new Object[]{});

しかし、これが唯一の方法ではありません...

シナリオ2:コンパイルおよび挿入されたクラスを使用するコード

Carクラスをコンパイルしてインジェクトし、その後、コンパイルされたクラスに対してコードを記述します(たとえば、Carクラスをjarファイルに含める)。インジェクトされたメソッドは、他の通常のメソッドであるかのように呼び出すことができます。

次の演習を行います。

  1. Carクラスをコンパイルする
  2. インジェクションを行うTestMainを実行します
  3. IDEで別のプロジェクトを作成し、そのプロジェクトのクラスパスに、挿入されたクラスを含むディレクトリを追加するか、挿入されたクラスのみを含むjarを作成し、そのjarをクラスパスに追加します
  4. 新しいインスタンスを作成する新しいプロジェクトにクラスを作成しCarます。これで、面倒なことなくtestPrintメソッドを呼び出すことができるようになりました。

注意しなければならないことがいくつかあります。

  • 挿入されたクラスで元のクラスを上書きすると、無効なクラスになってしまいjava.lang.ClassFormatError、切り捨てられたクラスファイルがあることを示すエラーメッセージが表示される可能性があります。これは、Javassistがすべてのバイトコードをメモリにロードしておらず、同じクラスファイルとの間で書き込みと読み取りを行おうとした場合に発生し、結果として混乱が生じます。これを回避するには、別のパスに書き込むか、ファイルを書き込む前にすべてのバイトコードをメモリにロードするようにします(toByteCode()fromを使用CtClass)。
  • 2つのクラスファイルがあり、1つは挿入されたコードを含み、もう1つは元のコードを含む場合、クラスパスに1つだけ含めることを忘れないでください
于 2012-12-10T09:51:55.953 に答える
0

javaリフレクションを使用して呼び出し、javassistを使用してクラスの変更とロードを行い、次にjavaリフレクションを使用して実行します(呼び出し...)。

于 2015-08-10T21:53:08.127 に答える