Unsprung! moving away from Spring DI


Advantages of Spring (when used for dependency injection):

- separates configuration from the code. Thus the wiring of the application can be modified without recompiling, in theory. Not sure how often this happens in practice.

... and the cons:

-   Xml config files are easy to get wrong (no compile time checks obviously)
- Annotations spread through out the code are not terribly visible
- Error messages thrown by the framework can be cryptic at times

There is a simpler alternative. Do away with the Spring container and inject the dependencies manually. All that is needed is a class to inject the dependency, a context which creates the appropriate dependency, and a bit of application code to wire the two together.


A DbReader uses a constructor-injected datasource to retrieve database results.
class DbReader{

  DataSource dataSource;

   DbReader (DataSource dataSource){
     this.dataSource= dataSource;
   }

   public  Object fetch(){
    //use the data source to execute a database query
   }

A Context  holds a reference to a data source (mock or real)
class Context {

  DataSource dataSource;

  static Context liveContext(){
     //get production db data source
     DataSource dataSource = ...;
     return new Context(dataSource);
  }

  static Context mockContext(){
     // get in memory test db data source (or mock)
     DataSource dataSource = ...; 
     return new Context(dataSource);
  }

  Context(DataSource dataSource){
      this.dataSource = dataSource;
  }

  DataSource getDataSource(){
     return dataSource;
  }

}


Project classes inject the datasource associated with a live context in the DbReader constructor.

   public static void main(String args[]){
      Context ctx = Context.liveContext();
      //fetch data from a prod database
      Object result = new DbReader(ctx.getDataSource()).fetch();
   }


while integration tests inject a mock datasource from a mock context.:

   @Test
   public void Test(){
     Context ctx = Context.mockContext();
     //fetch data from  a mock or in-memory db
     Object result = new DbReader(ctx.getDataSource()).fetch();
     //assert that result is as expected...
    }


Simple, easy to understand (and to debug), compile-time checks in place, no messing around with xml configuration files (or annotations).

...and it's even simpler with the Unsprung project which can help generate the Context class above from a Spring configuration file.

No comments:

Post a Comment