ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Migrating to Spring

by Q Ethan McCallum
12/13/2006

Call me late to the party. The Spring framework party, that is. For you fellow latecomers, Spring is a library of infrastructure code released under the Apache 2.0 license. At the core is an inversion-of-control container, around which the development team has created templates for boilerplate JDBC and JMS code, a web MVC framework, and more.

I say I'm late to the party because, in spite of Spring's maturity and publicity, it took me a while to try it out. My question was, "what can Spring do for me?" To find out, I replaced the guts of an existing reference app with Spring components. What I learned is that I should have started using Spring a long time ago; the app's code is now less cluttered than before, it should be easier to debug and extend, and it's lighter because I was able to ditch some custom helper code in favor of Spring counterparts.

I'll share my thoughts and findings from that experiment in this article. Specifically, I'll explain how I replaced the reference app's singleton registry, JDBC code, and web front-end layer with Spring components. I'll also describe one roadblock I hit and how I resolved it.

You needn't be a Spring expert to follow along, but I provide links to Spring resources later on.

The sample code has been tested under Spring 1.2.x, using Sun's JDK 1.5.0_07 for Linux.

The Original (Old) Code

I didn't want to experiment on a live, production application, so I pulled my test app from another article I had written. It's a simple Java web application with two servlet page controllers as entry points. These servlets talk to a database through a data access object (DAO), which fetches database connections from a local DataSource. Related objects call a singleton registry to find one another. Specifically:

This is a very simple webapp, but it is self-contained and exhibits behaviors of a larger, N-tier application. Some observations of this microscale experiment are hence applicable to real-world conversion projects.

Changing the Innards: The Object Registry

The first class under the microscope is ObjectRegistry, which is a layer of glue between related objects:

package pool_test.util ;

public class ObjectRegistry {

  private static ObjectRegistry _instance =
    new ObjectRegistry() ;

  public static ObjectRegistry getInstance(){
    return( _instance ) ;
  }


  private Map _singletons ;

  public void put(
    final String key , final Object obj
  ){
    _singletons.put( key , obj ) ;
  }
  
  public Object get( final String key ){
    return( _singletons.get( key ) ) ;
  }

}

ObjectRegistry is really a glorified map of String:Object pairs. You can store an object in the registry in one location (put()), and then fetch that same object from another location (get()). Using a registry weakens object dependencies because code that fetches an object only knows its general type (interface or superclass) and lookup key. The specifics--implementation, instantiation, configuration--are left to the code that calls put() to store the object.

This works, and I've seen it work on a much larger scale, but it's not perfect. A missing or misplaced put() could yield a null-pointer error or stack overflow. You also have to track the order in which objects are stored in the registry, to make sure you don't try to fetch an object that doesn't already exist. In small apps you use ContextListeners--as I have done here--to handle instantiation order, but a larger app would require extra diligence to avoid problems.

Another problem with the old singleton registry is that the explicit put() operations are Java calls. That means any change to the implementation of a stored object--say you want to swap your database-backed DAO for a testing stub--requires a recompile. One mistaken checkin and my live, production app uses the DAO stub. Again, in a larger app, that could be tough to track down since it's buried in code.

A dash of Spring addresses these shortcomings. This is the new registry:

package pool_test.util ;

import org.springframework....ApplicationContext ;
import org.springframework.
   ...ClasspathXMLApplicationContext ;

public class ObjectRegistry {

  private ApplicationContext _singletons ;
  
  private ObjectRegistry(){

    _singletons =
      new ClassPathXmlApplicationContext(
        new String[] { "spring-objectregistry.xml" }
      );
        
  }

  public Object get( final String key ){
    return( _singletons.getBean( key ) ) ;
  }

}

Notice, I have replaced the old Map with a Spring ApplicationContext. Similar to a Map, ApplicationContext stores objects and lets you fetch them by name. By comparison, ApplicationContext reads all of the object definitions from an XML file, which externalizes the configuration. Changing a definition in spring-objectregistry.xml requires an application restart, but not a full recompile.

Consider this excerpt from spring-objectregistry.xml:

<bean
  id="ObjectPool"
  class="org.apache...GenericObjectPool"
  singleton="true"
>
  <-- omitted for brevity -->
</bean>

<bean
  id="DataSource"
  class="org.apache...PoolingDataSource"
  singleton="true"
>
  <property name="pool">
    <ref local="ObjectPool" />
  </property>
</bean>

<bean
  id="DAO"
  class="pool_test.data.jdbc.SimpleDAO"
  singleton="true"
>
  <property name="dataSource">
    <ref local="DataSource"/>
  </property>             
</bean>

The XML elements correspond to Reflection calls: an outer <bean/> element defines an object, and inner <property/> elements call mutator methods on that object. For example, for the bean with the id DAO, Spring first instantiates an object of type SimpleDAO and calls setDataSource() on it. The argument for setDataSource() is the bean DataSource, which is defined earlier in the file.

Behind the scenes, Spring configures the DataSource and assigns it to the DAO. Other Spring-managed objects reference the DAO only by its bean name ("DAO") and as such, they are unaware of changes in the implementation thereof (here, "SimpleDAO").

Now that Spring manages the objects, the ObjectRegistry is read-only to client code. I can remove the put() method from the ObjectRegistry class, and also any explicit put() calls in other classes. For example, SetupDataSourceContextListener now just populates the pool with its initial connections.

There's now some dead weight in the web.xml deployment descriptor, too. For example, some context params pointed to local properties files for JDBC and such. Spring now assembles the objects that used those properties files, and assigns those values itself.

Spring also takes care of dependency tracking among the objects in spring-objectregistry.xml. I was handling this myself before, in code. Now, as I use more dependency injection in this app, Spring will make sure that such referenced objects are created in the proper order and before client code tries to use them. That means I've offloaded some bookkeeping work to Spring in addition to cleaning up my code.

One could argue that a "pure" IoC approach would obviate the need for an explicit, callable ObjectRegistry and let Spring manage object relationships at runtime. Consider that a future refactoring. It will cause a little trouble later on, but I still need the registry for now.

Changing the Data Tier: Spring JDBC

Configuring the custom DataSource was a matter of fancy XML work. Spring also provides base DAO classes to eliminate boilerplate JDBC code. That means the Spring framework takes care of connection management and closing ResultSets and PreparedStatements. What remains is my app-specific code.

The new DAO has the same interface as its predecessor:

package pool_test.data.jdbc ;

public class SimpleDAO {
  public void setupTable() ;
  public void putData() ;
  public Collection getData() ;
  public void destroyTable() ;
}

Under the hood, though, it's a different animal. Whereas the old version of the DAO had a lot of inline JDBC code, the new version offloads that grunt work to Spring:

package pool_test.data.jdbc ;

public class SimpleDAO extends JdbcDaoSupport {

  private GetDataWorker _getDataWorker ;
  private PutDataWorker _putDataWorker ;
  private CreateTableWorker _createTableWorker ;
  private DestroyTableWorker _destroyTableWorker ;

  // constructor is now empty

  protected void initDao() throws Exception {
    
    super.initDao() ;
    
    _getDataWorker =
      new GetDataWorker( getDataSource() ) ;

    _putDataWorker =
      new PutDataWorker( getDataSource() ) ;

    _createTableWorker =
      new CreateTableWorker( getDataSource() ) ;

    _destroyTableWorker =
      new DestroyTableWorker( getDataSource() ) ;

    return ;
    
  } // initDao()

  public void setupTable() {
    _createTableWorker.update() ;
  }

  public Collection getData() {
    return( _getDataWorker.execute() ) ;
  }

  // ... destroyTable() and getData()
  //   follow similar conventions ...

}

The first change is the parent class: SimpleDAO now extends Spring's JdbcDaoSupport, which has several methods and inner classes for database work. The first such method is setDataSource(), which assigns a JDBC DataSource to this object. Subclasses call getDataSource() to fetch this object.

initDao() is the next method inherited from JdbcDaoSupport. The parent class invokes this method to give its subclasses a chance to run any one-time initialization code. Here, SimpleDAO assigns values to its member variables.

The member variables are new, too: moving to Spring JDBC means moving SimpleDAO's functionality--fetching and storing data--to special inner classes such as GetDataWorker and PutDataWorker. There is one such inner class for each DAO action. For example, storing data is offloaded to PutDataWorker:

package pool_test.data.jdbc ;

import org.springframework ... SqlUpdate ;

public class SimpleDAO {

 ...
   private class PutDataWorker extends SqlUpdate {
    
     public PutDataWorker( final DataSource ds ){
      
       super( ds , SQL_PUT_DATA ) ;
    
       declareParameter(
             new SqlParameter( Types.VARCHAR ) ) ;
       declareParameter(
             new SqlParameter( Types.INTEGER ) ) ;

     }

     // a real app would load the SQL statements
     //   from an external source...
     private static final String SQL_PUT_DATA =
       "INSERT INTO info VALUES( ? , ? )" ;

   }

   ...

}

PutDataWorker extends SqlUpdate, a Spring template class that handles the grunt work of SQL INSERT and UPDATE calls. The declareParameter() calls tell Spring what data types the SQL statement will use--respectively a string and a number.

Notice, PutDataWorker is a very lean class. It calls super() to pass a DataSource and SQL statement to its parent class and declareParameter() to describe that query. SqlUpdate handles the real work of interacting with JDBC-related objects and closing connections. In turn, SimpleDAO.putData() is equally trim:

public class SimpleDAO {

  public void putData() {

    for( ... ){

      // ... "nameParam" and "numberParam" are
      // local loop variables ...

      Object[] params = {
        nameParam , // variable is a Java String 
        numberParam // some Java numeric type
      } ;

      _putDataWorker.update( params ) ;

    }
  }

}

putData() populates the database with some nonsense data. Notice that this method delegates to its worker class. Specifically, to an inherited method on its worker class. SqlUpdate.update() takes care of fetching the data, and closing the JDBC Connection and any relevant Statement objects. That means I can pitch a lot of my custom JDBC code, and even an entire class: the old DbUtil had convenience methods for closing Connections, Statements, and ResultSets.

What SqlUpdate does for update calls, Spring's MappingSqlQuery does for queries. Note the method mapRow() in GetDataWorker:

package pool_test.data.jdbc ;

import org.springframework ... MappingSqlQuery ;


// inside class SimpleDAO ...

private class GetDataWorker
  extends MappingSqlQuery {

  // ...constructor similar to PutDataWorker...
  
  protected Object mapRow( final ResultSet rs ,
       final int rowNum ) throws SQLException
  {
      
      final SimpleDTO result = new SimpleDTO(
        rs.getString( "names" ) ,
        rs.getInt( "numbers" )
      ) ;

      return( result ) ;
      
   } // mapRow()

}

This method takes care of translating the tabular ResultSet data into workable SimpleDTO objects, one row at a time. Spring calls this method for each row in the result set. While GetDataWorker.mapRow() interacts with the ResultSet, it isn't otherwise responsible for close()ing it or checking whether there are any rows left to process.

Changing the Web Tier: SpringMVC

Now that I have Spring-ified the infrastructure and data code, it's time to tackle the web tier. The central class here is Spring MVC's Controller interface:

package org.springframework ...

interface Controller {

  public ModelAndView handleRequest(
    HttpServletRequest request ,
    HttpServletResponse response
  ) throws Exception ;
        
}

The entry point, handleRequest(), has a signature similar to a Struts Action class or even a homegrown page controller: the framework provides the servlet request and response objects, and the controller implementation returns the result of some business logic (the model) and an indicator of how to display it (the view). The model of ModelAndView is a Map of business objects or other data that will be manipulated at the view tier. In the sample code, the view is a JSP, but Spring MVC also supports Velocity templates and XSLT.

The sample app's servlet page controllers are so simple that the power of Spring MVC doesn't shine through. Hence, the updated page controllers are just shy of most textbook examples.

The old page controllers used a similar strategy of passing a Map of objects from the servlets to the JSPs. As a result, I don't have to make any changes to the JSP for this to work. Nor do I have to use any Spring-specific tag libraries.

There's still work to do in the new, Spring-based controllers, though: they invoke the ObjectRegistry to look up the DAO. In a pure Spring world, the DAO would be assigned to the controllers in the web-specific XML config file, here WEB-INF/SpringMVC-servlet.xml.

That sounds easy enough, right? I'll just add the object registry's Spring config file--spring-objectregistry.xml--to web.xml like so:

<context-param>
  <param-name>
    contextConfigLocation
  </param-name>
  <param-value>
    classpath*:spring-objectregistry.xml
  </param-value>
<context-param>

Now the objects specified in spring-objectregistry.xml will be visible to those in WEB-INF/SpringMVC-servlet.xml.

Doing that creates another problem: the MVC layer loads the files spring-objectregistry.xml and SpringMVC-servlet.xml into one ApplicationContext. The object registry loads spring-objectregistry.xml into a different ApplicationContext. These two ApplicationContexts live in their own world and cannot, by default, see each other's bean definitions.

In turn, the objects defined in spring-objectregistry.xml will be loaded twice: once by the MVC layer, then by the object registry. In turn, then, my "singletons" in spring-objectregistry.xml are not really singletons any more. In the sample code these are all stateless objects, but in a larger app some singletons may indeed hold state. If I don't load those objects once and only once, I will have synchronization problems. That, and if such an object were to perform some one-time, resource-intensive operations, my app's performance would suffer.

My first reaction was to refactor the code such that ObjectRegistry wasn't needed. That's a simple undertaking, but this was a learning exercise. To simulate a larger project--one in which removing the registry would not be such a one-off task--I decided to stick with it and figure out how make the two worlds work.

In short, I needed some way to expose the objects of ObjectRegistry (spring-objectregistry.xml) to those used at the web tier (WEB-INF/SpringMVC-servlet.xml). Spring's solution is the BeanFactoryLocator, which is a registry of ApplicationContexts. I can tell both the object registry and the MVC layer to load the common objects from BeanFactoryLocator.

First, I have to change ObjectRegistry, such that it doesn't explicitly load spring-objectregistry.xml:

import org.springframework....BeanFactoryLocator ;
import org.springframework.
    ...ContextSingletonBeanFactoryLocator ;
import org.springframework.
    ...BeanFactoryReference ;

// this was an ApplicationContext before
private final BeanFactoryReference _singletons ;

private ObjectRegistry(){

  // ContextSingletonBeanFactoryLocator loads
  //   contents of beanRefContext.xml

  BeanFactoryLocator bfl =
    ContextSingletonBeanFactoryLocator
      .getInstance() ;

  BeanFactoryReference bf =
    bfl.useBeanFactory( "OBJ_REGISTRY_DEFS" );
                         
  _singletons = bf ;

}

public Object get( final String key ){
  return( _singletons.getFactory().getBean( key ) ) ;
}

This code replaces the ApplicationContext with a BeanFactoryReference, named OBJ_REGISTRY_DEFS, that it pulls from a BeanFactoryLocator. In turn, OBJ_REGISTRY_DEFS is defined in a file called beanRefContext.xml:

<beans>

  <bean
    id="OBJ_REGISTRY_DEFS"
    class="...ClassPathXmlApplicationContext"
  >

    <constructor-arg>
      <list>
        <value>spring-objectregistry.xml</value>
      </list>
    </constructor-arg>

  </bean>

</beans>

The bean named OBJ_REGISTRY_DEFS is really an ApplicationContext backed by the original object registry config file, spring-objectregistry.xml. The calls to getBean() on the BeanFactoryReference just pass through to the underlying ApplicationContext.

That takes care of the ObjectRegistry itself. To make the web tier use OBJ_REGISTRY_DEFS as well--that is, to make the objects defined therein visible to the web-tier Spring config, SpringMVC-config.xml--takes some extra entries in web.xml:

  <context-param>
     <param-name>
       parentContextKey
     </param-name>
     <param-value>
       OBJ_REGISTRY_DEFS
     </param-value>
  </context-param>

  <context-param>

     <param-name>
       locatorFactorySelector
     </param-name>

     <param-value>
       classpath*:beanRefContext.xml
     </param-value>
  </context-param>

The first entry tells the web-tier Spring configuration that it should call on the BeanFactoryReference named OBJ_REGISTRY_DEFS for any objects it cannot find. The second tells the framework to load any file named beanRefContext.xml from the classpath.

Now the objects defined in spring-objectregistry.xml are visible to the web-tier objects in SpringMVC-config.xml. This means I can slowly phase out ObjectRegistry, instead of trying to make such a far-reaching change in one step.

Ugly? Yes. Glue code? Yes. A means to migrate your app to Spring, when you already have your own singleton registry? Absolutely. Now the app is shielded from future refactorings: removal of the ObjectRegistry (and its explicitly-loaded ApplicationContext) should only affect client code of ObjectRegistry.

A point of warning, however: even the Spring docs note that BeanFactoryLocator isn't for everyday use. It should be used for migration projects such as this. If you intend to use Spring for a new app, by comparison, your design should account for proper IoC injection from the start.

The Round-up

Comparing the original app to its successor, I first notice the difference in size; there's less of my code, which should simplify debugging. Furthermore, letting Spring handle so much of the object instantiation and dependency tracking means I have one less worry as the system grows. Injection, via IoC, should also simplify my tests--I can swap out one DAO implementation for another by changing an XML file.

Taking a higher-level view, I pondered the specific Spring components I used. The object lookup and JDBC templates looked familiar. In a sizable project, it's common to have framework code to handle object lookups or database connectivity. That's what makes Spring so useful--it feels natural. We've seen it before, it's just that now we don't have to (re)write it as we move from project to project. As an added bonus, the Spring crew can focus on enhancing their product and we worry about our own.

Another benefit to adopting Spring is that it's not an all-or-nothing endeavor. I was able to apply Spring to each application tier without affecting the others, so I could have stopped at any point along the way. If I had only wanted to leverage Spring's JDBC templates, for example, I could have changed my DAOs and left the rest of my app untouched.

While I'm late to the Spring party, I'm certainly pleased to be in attendance.

Resources

Q Ethan McCallum grew from curious child to curious adult, turning his passion for technology into a career.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.