5

開発環境でいくつかのサービスをモックしようとしています。serviceFactory コードは次のようなものです。

public class ApiFacadeImpl implements ApiFacade {
private OneService oneService = null;
    public OneService getOneService(){
        if(oneService==null) {//some initialization steps }
        return oneService;
    }
}

私は、この工場が適切にコーディングおよび設計されていないことを知っています。ただし、そのコードを変更することはできません。私の考えは、バイトコードを変更することなので、次のように再定義できます。

public class ApiFacadeImpl implements ApiFacade {
private OneService oneService = new MyMockOneService();
    ....
}

私の1番目は、これはjavassistを使用して可能ですか? そしてどうやって?

Googleを使用してjavassistを使用してフィールドを再初期化するようなものが見つからないので、それを削除して再作成してみました:

        CtField oneServiceField = cc.getDeclaredField("oneService");
    cc.removeField(oneServiceField);
    CtField f = CtField.make(String.format("private %s %s=new %s();",
            oneServiceField.getType().getName(), "oneService",
            mockClass.getCanonicalName()), cc);
    cc.addField(f);
    cc.toClass();

それから私は例外を得ました:

javassist.CannotCompileException: by java.lang.ClassFormatError: Invalid length 99 in LocalVariableTable in class file com/Test
at javassist.ClassPool.toClass(ClassPool.java:1051)
at javassist.ClassPool.toClass(ClassPool.java:994)
at javassist.ClassPool.toClass(ClassPool.java:952)
at javassist.CtClass.toClass(CtClass.java:1079)

私の2番目の質問は、なぜこの例外なのですか? Javaクラスの定義に従わないステップはどれですか? そして、フィールドを削除すると、javassistは次のフィールド参照を削除するのに役立ちます:

public OneService getOneService(){
        if(oneService==null) {//some initialization steps }
        return oneService;
    }

どうもありがとう。

4

1 に答える 1

4

質問 1 については、はい、modifyApiFacadeImplのコンストラクターを介してこれを実現できます。CtConstructor#insertAfter割り当てステートメントを追加するために使用することを忘れないでください。

ClassPool pool = ClassPool.getDefault();
CtClass factoryClass = pool.getCtClass("ServiceFactory");
CtConstructor constructor = factoryClass.getDeclaredConstructor(null);
String setMockStatement = String.format("service = new %s();",
        MockServiceImpl.class.getCanonicalName());
constructor.insertAfter(setMockStatement);
factoryClass.toClass();
new ServiceFactory().getService().say();

ご提案の方法でやってみました。しかし、うまくいきませんでした。いくつかのデバッグの後、フィールドを削除しても、このフィールドの初期化ステートメントは削除されないことがわかりました。予想される初期化ステートメントでフィールドを再度追加すると、元の初期化ステートメントの前に実行されます。したがって、serviceフィールドはMockServiceImpl最初に に割り当てられ、次に に割り当てられnullます。次の javassist バグを参照してください。

javassist-3.14.0-GA - removeField および addField、CtField.make を使用してクラス変数を削除および作成するときの、クラス変数のデフォルトの初期化子の問題

質問 2 については、なぜこのようなことが起こるのかわかりません。javassist のバージョンは何ですか? あなたの mockClass はどのように見えますか? 次の javassist バグを参照していただけますか?

Javassist により java.lang.ClassFormatError: Invalid length 561 in LocalVariableTable in class file

于 2013-03-15T06:34:20.857 に答える