开发者

Is there something like PostConstruct for JAXB-annnotated classes?

Is there such a functionality in JAXB to perform operations on a class after it is unmarshalled i.e. 开发者_运维技巧after it is constructed by JAXB? If not, how could I achieve this?


You can use JAXB Unmarshal Event Callbacks which are defined in your JAXB class e.g:

// 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 );
}


Though the the demanded functionality seems not to be present in JAXB, I managed to achieve something which goes into the right direction:

  • I'm using JSR-305's @PostConstruct annotation
    (it's just a nacked annotation, no functionality is provided by the JSR)
  • I add an unmasrshaller-listener to the unmarshaller, which gets invoked by JAXB every time an object was unmarshalled.
  • I inspect this object using Java reflection and search for the @PostConstruct annotation on a method
  • I execute the method

Tested. Works.

Here is the code. Sorry, I'm using some external reflection API to get all methods, but I think the idea is understandable:

Implementation

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
Added a check for @PostConstruct-annotated method, to ensure it is final.
Do you think it's a useful restriction?

Usage

Here is how the concept might be used.

@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!

Filed a feature request

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


It's not a 100% solution, but you can always register a XmlAdapter using @XmlJavaTypeAdapter annotation for this type.

The downside would be that you have to serialize the class yourself (?). I am not aware of any simple way of accessing and calling the default serialization mechanism. But with custom [XmlAdapter] you can control how is the type serialized and what happens before/after it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜