Saturday, July 7, 2012

Spring Custom Namespaces

Spring Custom Namespaces  provides a good way to simplify the xml files used to describe the bean definitions of a Spring Application Context. It is a fairly old concept, first introduced with Spring 2.0, but deserves being reviewed once in a while.

Consider a case of having to configure a part of the beans for a Spring MVC application without custom namespaces - this would typically look like this:

<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
 <property name="webBindingInitializer">
  <bean class="">
   <property name="conversionService" ref="conversionService"></property>
   <property name="validator">
    <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
 <property name="messageConverters">
   <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
   <bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
   <bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
   <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
   <bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
   <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
   <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>

<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
 <property name="useSuffixPatternMatch" value="false"></property>

Here it is configuring two beans - a handlerAdapter to handle the MVC controller flow and a handlerMapping to keep the mapping between request URI's and the Controller methods to handle the requests.

The same configuration becomes very concise with a custom namespace, "" typically given a namspace prefix of "mvc":

<mvc:annotation-driven conversion-service="conversionService"> 

This is in essence the advantage of using a Custom namespace - a very concise way to describe the Spring bean definitions

So how does a custom namespace work:

This section in the Spring Reference document describes it much better than I can - . To summarize it, a custom namespace has 4 parts to it:

  • the schema - which describes the structure of the custom namespace - the tag names, attributes, child tags etc.
  • a NamespaceHandler - which creates the bean definition for the xml elements. However typically a better mechanism suggested by Spring document is to extend NameSpaceHandlerSupport and to register a series of BeanDefinitionParser(s) for the different xml elements supported by the Custom namespace(say annotation-driven, interceptors elements of mvc namespace).
  • BeanDefinitionParser - create the bean definition for the specific element - here is where a line like <mvc:annotation-driven/> will be expanded to the broader bean definitions with actual bean class names.
  • Registering the schema, NamespaceHandler - This is for Spring to find the schema for the custom namespaces and to find the NamespaceHandler that will handle the custom namespace. The registration of the schema is done by a file called META-INF/spring.schemas, this is neat way for Spring to find the schema in the classpath rather than downloading the schema over the web. The NamespaceHandler is further specified using a META-INF/spring.handlers file and contains the NamespaceHandler name that will handle the custom namespace, for eg. from the Spring documentation - 

Tying it together

This information of how the Custom namespace works internally can be put to good use to understand some of behavior of a few custom namepsace tags. Consider a tag to load up the properties file:
<context:property-placeholder location="classpath*:META-INF/spring/"/>
So to find how a property-placeholder element works internally, first find the spring.handlers file. Since property-placeholder is in the context namespace, the spring.handlers file will be present in the spring-context.jar file
and the file indicates that the NamespaceHandler is org.springframework.context.config.ContextNamespaceHandler

The ContextNameSpaceHandler registers a BeanDefinition parser called org.springframework.context.config.PropertyPlaceholderBeanDefinitionParser. This BeanDefinitionParser creates a bean definition with a class name of "PropertyPlaceholderConfigurer" and so essentially we could have replaced:
<context:property-placeholder location="classpath*:META-INF/spring/" local-override="true" properties-ref="localProperties"/>
with but losing conciseness in the process -
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 <property name="location" value="classpath*:META-INF/spring/"></property>
 <property name="localOverride" value="true"></property>
 <property name="properties">
  <ref bean="localProperties"/>

This however provides a good way to understand some of the nuances of how Spring handles things underlying a custom namespace.

No comments:

Post a Comment