使用Javassist将注释添加到运行时生成的类 [英] Adding an annotation to a runtime generated class using Javassist
问题描述
我正在使用Javassist(Java 1.7)向类ClassA添加注释,但我得到了异常。我究竟做错了什么?我尝试的代码如下所示:
I'm using Javassist(Java 1.7) to add an annotation to the class ClassA, but i get the exception. What am i doing wrong? The code I tried looks like this:
ClassA.java
ClassA.java
public class ClassA
{
}
添加方法
public static <T> Class<T> addXmlRootAnnotationDynamicly(Class<T> declaredTyp) throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException
{
//pool creation
ClassPool pool = ClassPool.getDefault();
//extracting the class
CtClass cc = pool.getCtClass(declaredTyp.getCanonicalName());
// create the annotation
ClassFile ccFile = cc.getClassFile();
ConstPool constpool = ccFile.getConstPool();
AnnotationsAttribute attr = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
Annotation annot = new Annotation("javax.xml.bind.annotation.XmlRootElement", constpool);
attr.addAnnotation(annot);
// add the annotation to the class
cc.getClassFile().addAttribute(attr);
// transform the ctClass to java class
Class<T> dynamiqueBeanClass = cc.toClass();
//instanciating the updated class
// T sayHelloBean = dynamiqueBeanClass.newInstance();
return dynamiqueBeanClass;
}
致电
Class<ClassA> addXmlRootAnnotationDynamicly = addXmlRootAnnotationDynamicly(ClassA.class);
异常
javassist.CannotCompileException:by java.lang.LinkageError:loader(sun / misc / Launcher $ AppClassLoader的实例):为名称尝试重复的类定义:de / it_p / pvlight / share / util / ClassA
at javassist.ClassPool.toClass(ClassPool.java:1099)
at javassist.ClassPool.toClass(ClassPool.java:1042)
at javassist.ClassPool.toClass(ClassPool.java:1000)
at javassist.CtClass.toClass(CtClass.java:1224)
at de.it_p.pvlight.share.util.JAXBUtil.addXmlRootAnnotationDynamicly(JAXBUtil.java:107)
at de.it_p.pvlight。 share.util.JAXBUtilTest.addXmlRootAnnotationDynamicly(JAXBUtilTest.java:60)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.lang.reflect.Method.invoke(Method.java:606 )
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.lang.reflect.Method.invoke(Method。 java:606)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution。运行(TestExecution.java:38)
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit。 runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt。 internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
引起:java.lang.LinkageError:loader(sun / misc / Launcher $ AppClassLoader的实例):尝试重复的类名定义: de / it_p / pvlight / share / util / ClassA
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
在java.lang.ClassLoader.defineClass(ClassLoader.java :643)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.lang.reflect.Method.invoke(Method.java:606)
at javassist.ClassPool.toClass2 (ClassPool.java:1112)
at javassist.ClassPool.toClass(ClassPool.java:1093)
... 15 more
javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "de/it_p/pvlight/share/util/ClassA" at javassist.ClassPool.toClass(ClassPool.java:1099) at javassist.ClassPool.toClass(ClassPool.java:1042) at javassist.ClassPool.toClass(ClassPool.java:1000) at javassist.CtClass.toClass(CtClass.java:1224) at de.it_p.pvlight.share.util.JAXBUtil.addXmlRootAnnotationDynamicly(JAXBUtil.java:107) at de.it_p.pvlight.share.util.JAXBUtilTest.addXmlRootAnnotationDynamicly(JAXBUtilTest.java:60) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.lang.reflect.Method.invoke(Method.java:606) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.lang.reflect.Method.invoke(Method.java:606) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) Caused by: java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "de/it_p/pvlight/share/util/ClassA" at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:800) at java.lang.ClassLoader.defineClass(ClassLoader.java:643) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.lang.reflect.Method.invoke(Method.java:606) at javassist.ClassPool.toClass2(ClassPool.java:1112) at javassist.ClassPool.toClass(ClassPool.java:1093) ... 15 more
推荐答案
问题的根源可以在堆栈跟踪中找到:
The root of your problem can be found in your stack trace:
尝试重复的名称类定义:de / it_p / pvlight / share / util / ClassA
attempted duplicate class definition for name: "de/it_p/pvlight/share/util/ClassA"
您的 addXmlRootAnnotationDynamicly
方法接受加载的类,并重新定义这个相同的类而不更改其名称。在重新定义之后,您将尝试再次加载已更改的类。然而,这在Java中是不可能的,其中任何 ClassLoader
一次只能加载一个给定名称的类。
Your addXmlRootAnnotationDynamicly
method takes a loaded class and redefines this very same class without changing its name. After this redefinition, you attempt to load the altered class one more time. This is however not possible in Java where any ClassLoader
can only load a class of a given name one single time.
因此, pool.getCtClass
方法取一个 String
代替已加载类
,并与 CtClass
es一起使用,用于描述已卸载 类
ES。要解决您的问题,您有不同的选择:
For this reason, the pool.getCtClass
method takes a String
instead of a loaded Class
and works with CtClass
es which are used to describe unloaded Class
es. To overcome your problem, you have different choices:
- 将方法的签名更改为
addXmlRootAnnotationDynamicly(String)
并传递de.it_p.pvlight.share.util.ClassA
作为参数。在转换代码之前,请确保未加载此类。因此,您应该在应用程序的启动时运行转换,以确保在转换之前不会意外加载该类。然后在cc.toClass()
上加载您更改的Class
。 - 创建使用随机名称的参数类(或使用接口)的子类。然后,子类与您的参数类类型兼容,但从不加载。
- 使用Instrumentation API 在运行时重新定义加载的类。
- 确保输入类和输出类已加载不同的
ClassLoader
s。 (不推荐)
- Change the signature of your method to
addXmlRootAnnotationDynamicly(String)
and deliverde.it_p.pvlight.share.util.ClassA
as the argument. Make sure that this class is not loaded before you transform it, anywhere in your code. You should therefore run the transformation at your application's startup to make sure that the class is not accidentally loaded before the transformation. Your alteredClass
is then loaded oncc.toClass()
. - Create a subclass of the argument class (or use interfaces) which uses a random name. The subclass is then type compatible to your argument class but is never loaded.
- Use the Instrumentation API to redefine your loaded class at runtime.
- Make sure that the input class and the output class are loaded with different
ClassLoader
s. (not recommended)
这篇关于使用Javassist将注释添加到运行时生成的类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!