MemeStorm

Exploring the Spring Framework and Application Development

Spring Components - XML configuration on steroids

Posted in All by Jon on the March 29th, 2006

The single most important feature in Spring 2.0 (IMO) is a new XML
configuration convenience feature. I believe this is going to
revolutionize the way people develop for Spring and bootstrap a modest
component market too. This is a lot more than a convenience features
- it’s a fledgling component model too.

In the ‘old’ Spring configuration files, almost everything was a bean. That is, you would write:

<bean id="foo" class="bar">
   <!-- zip zip -->
</bean>

In fact, if you look at the DTD you’ll see why:

<!ELEMENT beans (
	description?,
	(import | alias | bean)*
)>

In Spring 2 (I’m using M3) you can do something altogether different:

 <mycomponent:foobar att="zip"/>

Note that there is no class attribute - all the necessary information is captured behind the namespace. If you look at it from an XML configuration point of view, what this provides is a way of avoiding laboriously repetitive bean definitions by capturing beans in their own namespace. Spring comes with a number of these already defined:

 <jndi:lookup id="dataSource" jndiName="jdbc/MyDS"/>
 <tx:advice id="txAdvice">
   <tx:attributes>
     <tx:method name="insert*"/>
     <tx:method name="update*"/>
     <tx:method name="*" read-only="true"/>
   </tx:attributes>
 </tx:advice>

However, now think of the possibilities:

  • You can simplify configuration (as we’ve just seen)
  • You can hide implementation (all the ‘user’ needs to see is the namespace)
  • You can start distributing ‘Spring Components’ that folk can simply plug in by adding the JAR and referencing them from your application context!

I think it’s the last two points that are the most exciting. They’re perhaps not what the Spring developers intended, but they are the start of something new. You can now distribute a simple JAR that contains all your Spring beans that do something, say interface with Amazon, together with an XML schema describing those beans that you want to make public. Any user of this ‘Spring Component’ simply installs this JAR and starts typing:

<amazon:listBooks conditionSubject='memetics' associateId='memestorm'/>

You can even bung view tier stuff into beans, so the future is wide open.

How to do it

Let’s look at how you can roll your own component, resulting in a
simple JAR that you can distribute. See the attachment for a deployable
example: simple.jar. If this is in your application’s classpath, for example deployed in WEB-INF/lib then at the end of the day, all you need to do is wire it into your application context.

Wiring in the component

<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:memestorm=”http://www.memestorm.com/schema/simple”
    xsi:schemaLocation=”http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.memestorm.com/schema/simple
     http://www.memestorm.com/schema/simple/spring-simple.xsd”>

  <memestorm:action id=”s” value=”HelloWorld” />

  <bean id=”foo”>
    <!– .. –>
  </bean>
  <!– other definitions –>

</beans>

Notice that I’m using the XML Schema version of the bean configuration instead of DTD, and that I’ve defined my memestorm component as a namespace mapped to http://www.memestorm.com/schema/simple. We’ll later see how this name space gets mapped to a bean.

Coding the component

That’s it as far as the client goes. Just add the JAR and wire it in. Let’s look at the implementation. Instead of an Amazon component, we’ve written a rather simpler Simple component:

package com.memestorm.beandemo;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class SimpleBean {
   private String value;
   private String otherValue;

   Log logger = LogFactory.getLog(getClass());

   public SimpleBean () {
      logger.error("In the constructor of SimpleBean");
   }

   public void action() {
      logger.error("In action with value='" + value + "' and otherValue='" + otherValue + "'");
   }

   public String getValue() {
      return value;
   }

   public void setValue(String value) {
      this.value = value;
   }

   public String getOtherValue() {
      return otherValue;
   }

   public void setOtherValue(String otherValue) {
      this.otherValue = otherValue;
   }
}

As you can see, it has two properties (value, otherValue) and its constructor and action() method both log messages. (So to use the component, you need commons-logging in your app!)

Defining its XML interface

As you saw in the wiring, the XML interface is defined by an XML schema. So we need to define an XML schema that should be adhered to by clients of our component. Here is one we created, simple.xsd:

<xsd:schema xmlns="http://www.memestorm.com/schema/simple"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
         targetNamespace="http://www.memestorm.com/schema/simple"
         elementFormDefault="qualified"
         attributeFormDefault="unqualified">
   <xsd:element name="action">
      <xsd:complexType>
         <xsd:attribute name="id" type="xsd:ID" use="required"/>
         <xsd:attribute name="value" type="xsd:string"/>
      </xsd:complexType>
   </xsd:element>
</xsd:schema>

As you can see, it simply permits two attributes to an element called action. Look back to ‘Wiring in the component’ and you’ll see it all fits together.

The Namespace Handler

Behind the scenes, Spring invokes a namespace handler to create the actual bean instance from something like:

<memestorm:action id="s" value="HelloWorld" />

As a component developer, you have to supply this namespace handler. Here is our implementation:

package com.memestorm.tag;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.w3c.dom.Element;
import com.memestorm.beandemo.SimpleBean;

public class SimpleNamespaceHandler extends NamespaceHandlerSupport {

  public SimpleNamespaceHandler() {
    registerBeanDefinitionParser(”action”, new SimpleBeanDefinitionParser());
  }

  private static class SimpleBeanDefinitionParser extends
      AbstractSimpleBeanDefinitionParser {

    protected Class getBeanClass(Element element) {
      return SimpleBean.class;
    }

    protected void postProcess(BeanDefinitionBuilder definitionBuilder,
        Element element) {
      definitionBuilder.addPropertyValue(”otherValue”, “fromPostProcess”);
      definitionBuilder.setInitMethodName(”action”);
    }
  }
}

Note that the constructor registers a bean definition parser, associating it with an element. You’ll later see how we map the namespace to the element. The bean definition parser itself is pretty simple. getBeanClass() should return the appropriate class - as you can see we return SimpleBean.clas. We also have a postProcess() method that you can use to add to the parsed bean definition. In this case we set another property value and we set the init-method to be invoked. What’s happening behind the scenes here, in AbstractSimpleBeanDefinitionParser, is that the XML snippet is parsed and all of its attributes (and values) are mapped to setters of properties on the bean instance (except for id). In other words, its programmatically creating the bean definition and its properties according to the values defined in the application context.

Packaging

To package everything, simply include all the XSD files, beans, and namespace handlers into a JAR with two special files in the META-INF directory. First you need spring.schemas:

http://www.memestorm.com/schema/simple/spring-simple.xsd=com/memestorm/tag/simp
le.xsd

As you can see, this maps the namespace location to the physical location of the schema in the JAR. Next, you need spring.handlers:

http://www.memestorm.com/schema/simple=com.memestorm.tag.SimpleNamespaceHandler

As you can see, this maps the schema namespace to the tag handler. That’s it.

Summary

In summary then:

  • Create the XML schema defining your component
  • Create your component bean/beans
  • Create a namespace handler that maps the schema to the beans
  • Create two packaging artifacts to tie it all together

Download

Here’s a deployable JAR with source (of course, you need to deploy to a Spring 2 M3 application, with commons-logging available too).

simple.jar


Print This Post Print This Post

Convention over Configuration in Spring’s MVC

Posted in All by Jon on the March 17th, 2006

Convention Over Configuration (CoC) is a term often bandied
around by Ruby on Rails followers. From the wikipedia it’s
defined as “the programmer only needs to specifically configure what
is unconventional.” Very sensible advice indeed, although it’s not without its problems. We’ve already
seen one example
of CoC in Spring: the use of
InternalResourceViewResolver effectively lets programmers
forget about configuring resource views for incoming URLs, and instead
rely on the convention of mapping to a like-named view (it extracts
the view name from the URL). In fact, the MultiActionController is a type of CoC too - you don’t have to configure an action name; Spring tries to automatically invoke a like-named method on the controller class based on the URL. (The MethodNameResolver actually.) These two essentially enforce the convention of automatically selecting a view, and a method in a controller class respectively.

Now let’s look at a third example of CoC: ControllerClassNameHandlerMapping. This enforces the convention of automatically selecting a controller.

Background

As shown in Simple URL Mapping…, a URL Mapper has to map incoming URL requests to particular controllers. Typically we have to configure the URL mapper with each new controller that we add. Turning this on its head, we want to use CoC so that the convention is that the correct controller is automatically selected if it’s registered, without any configuration.

How to do it

Simply use the ControllerClassNameHandlerMapping as your URL mapper in your dispatch-servlet.xml configuration file. For example:

<bean id="urlMapping"
  class="org.springframework.web.servlet.mvc.ControllerClassNameHandlerMapping">
</bean>

Define your controllers as usual. For example, I have this:

<bean id="commandController"
   class="com.memestorm.web.CommandController">
</bean>

That’s it. You don’t have to wire your controllers to URLs, this is now automated. Now instead of explicitly configuring mappings between URLs and controllers, you can rely on the convention, which is to take the ClassUtils.getShortName() name of the controller class, remove the “Controller” suffix if it exists and the cap, and use the result as a mapping. For example our CommandController would be mapped to /command/.

For MultiActionController controllers (see our recent example) it works slightly differently. In this case, say for our DispatchController example, it will map /dispatch/* to the controller. (Note the extra wildcard).

Now you can concentrate on writing your controllers and action methods. Everything will be automatically wired in; nice.

How it works

It’s actually dead simple. The ControllerClassNameHandlerMapping class simply iterates through the application context looking for beans of type Controller, adding them to the mapping as described earlier.

Acknowledgements

Thanks to Matt for highlighting this class.


Print This Post Print This Post