Have you ever heard of custom XML namespaces for Spring? I know you love Spring (like I do), so... probably yes. They are available since Spring 2.0 which came out October 2006, so I just can't believe we didn't use them up to now.
The Old-Fashioned Way
Well, we are building our web apps on JSF and Spring Web Flow, and additionally use a custom framework for adding a few more features like mapping between business objects and view objects based on XML mapping descriptions. Of course, this framework part is designed "IoC friendly", i.e. it's based on interfaces and standard implementations that need to be plugged by Spring configuration.
We did this by providing a basic Spring configuration that defines defaults for most elements and injects all required references into the main configuration objects. To allow the project to provide custom implementations, this code relies on particular bean names that have to be defined by the custom project. Here's a simple example for the framework's configuration:
<bean id="iplIntegrationInfo" class="...IntegrationInfo">
<!-- myViewObjAccessor: to be defined in project specific Spring config file -->
<property name="viewObjAccessor" ref="myViewObjAccessor" />
<!-- myContextResolver: to be defined in project specific Spring config file -->
<property name="contextResolver" ref="myContextResolver" />
...
</bean>
<!-- These can be used for aliases if standard implementation should be used -->
<bean id="defaultViewObjAccessor" class="..." />
<bean id="defaultContextResolver" class="..." />
As you can see, all references are injected into the main configuration (iplIntegrationInfo). However, the referenced beans are not actually defined but have to be provided by the project. So, here's an example for the project's configuration:
<!-- use default -->
<alias alias="myViewObjAccessor" name="defaultViewObjAccessor" />
<!-- use custom implementation -->
<bean id="myContextResolver" class="..." />
Here we use the default for the first bean (by just establishing an alias for the configuration done in the framework file), and a custom implementation for the second.
This approach works, but obviously has a few drawbacks:
- Configuration is shared between framework and project specific configuration code.
- Project needs to know the framework's Spring file to do its work.
- Internal framework structures (bean names, classes) are exposed to the outside.
- A lot of XML code is required, even if using all the defaults.
Using Custom Namespaces
What should I say, we have switched to custom namespace lately and now configuration looks like this:
<ui:view-config/>
That's as short as it can be, but still is a complete configuration in case the project is using default implementations for all the beans. If the project would like to override some settings, the configuration file may be:
<ui:view-config>
<ui:viewobj-accessor ref="viewObjAccessor"/>
<ui:context-resolver ref="contextResolver"/>
</ui:view-config>
<bean id="viewObjAccessor" class="..." />
<bean id="contextResolver" class="..." />
Hence, you have the option to override all default implementations, and use meaningful tags to do so. Can you see the gains?
How Does This Magic Work?
I'm not going to provide full description on how to author custom namespaces since that is available in the Spring reference documentation and elsewhere. So, just a few basics to give you the idea...
First of all, you have to provide an XML schema describing your namespace. Then you have to implement a Namespace Handler that links each tag of your namespace to an XML parser, the Bean Definition Parser. And then, of course, those parsers must be implemented.
Each bean defintion parser is supposed to parse the XML element (including all attributes and sub elements) and to build the appropriate Spring beans. Spring provides a small hierarchy of classes you can use, depending on your situation:
- The most specific is
AbstractSimpleBeanDefinitionParser
that fits well when there is a strong correlation between the attributes on a tag and the properties on your bean. In this case, there is really few code on your side. - The
AbstractSingleBeanDefinitionParser
is a bit more general, it allows you to create any single bean definition for an arbitrary complex nested XML structure. This involves more coding, of course, but still does some work for you (like automatically registering the bean in the context). This is the one you'll probably use most of the time. - The
AbstractBeanDefinitionParser
is even more general and allows you to create multiple bean definitions and register them directly. It takes care of id generation and a few other things. - Instead of using the latter one, you could also implement the
BeanDefinitionParser
interface directly, giving you all the flexibility you need.
All in all, you'll need a few hours to get comfortable with all the classes that are used to build up the bean definitions (DomUtils
, BeanDefinition
, BeanDefinitionBuilder
, RuntimeBeanReference
and ManagedMap
/ManagedList
to name a few).
Okay, that's all for now, so go ahead and make life easier for your customers, your team, or yourself by providing a well-designed custom Spring namespace!
This comment has been removed by the author.
ReplyDeleteoldie, but goldie... thx for the quick overview.
ReplyDelete