Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
359 views
in Technique[技术] by (71.8m points)

java - Adding an annotation to a runtime generated class using Javassist

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

public class ClassA
{

}

add method

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;

}

call

Class<ClassA> addXmlRootAnnotationDynamicly = addXmlRootAnnotationDynamicly(ClassA.class);

Exception

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The root of your problem can be found in your stack trace:

attempted duplicate class definition for name: "de/it_p/pvlight/share/util/ClassA"

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.

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:

  • 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)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...