使用 java 创建空构造函数 (java) ssist 无法开始工作

Create empty constructor (java) with javassist cannot get to work

我是 javassist 的新手,开始弄乱它并让一些东西起作用。但是,还有很多其他的似乎不起作用。

我做了一个注入代码到classes-

的方法
public static void editMethodAddEvent(CtClass target, MethodInfo method, CtClass eventClass, int start, int[] bytes, int stacksize, String constructorParameters) throws BadBytecode, NotFoundException, CannotCompileException {
    target.defrost();
    CodeAttribute codeAttribute = method.getCodeAttribute();
    CodeIterator iterator = codeAttribute.iterator();
    int classID = method.getConstPool().addClassInfo(eventClass);
    int constrnatID = method.getConstPool().addNameAndTypeInfo("<init>",constructorParameters);
    int constructID = method.getConstPool().addMethodrefInfo(classID,constrnatID);
    int callnatID = method.getConstPool().addNameAndTypeInfo("call","()V");
    int callID = method.getConstPool().addMethodrefInfo(classID,callnatID);
    iterator.insertGap(start,bytes.length);

    for (int i = 0; i < bytes.length; i++) {
        int byteCode = bytes[i];
        if (byteCode >= 0) {
            iterator.writeByte(byteCode, start+i);
        } else if (byteCode == -1) {
            iterator.writeByte(classID,start+i);
        } else if (byteCode == -2) {
            iterator.writeByte(constructID, start+i);
        } else if (byteCode == -3) {
            iterator.writeByte(callID, start+i);
        }
    }

    if(stacksize > codeAttribute.getMaxStack())
        codeAttribute.setMaxStack(stacksize);
    target.toClass();
}

我使用此代码将字节码添加到方法中,以便在代码为 运行.

时触发事件

当我最后编译 class (target.toClass()) 时,它没有错误(并且工作正常)。

但是,当我使用下面的代码添加方法(在本例中为空构造函数)时,它出错了。

public static Class addEmptyConstructor(Class clazz) throws NotFoundException, CannotCompileException {
    CtClass ctClass = ClassPool.getDefault().getCtClass(clazz.getName());
    ctClass.defrost();
    ClassFile classFile = ctClass.getClassFile();
    MethodInfo newMethod = new MethodInfo(classFile.getConstPool(), "<init>", "()V");
    newMethod.setCodeAttribute(new CodeAttribute(classFile.getConstPool(),1,1,new byte[]{0,0,0,0,0},new ExceptionTable(classFile.getConstPool())));
    CodeIterator iterator = newMethod.getCodeAttribute().iterator();
    iterator.writeByte(42, 0);
    iterator.writeByte(183,1);
    iterator.writeByte(0,2);
    iterator.writeByte(1,3);
    iterator.writeByte(177,4);
    classFile.addMethod(newMethod);
    return ClassPool.getDefault().toClass(ctClass);
}

给出的错误是:

javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "mod/TestClass"
        at javassist.util.proxy.DefineClassHelper.toClass2(DefineClassHelper.java:140)
        at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:95)
        at javassist.ClassPool.toClass(ClassPool.java:1143)
        at javassist.ClassPool.toClass(ClassPool.java:1106)
        at javassist.ClassPool.toClass(ClassPool.java:1064)
        at mod.edit.MethodEdit.addEmptyConstructor(MethodEdit.java:113)
        <other nonrelevant stuff...>
  Caused by: java.lang.LinkageError: loader (instance of  
  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for 
  name: "haven/mod/TestClass"
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at javassist.util.proxy.DefineClassHelper.toClass3(DefineClassHelper.java:152)
        at javassist.util.proxy.DefineClassHelper.toClass2(DefineClassHelper.java:134)
        ... 10 more

我的目标是创建一个空的构造函数,这样我就可以在不输入参数的情况下创建任何对象的实例。具体来说,我有很多种带有参数的事件。我不想在编译时访问这个构造函数。因此,我尝试在 运行 期间使用 javassist 添加构造函数,但它拒绝编译。如果我只是简单地编辑一个方法,它不会大惊小怪,但是如果我添加一个方法,它似乎拒绝做我想做的事。我该如何解决这个问题?我已经尝试了 16 个多小时,研究、测试不同的代码,但无法正常工作。请帮助!!!!

您的问题可能是您正在创建一个新方法而不是构造函数。您是否尝试查看 hereCtNewConstructor

我认为你应该尝试像我的代码片段这样的东西: 首先检查你没有尝试向已经有构造函数的人添加构造函数(我只是为它写了一个布尔值你应该自己检查如何做)并且你还应该检查这个 class 不是一个接口。

然后用 class 创建一个新的构造函数,我刚刚链接了你并将它添加到你的 class。这是一个带有示例的小代码片段

 if (!hasDefaultConstructor && !ctClass.isInterface()) {
      CtConstructor defaultConstructor = CtNewConstructor.make("public " + ctClass.getSimpleName() + "() {}", ctClass);
      ctClass.addConstructor(defaultConstructor);
 }

我好像找到问题的根源了。我正在传递对我的方法的 class 引用。从 class 我正在做 class.getName(),以放入 classPool.get(需要字符串) 方法。似乎通过创建 class 的实例,classloader 无法重新加载,因为存在 class 的实例。从我有限的测试看来,即使我通过以下方式从外部调用该方法:

MethodEdit.addEmptyConstructor(TestClass.class.getName() )

只需调用 TestClass.class(创建 class 实例),代码就无法重新加载(不确定这是否有效,需要更多测试)。我认为这就是为什么我将代码注入方法的其他方法起作用的原因,因为我是通过直接名称获取 CtClass 和 CtMethod,而不是通过 class.getName() 获取它们。虽然我可以简单地手动调用该方法("mod.TestClass" 作为参数而不是 TestClass.class),但如果有可用的解决方案,我仍然可以使用 class 文件而不会出错,请让我知道!到那时我将手动输入文件名的字符串。