Archive for January, 2010

Why I wish Spring IoC was not marketed as a DI framework


I recently came across Guice, a framework that is widely considered an alternative / competing Dependancy Injection (DI) framework to Spring’s IoC container. After reading the documentation (which was very good), and playing around a little, I started to read the numerous articles blog posts comparing the two, as I was personally very surprised that there was even to be a comparison to be made.

Most comparisons I read centered around the use XML vs. annotations (although Spring does allow for the latter as well), and other distinctions that entirely miss the real observation that should be made. While Spring’s IoC container certainly does do dependency injection, it does so as a side-effect of doing something much more general, that no other DI framework I have seen does well: creating and configuring instances of Java objects. Sound like a boring and uninteresting statement? Well, this is a very subtle and powerful point whose ramifications are not obvious, but really should be understood in order to get the most out of Spring’s IoC container and change the way you program (for the better). Let me elaborate.

Most DI frameworks focus on saving you from having to write a lot of object factories to bind the concrete implementation classes to the interfaces exposed to and used by the rest of your codebase (i.e. bind the <code>PaypalBillingService</code> class to all places in the codebase where the <code>BillingService</code> interface is used). Spring’s IoC container does this too, and for many, this is all they use it for. But the real power of Spring’s IoC container is in its ability weave together complex graphs of Java objects and configure them with values.

Consider a simple example of a class meant to represent a JDBC configuration, and a goal of creating JDBC configurations for your development, qa, and production databases.

// I will use the non-compilable shorthand 'property TYPE NAME' to represent Java Bean properties and save me from
// writing getters and setters in this example

public class JdbcConfiguration {
    property String driverClassName;
    property String jdbcUrl;
    property String username;
    property String password;
}

I can then define beans for each configuration:

<bean id="myDevJdbcConfig" class="com.acme.JdbcConfiguration">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
   <property name="jdbcUrl" value="jdbc:mysql://devserver:3306/mydb"/>
   <property name="username" value="admin"/>
   <property name="password" value="admin"/>
</bean>

<bean id="myQAJdbcConfig" class="com.acme.JdbcConfiguration">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
   <property name="jdbcUrl" value="jdbc:mysql://qaserver:3306/mydb"/>
   <property name="username" value="admin"/>
   <property name="password" value="admin"/>
</bean>

<bean id="myProdJdbcConfig" class="com.acme.JdbcConfiguration">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
   <property name="jdbcUrl" value="jdbc:mysql://prodserver:3306/mydb"/>
   <property name="username" value="admin"/>
   <property name="password" value="admin"/>
</bean>

What’s interesting in this simple example is that I used Spring more to configure instances of a single Java class than to provide Dependency Injection in the way that other frameworks like Guice are primarily used.

But this example was somewhat trivial, so let’s kick it up a notch. At my last company, Merced Systems, our professional services team was able to implement incredibly complex customizations of our core platform for our customers using only configuration (no code) using an IoC container I wrote in 2001 very similar to Spring’s (enough like Spring’s that I will use Spring to illustrate).

Let’s say we have a simple ETL (Extraction, Translation, and Loading) framework for moving data from a source database to a target. You could use Spring to completely define an entire ETL process by linking together a set of Java Bean instances:

(I will omit class definitions as they will be obvious from the structure of the bean definitions)

<bean id="myETLConversion" class="com.acme.ETLConversion">
   <property name="source" ref="source">
   <property name="target" ref="target">
   <property name="mapping" ref="mapping">
   <property name="startTime" value="12:00am EST">
   <property name="frequency" value="DAILY">
   <property name="adminEmailForErrorAlerts" value="admin@fooco.com">
</bean>

<bean id="source" class="com.acme.ETLTableEndpoint">
   <property name="tableName" value="PERSON">
   <property name="jdbcConfig" ref="mySourceJdbcConfig">
</bean>

<bean id="target" class="com.acme.ETLTableEndpoint">
   <property name="tableName" value="PERSON">
   <property name="jdbcConfig" ref="myTargetJdbcConfig">
</bean>

<bean id="mapping" class="com.acme.ETLMaping">
   <property name="columnMappings">
      <list>
         <bean class="com.acme.ColumnMapping">
            <property name="sourceColumn" value="PERSON_ID"/>
            <property name="targetColumn" value="ID"/>
         </bean>
         <bean class="com.acme.ColumnMapping">
            <property name="sourceColumn" value="FIRST_NAME"/>
            <property name="targetColumn" value="FN"/>
         </bean>
         <bean class="com.acme.ColumnMapping">
            <property name="sourceColumn" value="LAST_NAME"/>
            <property name="targetColumn" value="LN"/>
         </bean>
      </list>
   </property>
</bean>

At Merced we used this technique to allow our customer services team to customize almost every single aspect of our product. In addiction to the (simplified) ETL example above, we used it for:

  • Defining Report table layouts and Chart configurations (like Bar Charts vs. Line Charts, font colors and sizes, etc…)
  • Defining the content and layout of Dashboards
  • Defining customizations of our DB schema (which we would then plugin to our ORM framework)
  • Customizations of our URL structure
  • Customizations of left-nav elements for different User roles
  • Access control rules
  • Internationalization and localization
  • More, I just cant even remember…

And… very importantly, for customizing aspects of the product the engineering team had never even anticipated. Because we had a development discipline of exposing almost every object in our codebase as a configurable Java Bean, our professional services group and customers were able to a accommodate numerous unanticipated customization requests without the need to change our codebase.  Did this mean a typical deployment of our system had hundreds of XML bean definitions? Yes. Could the configurations get very complex? Yes. Was it scary? No. It allowed us to deliver a highly customizable enterprise software product with ONE codebase, and we never had to support and rationalize Java code written by professional services, customers or outside integration shops. All customization was done via configuration and it was beautiful.

The point is, using Spring’s IoC framework to inject class dependencies as a substitution for class factories is just the beginning. It’s real power is in creating object graphs of components to drive the functionality and behavior of your system in ways that most think require code. Sure, it can result in a enormous amount of XML, and yes, the Java compiler can catch a lot more typos than Spring’s bean XML parser, but the less Java code you have, the better, because writing code causes bugs (even when you have a compiler keeping you type-safe).

I have come to realize that Spring’s IoC framework is often compared to DI frameworks like Guice and other’s because of it’s name. The term “Inversion of Control” is pretty much used interchangeably with “Dependency Injection”, and hence the comparisons. And so, I think Spring’s IoC framework suffers because of it’s name. It’s name does not do its power justice and results in naive comparisons to other frameworks. Maybe it should be called a Bean Configuration Framework, or Component Configuration Framework… not sure… but I wish it was not simply marketed, and therefore perceived, as a means for doing Dependency Injection just so that I might use a <code>PaypalBillingService</code> as my programs’s implementation of my <code>BillingService</code> interface. Its much more than that.