ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

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:

  • SimpleDAO: transports info objects to and from the database
  • DbUtil: convenience routines for working with JDBC ResultSets, Connections, and such
  • ObjectRegistry: singleton registry, by which objects find one another
  • SetupDataSourceContextListener: sets up JDBC DataSource
  • SetupDBContextListener: prepares the (embedded) database
  • GetDataServlet: page controller to display data
  • PutDataServlet: page controller to store data

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.

Pages: 1, 2, 3

Next Pagearrow