使用 Javassist 向运行时生成的类添加注释 [英] Adding an annotation to a runtime generated class using Javassist

查看:65
本文介绍了使用 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 (instance of sun/misc/Launcher$AppClassLoader): 尝试为名称重复类定义:de/it_p/pvlight/share/util/ClassA"在 javassist.ClassPool.toClass(ClassPool.java:1099)在 javassist.ClassPool.toClass(ClassPool.java:1042)在 javassist.ClassPool.toClass(ClassPool.java:1000)在 javassist.CtClass.toClass(CtClass.java:1224)在 de.it_p.pvlight.share.util.JAXBUtil.addXmlRootAnnotationDynamicly(JAXBUtil.java:107)在 de.it_p.pvlight.share.util.JAXBUtilTest.addXmlRootAnnotationDynamicly(JAXBUtilTest.java:60)在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)在 java.lang.reflect.Method.invoke(Method.java:606)在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)在 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 (instance of sun/misc/Launcher$AppClassLoader): 试图重复名称的类定义:de/it_p/pvlight/share/util/ClassA"在 java.lang.ClassLoader.defineClass1(Native Method)在 java.lang.ClassLoader.defineClass(ClassLoader.java:800)在 java.lang.ClassLoader.defineClass(ClassLoader.java:643)在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)在 java.lang.reflect.Method.invoke(Method.java:606)在 javassist.ClassPool.toClass2(ClassPool.java:1112)在 javassist.ClassPool.toClass(ClassPool.java:1093)……还有 15 个

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 而不是加载的 Class 并与 CtClasses 用于描述 unloaded Classes.为了解决您的问题,您有不同的选择:

For this reason, the pool.getCtClass method takes a String instead of a loaded Class and works with CtClasses which are used to describe unloaded Classes. To overcome your problem, you have different choices:

  • 将您的方法的签名更改为 addXmlRootAnnotationDynamicly(String) 并提供 de.it_p.pvlight.share.util.ClassA 作为参数.在转换之前,确保在代码中的任何位置都没有加载此类.因此,您应该在应用程序启动时运行转换,以确保在转换之前不会意外加载该类.然后将更改后的 Class 加载到 cc.toClass().
  • 创建使用随机名称的参数类(或使用接口)的子类.然后子类的类型与您的参数类兼容,但永远不会加载.
  • 使用 Instrumentation API 重新定义加载的类在运行时.
  • 确保输入类和输出类使用不同的 ClassLoader 加载.(不推荐)
  • Change the signature of your method to addXmlRootAnnotationDynamicly(String) and deliver de.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 altered Class is then loaded on cc.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 ClassLoaders. (not recommended)

这篇关于使用 Javassist 向运行时生成的类添加注释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆