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

时间:2017-08-25 04:42:55

标签: java javassist

我是javassist的新手,并开始搞乱它,并得到了一些工作。但是,还有很多其他的东西似乎无法发挥作用。

我制作了一个将代码注入类的方法 -

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

我使用此代码将字节码添加到方法中,以在代码运行时触发事件。

当我在最后编译类(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个小时,研究,测试不同的代码,并且无法获得任何工作。请帮助!!!!

2 个答案:

答案 0 :(得分:0)

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

我认为你应该尝试使用我的代码片段: 首先检查你是不是试图将构造函数添加到已经有构造函数的人(我只是为它编写一个布尔值,你应该自己检查一下如何做),你还应该检查这个类是不是一个接口。 / p>

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

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

答案 1 :(得分:0)

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

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

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