2013-03-15 12 views
5

Próbuję wyśmiać niektóre usługi w moim rozwijającym się środowisku. Kod serwisowy jest podobny do:jak zmienić wartość początkową pola za pomocą javassist

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

Wiem, że ta fabryka nie jest dobrze zakodowana i zaprojektowana. Nie mogę jednak zmodyfikować jego kodu. mój pomysł jest zmiana kodu bajtowego, więc mogę zdefiniować ją do czegoś podobnego:

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

Mój 1 jest: jest to możliwe przy użyciu javassist? I jak?

Ponieważ nie mogę znaleźć czegoś takiego ponownie zainicjować pole używając javassist za pomocą google, starałem się, usuwając je i odtworzyć go:

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

potem mam wyjątek:

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) 

Moje drugie pytanie brzmi: dlaczego ten wyjątek? Który krok nie pasuje do definicji klasy java? I kiedy usunąć pole robi javassist pomóc usunąć odniesienie pola w:

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

Wielkie dzięki.

+0

Co jest mockClass.getCanonicalName()? – longhua

+0

Jaka jest twoja wersja javassist? – longhua

Odpowiedz

4

Dla pytania nr 1, tak, można to osiągnąć, modyfikując konstruktor ApiFacadeImpl. Pamiętaj, aby użyć CtConstructor#insertAfter, aby dołączyć instrukcję przypisania.

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

Próbowałem tak, jak zaproponowałeś. Jednak to nie zadziałało. Po kilku debugowaniach stwierdziłem, że jeśli usuniemy pole, to nie usunie instrukcji inicjalizacyjnej dla tego pola. Jeśli dodamy pole ponownie z naszą oczekiwaną instrukcją inicjalizacji, zostanie wykonane przed oryginalną instrukcją inicjalizacji. Dlatego pole service jest początkowo przypisywane MockServiceImpl, a następnie przypisywane do null. Proszę odnieść się do następującego błędu 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

Na pytania nr 2, nie jestem pewien, dlaczego tak się dzieje. Jaka jest twoja wersja javassist? Jak wygląda Twój mockClass? Czy mógłbyś odnieść się do następującego błędu javassist?

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

+0

lhuang, dzięki za odpowiedź. W rzeczywistości klasa mockclass po prostu rozszerza OneServuce: klasa publiczna MockClass rozszerza OneService {// nothing}. Sprawdzę linki referencyjne. Używam javassist-3.11.0.GA btw. – kingpeter

+0

OneService to interfejs lub klasa? – longhua

+0

OneService to klasa. – kingpeter