使用Javassist将注释添加到运行时生成的类中

时间:2014-04-28 08:32:03

标签: java annotations javassist

我正在使用Javassist(Java 1.7)向类ClassA添加注释,但我得到了异常。我究竟做错了什么?我试过的代码看起来像这样:

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)       在org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)       在org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)       在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)       在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)       在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)       在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)       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个

1 个答案:

答案 0 :(得分:11)

问题的根源可以在堆栈跟踪中找到:

  

尝试重复的名称类定义:&#34; de / it_p / pvlight / share / util / ClassA&#34;

您的addXmlRootAnnotationDynamicly方法需要加载类,并重新定义此类,而不更改其名称。在重新定义之后,您将再次尝试加载已更改的类。但是,在Java中,ClassLoader only load a class of a given name one single time可以Instrumentation API

,这是不可能的

出于这个原因,pool.getCtClass方法使用String代替加载的Class,并使用用于描述卸载的CtClass es Class es。为了解决您的问题,您有不同的选择:

  • 将方法的签名更改为addXmlRootAnnotationDynamicly(String)并传递de.it_p.pvlight.share.util.ClassA作为参数。在代码中的任何位置转换之前,请确保未加载此类。因此,您应该在应用程序的启动时运行转换,以确保在转换之前不会意外加载该类。然后,您在Class上加载了已更改的cc.toClass()
  • 创建使用随机名称的参数类(或使用接口)的子类。然后,子类与您的参数类类型兼容,但从不加载。
  • 使用{{3}}在运行时重新定义已加载的类。
  • 确保输入类和输出类加载了不同的ClassLoader s。 (不推荐)