2013-03-11 42 views
3

我想在运行时使用Javassist注入JAXB注释。我已经写了下面的代码:javassist没有注入现有字段的注释

public class AssistAnnotationInjector { 
public static void addAnnotationRunTime(String className, String fieldName) throws NotFoundException, CannotCompileException, IOException, ClassNotFoundException{ 

    CtClass ctClass = ClassPool.getDefault().get(className); 
    ClassFile ccFile = ctClass.getClassFile(); 
    ConstPool constPool = ccFile.getConstPool(); 
    AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); 
    Annotation annot = new Annotation("javax.xml.bind.annotation.XmlTransient",constPool); 
    attr.addAnnotation(annot); 
    CtField field = ctClass.getDeclaredField(fieldName); 
    field.getFieldInfo().addAttribute(attr); 
    System.out.println(field.getAnnotation(XmlTransient.class)); 
    ccFile.setVersionToJava5(); 
    ctClass.writeFile(); 
} 

public static void main (String args[]) throws CannotCompileException, NotFoundException, IOException, SecurityException, NoSuchMethodException, ClassNotFoundException, JAXBException, NoSuchFieldException{ 
    Person<Student> p = new Person<Student>(); 
    p.setName("XYZ"); 
    Student s = new Student(); 
    s.setName("ABC"); 
    s.setId("239423"); 
    p.setPayload(s); 

    addAnnotationRunTime("RuntimeAnnotation.Person", "name"); 

    Field f = p.getClass().getDeclaredField("name"); 
    System.out.println(f.getAnnotation(XmlTransient.class)); 

    JAXBContext context = JAXBContext.newInstance(p.getClass()); 

     Marshaller mr = context.createMarshaller(); 
     mr.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); 

     mr.marshal(p, System.out); 
} 
} 

而且Person.java类是:

@XmlRootElement(name="Person") 
@XmlAccessorType(XmlAccessType.FIELD) 
@XmlSeeAlso({Student.class}) 
public class Person <T>{ 

private T payload; 

private String name; 

public void setPayload(T payload){ 
    this.payload = payload; 
} 

public T getPayload(){ 
    return payload; 
} 

public void setName(String name){ 
    this.name = name; 
} 

public String getName(){ 
    return name; 
} 
} 

在AssistAnnotationInjector.java,我想XmlTransient注释添加到 '姓名' 字段。但名称字段仍然在编组输出。为什么这样?

PS:元帅输出为:预计不会

@javax.xml.bind.annotation.XmlTransient 
null 
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<Person> 
<payload xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="student"> 
    <name>ABC</name> 
    <id>239423</id> 
</payload> 
**<name>XYZ</name>** 
</Person> 

名称标签呈现在输出..

+0

请记住,您的代码当前的方式是,只要启动AssistAnnotationInjector,Person类就会由类加载器加载,这意味着即使注入成功,您已经将非注入类加载到内存中,是使用的那个。要检查注入是否成功,请在编译的类中使用'java -c -l -v -p'并检查该字段是否已注释。你也可以运行另一个'TestClass'来完成编组。 – pabrantes 2013-03-11 12:38:06

+0

所以没有办法改变加载类的字节码?在这种情况下,我必须实现新的类加载器吗? – user2122853 2013-03-12 03:54:27

+0

据我所知,一旦默认的类加载器(默认读取JVM中的标准加载器)加载类的字节码,您将无法更改它。重新加载已加载的已更改类的唯一方法是通过另一个类加载器实例加载它。或者,您也可以拥有自己的类加载器实现,以跟踪您的bean并在需要时热交换它们。另外,请记住,一旦通过字节码修改添加了注释,它就会保存在字节码中。因此,不会再次序列化名称(直到您通过设备再次删除注释)。 – pabrantes 2013-03-12 08:57:09

回答

0

您basicaly有2个选项:

  1. 做修改你加载类。你不能以正常的方式使用反射!可以尝试使用Maven插件的org.reflections来预取类。有关更多信息,请参阅here

  2. 使用自定义类加载器加载修改后的类。有关更多信息,请参见here

0

将属性添加到字段后,需要调用ctClass.toClass()方法,该方法冻结类。在此之后,您可以检查注释。