2009-10-09 10 views
2

J'ai besoin d'effectuer certaines opérations sur une classe après qu'elle ait été unmarshalled (après qu'elle soit construite par JAXB, plutôt que par moi-même).Existe-t-il quelque chose comme PostConstruct pour les classes JAXB annnotées?

Existe-t-il une telle fonctionnalité dans JAXB?

Sinon, comment pourrais-je l'obtenir?

+1

Grande question, mais je ne pense pas qu'il y ait quelque chose comme ça. Il pourrait et devrait être, cependant, je vous encourage à enregistrer une demande de fonctionnalité sur http://jaxb.dev.java.net – skaffman

+0

Excellent :) https://jaxb.dev.java.net/issues/show_bug.cgi ? id = 698 – skaffman

+0

Regardez-vous la liste de diffusion JAXB pour les bugs? ;) Et comment ça se fait, qu'il a déjà 10 votes? –

Répondre

3

Bien que la la fonctionnalité demandée ne semble pas être présent dans JAXB, je réussi à réaliser quelque chose qui va dans la bonne direction:

  • J'utilise @PostConstruct annotation de JSR-305
    (il est juste une annotation nacked, aucune fonctionnalité n'est fournie par le JSR)
  • J'ajoute unmasrshaller-listener au unmarshaller, qui est invoqué par JAXB chaque fois qu'un objet est unmarshalled.
  • J'inspectent cet objet en utilisant la réflexion Java et la recherche de l'annotation @PostConstruct sur une méthode
  • I exécuter la méthode

Testé. Travaux.

Voici le code. Désolé, je suis en utilisant une API de réflexion externe pour obtenir toutes les méthodes, mais je pense que l'idée est compréhensible:

mise en œuvre

JAXBContext context = // create the context with desired classes 

Unmarshaller unmarshaller = context.createUnmarshaller(); 

unmarshaller.setListener(new Unmarshaller.Listener() { 

    @Override 
    public void afterUnmarshal(Object object, Object arg1) { 
    System.out.println("unmarshalling finished on: " + object); 

    Class<?> type = object.getClass(); 
    Method postConstructMethod = null; 

    for (Method m : ReflectionUtils.getAllMethods(type)) { 
     if (m.getAnnotation(PostConstruct.class) != null) { 
     if (postConstructMethod != null) { 
      throw new IllegalStateException(
       "@PostConstruct used multiple times"); 
     } 

     postConstructMethod = m; 
     } 
    } 

    if (postConstructMethod != null) { 
     System.out.println("invoking post construct: " 
      + postConstructMethod.getName() + "()"); 

     if (!Modifier.isFinal(postConstructMethod.getModifiers())) { 
     throw new IllegalArgumentException("post construct method [" 
      + postConstructMethod.getName() + "] must be final"); 
     } 

     try { 
     postConstructMethod.setAccessible(true); // thanks to skaffman 
     postConstructMethod.invoke(object); 
     } catch (IllegalAccessException ex) { 
     throw new RuntimeException(ex); 
     } catch (InvocationTargetException ex) { 
     throw new RuntimeException(ex); 
     } 
    } 
    } 

}); 

EDIT
Ajout d'un chèque de @PostConstruct méthode -annotated, à assurez-vous qu'il est définitif.
Pensez-vous que c'est une restriction utile?

Utilisation

Voici comment le concept pourrait être utilisé.

@XmlAccessorType(XmlAccessType.NONE) 
public abstract class AbstractKeywordWithProps 
    extends KeywordCommand { 

    @XmlAnyElement 
    protected final List<Element> allElements = new LinkedList<Element>(); 

    public AbstractKeywordWithProps() { 
    } 

    @PostConstruct 
    public final void postConstruct() { 
    // now, that "allElements" were successfully initialized, 
    // do something very important with them ;) 
    } 

} 

// further classes can be derived from this one. postConstruct still works! 

a déposé une demande de fonctionnalité

https://jaxb.dev.java.net/issues/show_bug.cgi?id=698

+0

Essayez d'appeler 'setAccessible (true)' sur 'postConstructMethod', ce qui devrait le rendre invocable. – skaffman

+0

Merci! Incorporé le changement dans le code. –

0

Ce n'est pas une solution à 100%, mais vous pouvez toujours enregistrer un XmlAdapter en utilisant @XmlJavaTypeAdapter annotation pour ce type. L'inconvénient serait que vous deviez sérialiser la classe vous-même (?). Je ne suis pas au courant d'un moyen simple d'accéder et d'appeler le mécanisme de sérialisation par défaut. Mais avec la personnalisation [XmlAdapter] vous pouvez contrôler comment le type est sérialisé et ce qui se passe avant/après.

5

Vous pouvez utiliser les callbacks événement 'classe définie'. En savoir plus ici http://java.sun.com/javase/6/docs/api/javax/xml/bind/Unmarshaller.html#unmarshalEventCallback
Par exemple mettre cette méthode en vous JAXB objet:

 
    //This method is called after all the properties (except IDREF) are unmarshalled for this object, 
    //but before this object is set to the parent object. 
    void afterUnmarshal(Unmarshaller u, Object parent) 
    { 
     System.out.println("After unmarshal: " + this.state); 
    } 
Questions connexes