java - 如何使用 javassist 更改字段的初始值

标签 java javassist

我正在尝试在我的开发环境中模拟一些服务。 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 吗?又如何?

因为我找不到任何类似使用 javassist 使用 google 重新初始化字段的东西,所以我自己尝试删除它并重新创建它:

        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)

我的第二个问题是为什么会出现这个异常?哪一步违反了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 版本是多少?你的 mockClass 是什么样子的?能否请您引用以下 javassist 错误?

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

关于java - 如何使用 javassist 更改字段的初始值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15423981/

相关文章:

java - 使用 javaagent 获取 Web 应用程序类?

javassist 字段检查初始化

java - 在同一项目中使用 ASM 和 Javassist 的注意事项

java - 如何验证 selenium webdriver 中的文本颜色?

java - 将字符串编码为 UTF-8

java - 用坐标寻找迷宫中的最短路径

java - 数据存储和 Memcache 一致性 - Google App Engine 和 Objectify

java - 抛出 InterruptedException 时不会清除线程中断状态

java - 从动态编译的源代码中实例化一个对象