使用 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 个多小时,研究、测试不同的代码,但无法正常工作。请帮助!!!!
您的问题可能是您正在创建一个新方法而不是构造函数。您是否尝试查看 here 和 CtNewConstructor
?
我认为你应该尝试像我的代码片段这样的东西:
首先检查你没有尝试向已经有构造函数的人添加构造函数(我只是为它写了一个布尔值你应该自己检查如何做)并且你还应该检查这个 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 文件而不会出错,请让我知道!到那时我将手动输入文件名的字符串。
我是 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 个多小时,研究、测试不同的代码,但无法正常工作。请帮助!!!!
您的问题可能是您正在创建一个新方法而不是构造函数。您是否尝试查看 here 和 CtNewConstructor
?
我认为你应该尝试像我的代码片段这样的东西: 首先检查你没有尝试向已经有构造函数的人添加构造函数(我只是为它写了一个布尔值你应该自己检查如何做)并且你还应该检查这个 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 文件而不会出错,请让我知道!到那时我将手动输入文件名的字符串。