Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreLately it seems like I've been focusing on creating Spring XML namespaces. It's been a lot of trial and error (both on the XSD and Spring side) to get a good pattern for creating parsers. One of the biggest confusions that I ran into was the AbstractBeanDefinitionParser hierarchy. At this point it isn't documented especially well (but there is a JIRA for it, so it'll be fixed before GA), so I'll give you a rundown of your choices, what they're good for and how to use them.
I'm going to start at the most specific and work towards the most general to show how to gain more power when you need it. If you want to skip the examples and see the summary, check here.
<util:properties location="..." />
public class PropertiesFactoryBean extends PropertiesLoaderSupport
implements FactoryBean, InitializingBean {
...
public void setLocation(Resource location) {
this.locations = new Resource[] {location};
}
...
}
You'll notice that the location attribute on the util:properties tag matches a java bean property on the PropertiesFactoryBean type. The AbstractSimpleBeanDefinitionParser automatically extracts the attribute and maps it to that property. To get this behavior, you only need to implement a single method getBeanClass(). So the implementation for this example looks like:
public class PropertiesBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {
protected Class getBeanClass(Element element) {
return PropertiesFactoryBean.class;
}
}
As with all of the abstract parsers, the framework code hidden behind the scenes takes the bean definition that is created and registers it with the application context.
<tx:advice>
<tx:attributes>
<tx:method name="get*" read-only="false" />
</tx:attributes>
</tx:advice>
public class TransactionInterceptor extends TransactionAspectSupport
implements MethodInterceptor, Serializable {
...
public void setTransactionAttributes(Properties transactionAttributes) {
NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
tas.setProperties(transactionAttributes);
this.transactionAttributeSource = tas;
}
...
}
As you can see with the complex nested structure of tx:advice there isn't going to be that one to one mapping we saw earlier. However, with AbstractSingleBeanDefinitionParser you get to do arbitrary traversal of the DOM structure like so:
class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
...
protected void doParse(Element element, BeanDefinitionBuilder builder) {
// Set the transaction manager property.
builder.addPropertyReference(TxNamespaceUtils.TRANSACTION_MANAGER_PROPERTY,
element.getAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE));
List txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES);
if (txAttributes.size() > 1) {
throw new IllegalStateException("Element 'attributes' is allowed at most once inside element 'advice'");
}
else if (txAttributes.size() == 1) {
// Using attributes source.
parseAttributes((Element) txAttributes.get(0), builder);
}
else {
// Assume annotations source.
Class sourceClass = TxNamespaceUtils.getAnnotationTransactionAttributeSourceClass();
builder.addPropertyValue(TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE, new RootBeanDefinition(sourceClass));
}
}
...
}
You can see here we're examining the DOM and making complex decisions about the bean definition based on it. As I said earlier, I think this will be one of the most used support classes for doing bean definition parsing.
<tx:annotation-driven />
Those familiar with Spring 2.0 and its new namespaces should recognize this tag as being a one-liner that will automatically detect @Transactional annotations and proxy the classes they are contained in. Now under the hood, the same set of bean definitions that you created for a DefaultAutoProxyCreator style behavior in Spring 1.2.8 are created; 4 beans in total. So what does an example of this kind of behavior look like?
class AnnotationDrivenBeanDefinitionParser extends AbstractBeanDefinitionParser {
...
protected BeanDefinition parseInternal(Element element, ParserContext parserContext) {
// Register the APC if needed.
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext);
boolean proxyTargetClass = TRUE.equals(element.getAttribute(PROXY_TARGET_CLASS));
if (proxyTargetClass) {
AopNamespaceUtils.forceAutoProxyCreatorToUseClassProxying(parserContext.getRegistry());
}
String transactionManagerName = element.getAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE);
Class sourceClass = TxNamespaceUtils.getAnnotationTransactionAttributeSourceClass();
// Create the TransactionInterceptor definition
RootBeanDefinition interceptorDefinition = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDefinition.getPropertyValues().addPropertyValue(
TxNamespaceUtils.TRANSACTION_MANAGER_PROPERTY, new RuntimeBeanReference(transactionManagerName));
interceptorDefinition.getPropertyValues().addPropertyValue(
TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE, new RootBeanDefinition(sourceClass));
// Create the TransactionAttributeSourceAdvisor definition.
RootBeanDefinition advisorDefinition = new RootBeanDefinition(TransactionAttributeSourceAdvisor.class);
advisorDefinition.getPropertyValues().addPropertyValue(TRANSACTION_INTERCEPTOR, interceptorDefinition);
return advisorDefinition;
}
...
}
The big addition here is the ability to get to the <span style="font-family:courier>ParserContext. This context gives you the ability to delegate sub elements to the namespace handler again and let their parsers create and return bean definitions. It's actually one of the features I really like. The <span style="font-family:courier>ParserContext also allows you to create your own definitions and register them directly if you want.
So there you have it, a quick intro to bean definition parsing. What I'd like to know is how many of you are doing this? What have you created namespaces for and how are you using the parser hierarchy? Use the comments to have your voice heard. Who knows, your experiences and suggestions may make there way into a JIRA as an enhancement...