All entries for Tuesday 13 May 2008

May 13, 2008

Making Spring XML config better

One of my pet peeves with Spring is the way that, left unchecked, it can grow yards and yards of inscrutiable XML. Over the last few days, amongst other things, I’ve been looking at whether we can improve things.

Here’s an example to get started with. We use classes called ModelAccessors to hold references to data that our Spring WebFlow processes need. A typical flow might have half-a-dozen ModelAccessors, for all of it’s various bits of state. They look like this in the application context:

  <bean id="emptyFilesAccessor" class="uk.ac.warwick.sbr.webflow.FlowScopeModelAccessor">
    <constructor-arg index="0" value="duplicateFiles"/>
    <constructor-arg index="1" value="java.util.List"/>
  </bean>
  <bean id="invalidFileNamesAccessor" class="uk.ac.warwick.sbr.webflow.FlowScopeModelAccessor">
    <constructor-arg index="0" value="createdFiles"/>
    <constructor-arg index="1" value="java.util.List"/>    
  </bean>

  <bean id="uploadZipFileFormAccessor" class="uk.ac.warwick.sbr.webflow.FlowScopeModelAccessor">
    <constructor-arg index="0" value="uploadZipFileForm"/>
    <constructor-arg index="1" value="uk.ac.warwick.sbr.webflow.action.upload.UploadZipFileForm"/>
  </bean>
  ... continues for many more...

Now, there’s a couple of problems with this:
1) There’s 4 lines of XML for every accessor, and only 2 things ever change; the ID and the second constructor argument (the first arg is derivable from the ID). Even the second argument is usually just ‘java.util.List’

2) They’re not very communicative. The most important thing about this element (that it’s a ModelAccessor) is an attribute. There’s very little information about what those constructor-arguments actually mean. Surely we could have something a bit more expressive?

And luckily, there’s a simple fix for both of these problems. Spring has support for extending the XML context syntax by adding in your own custom namespaces. Define your extension in an XSD schema, write a parser plug-in, and away you go:

<sbr:model-accessor id="uploadZipFileFormAccessor" modelclass="uk.ac.warwick.sbr.webflow.action.upload.UploadZipFileForm"/>
  <sbr:list-model-accessor id="invalidFileNamesAccessor"/>
  <sbr:list-model-accessor id="emptyFilesAccessor" />

- much nicer.

So, how much effort is this to implement? Not that much, as it turns out. The instructions are a pretty good start. The only annoyance you’re likely to face is cryptic SAX parsing errors like this:

 org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 8 in XML document from class path resource [uk/ac/warwick/sbr/spring/sbr-modelaccessor.xml] is invalid; nested exception is org.xml.sax.SAXParseException: cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'sbr:model-accessor'.

What this means is that somewhere in either the header of your context.xml file, or in your META-INF/spring.handlers file, there’s a typo, so the parser can’t go from xmlns: element to xsi:schemalocation element to spring.handlers mapping.

Spring provides a base class for the NamespaceHandler and BeanDefinitionParser, so there’s really not much work to do in implementing them:

    public void init() {
        registerBeanDefinitionParser("model-accessor", new ModelAccessorBeanDefinitionParser()); 
    }
    final class ModelAccessorBeanDefinitionParser extends AbstractSingleBeanDefinitionParser{
        protected Class getBeanClass(Element element) {
            return FlowScopeModelAccessor.class; 
         }
        protected void doParse(Element element, BeanDefinitionBuilder bean) {
         bean.addConstructorArgValue(element.getAttribute("id").replaceAll("Accessor$", ""));           
         bean.addConstructorArgValue(element.getAttribute("modelclass"));               
         }

Easy!


Most recent entries

Loading…

Search this blog

on twitter...


    Tags

    Not signed in
    Sign in

    Powered by BlogBuilder
    © MMXXI