我正在尝试在开发环境中模拟某些服务。 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();
    ....
}

我的第一个是:
使用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();

然后我得到了异常(exception):
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)

我的第二个问题是为什么会出现此异常?哪个步骤违反了Java类的定义?当我删除字段时,javassist帮助删除了中的字段引用
public OneService getOneService(){
        if(oneService==null) {//some initialization steps }
        return oneService;
    }

非常感谢。

最佳答案

对于问题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

10-08 12:08