2013-03-15 3 views
5

開発中のenvでいくつかのサービスを模擬しようとしています。 serviceFactoryコードは次のようなものです:javassistを使用してフィールドの初期値を変更する方法

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(); 
    .... 
} 

私の第一は、次のとおりです。 がこれを可能に使用してJavassistのでしょうか?そしてどうやって?

私はグーグルを使って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クラスの定義に従わないのですか?

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

多くの感謝:私はフィールドを削除する場合のフィールドの参照を削除するのに役立ちJavassistのありません。

+0

mockClass.getCanonicalName()とは何ですか? – longhua

+0

javassistのバージョンは何ですか? – longhua

答えて

4

質問1の場合は、ApiFacadeImplのコンストラクタを変更することでこれを実現できます。代入文を追加するには、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 - Problm in default initializer of class variables when they are deleted and created using removeField and addField, CtField.make

は質問#2のために、私はこれがなぜ起こるかわかりません。 javassistのバージョンは何ですか?あなたのモッククラスはどのように見えますか? javassistのバグを参照してください。

Javassist causes java.lang.ClassFormatError: Invalid length 561 in LocalVariableTable in class file

+0

lhuang、答えに感謝します。実際、mockclassはOneServuceを拡張するだけです:public class MockClass extends OneService {// nothing}。私は参照リンクをチェックします。私はjavassist-3.11.0.GA btwを使用します。 – kingpeter

+0

OneServiceはインターフェイスかクラスですか? – longhua

+0

OneServiceはクラスです。 – kingpeter

関連する問題