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.

About these ads
  1. I tried Guice a few years back and used it in several projects. Then I tried Spring and haven’t really looked back. Guice is great and I feel like it pushed Spring to innovate as Spring was starting suffer from the same stodgy, monolithic vision of which it accused J2EE. But the Spring Source suite of tools (not just the IoC container) do so much well that that it is difficult not to want to use them. And if you like the Guice way of doing things: Spring JavaConfig.

  2. Is why I became motivated to design a companion DSL for Java that would be specialized to just this purpose. It has better syntax than the XML config files of Spring but can work standalone or in conjunction to Spring.

    SFig – alternative metadata config language for Spring-Framework

    SFig™ – alternative metadata config language for Spring-Framework

    Hmm, perhaps a JSR to standardize a companion bean factory config language for Java…

    The DSL I devised addresses some of the short-comings of Java language itself. And these config files are processed at runtime. By using file system notification, it could be detected when they’re modified and reloaded on the fly.

    • David
    • January 11th, 2010

    I believe that what you are describing is dependency injection. A more liberal definition than may be familiar but if dependency injection is the injection of dependencies then, for example, your ETLTableEndpoint certainly does ‘depend’ on the tableName do make sense. The only difference between that and any bean is that tableName is a primitive. Guice (and pico/nanocontainer) have options for wiring such things as well (by name). In fact, I use guice for that because it ends up with less baggage in that case.

    I’ve found that the most intense distinction, which has been analyzed to death on the interwebs, is the “extensions” to Spring. I call them extensions because they are superfluous from where I sit but they make a *lot* of people happy. Things like transation handling, AOP, and so on.

    There is an important point to be made about ‘config’ parameters vs ‘dependencies’ (in the not-so-liberal sense)… typically the most convenient way to specify them isn’t the same as the way to specify services. It can be (and is) done but it can be inconvenient. I’ve put some thought into what general-purpose DI would look like if it was focused on being easy and I keep coming back to “its the language”. That’s why I was interested to hear about google’s Noop language before it went silent (about a week later).

    Of course, spending my time fretting over the ‘most convenient’ way is a luxury that only google devs have time for…

    • J Yu
    • January 11th, 2010

    Dude. Writing code causes bugs, writing XML does not? Why can’t you guys look at all your bean XMLS and realize that it is the SAME Java code written with angle brackets. And if you are using a decent IDE, it assists you just like Java code, type checking, auto complete, refactoring and all that – because it’s a thin wrapper around java anyway. I know, it is so hard to new objects and set their properties – but why would you do it in XML? Unless in your organization you can excuse yourself from doing that part since it’s not java, it’s not your job.

  3. David-

    You raise a good point. I can see one saying that an instance of a class depends on a primitive value used to define it. But, correct me if I am wrong here, my understanding is that with Guice, if you were to create a dependency with a primitive it applies to all instances of the class whose dependencies are being defined (i.e. one cannot create define two instances of the same class with different dependencies). Is that incorrect? Or I recall it not being straightforward (if not strictly impossible). Not finding anything on this as a quickly rescan the Guice docs.

    You make an astute point about ‘it’s the language’. I have often thought about how one might integrate some of the more useful DI concepts into the language itself. I think a lot could be learned from LISP / Scheme in this area, as that is a language that does a very good job with the “programs as data” concept due to its declarative list syntax. I also came across Noop at one point and found some of its design goals interesting.

    Thanks for your thoughtful comments.

    -will

  4. J-

    Of course you can create bugs in XML. I tried to imply as much in my post. My point here was that the sorts of bugs you can create in the XML config layer are more limited in scope and complexity (especially by the subset of those who use XML to define configurations who are not programmers).

    It’s not hard to ‘new’ objects and set their properties. The value of an extrinsic configuration layer (in XML or any other language) is not that it makes it easier to create objects and set their properties. I would be glad to walk you through some of the reasoning behind such frameworks if you like, but would first want to understand more clearly your objections to their existence and use. Is it that you object to the concept of code configuration in general or just Spring’s approach? If the latter, is it simply that XML is the language being used?

  5. Vaguely related – noop isn’t dead, but it is silent – mostly to allow the Go team to release in peace, but also because we all got crazy busy over the holidays. We’re probably looking at end of 2010 for a really useable version, though I’m hoping for earlier alphas. Just fyi.

  1. January 11th, 2010

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: