Is there something like PostConstruct for JAXB-annnotated classes?
While the required functionality doesn't seem to be present in JAXB, I've managed to achieve something that goes in the right direction:
- I am using JSR-305 annotation
@PostConstruct
(this is just a deprecated annotation, functionality is not provided by JSR) - I am adding unmasrshaller-listener to unmarshaller, which is called by JAXB every time the object was unmarshalled.
- I am checking this object using Java reflection and looking for an annotation
@PostConstruct
for the method - I am executing method
Tested. Works.
Here is the code. Sorry, I'm using the external reflection API to get all methods, but I think the idea is clear:
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 the @PostConstruct
non-nominated method to make sure it is final.
Do you find this a useful constraint?
Using
This is how the concept can 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!
Feature request submitted
source to share
You can use class-defined event callbacks. More details here http://java.sun.com/javase/6/docs/api/javax/xml/bind/Unmarshaller.html#unmarshalEventCallback
For example, put this method in a JAXB object:
// 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); }
source to share
This is not a 100% solution, but you can always register XmlAdapter
with a help @XmlJavaTypeAdapter annotation
for this type.
The downside would be that you have to serialize the class yourself (?). I don't know what a simple way to access and call the default serialization mechanism. But with a custom [ XmlAdapter
] you can control how the type is serialized and what happens before / after it.
source to share