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.
精彩评论