Search code examples
javadependency-injectionconstructorjaxbinstantiation

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?


Solution

  • 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