August 6, 2008

Select Spring Bean Implementations at Runtime

Finally I made myself familiar with Spring. I used the chance to integrate it into a new project I'm implementing at work. Although the basic idea of dependency injection is straigt forward, there are some concepts under the surface, which have to be discovered.

It seems to me, that the common spring application is something like a web app: the wiring between all the beans is done before staging and packaging the application into a war. Hence it is quite easy to write some spring bean definitions, give every bean a nice name, wire them properly together and that's that.

As a matter of fact I had to deal with a slightly different application paradigm. The user chooses at "startup time", which particular service realization he wants to use. Consider the following persistent service. There might be a realization that simply maintains the model in memory, on the other hand there could be realization that stores those model objects via hibernate into a database. Let's cut to the chase and give some handy code examples.
// The Service
public interface MyService {

 void persist(Model model);
 
}

// Both realizations

public class InMemoryService implements MyService {

 @Override
 public void persist(Model model) {
  // store in memory...
 }
}

public class HibernateService implements MyService {

 @Override
 public void persist(Model model) {
  // store to hibernate
 }

}

// And finally the client using the service
public class Client {

 private MyService myService;
 
 public void setMyService(MyService myService) {
  this.myService = myService;
 }
 
 public void doSomeClientBusiness(){
  // whatever
 }
}
In the general spring paradigm you would write a bean definition containing all three beans and assign one of the two service realizations to client. Somehow like this
<bean name="inMemoryService" class="foo.InMemoryService"/>

<bean name="hibernateService" class="foo.HibernateService" />

<bean name="client" class="foo.Client">
 <!--  choose the actual service -->
 <property name="myService" ref="hibernateService"/>
</bean>
But that's not possible in our scenario, because
  1. The client needs to decide which of both services to take when he starts the application
  2. The client may not be faced beans configs. That's not in the client's scope and would give him a too high degree of freedom.
So what can we do? We have to delay the instantiation of the service to the startup time depending on the user choice. So we need to detail what we mean by "user's choice". To keep it simple, let's just say the user could specify a value in a properties file like
app.persist.service=IN_MEMORY
Now we need a mechanism that decides how to pick a particular realization depending on the parameter. Has anyone said factory? Yes, fine. Problem solved. The more ellaborated problem is to integrate this into the spring world. We need some bean we could define which finally yields our service realization. But spring comes with two mechanaism to the rescue.

First there is FactoryBean. If the spring container is faced with an implementation of a FactoryBean it does not instantiate the FactoryBean, but its product. With this we get something as follows. To distinguish which service to pick we define an enum:

public enum ServiceType {
 IN_MEMORY, HIBERNATE
}
Well, this is a type safe way to maintain the instantiation, but surely this violates OCP. But let's just say that would be another discussion and ignore any consequences for now. Anyways, our factory looks like:
public class MyServiceBean implements FactoryBean {

 private ServiceType serviceType;
 
 public void setServiceType(ServiceType serviceType) {
  this.serviceType = serviceType;
 }
 
 @Override
 public Object getObject() throws Exception {
  switch(serviceType) {
  case IN_MEMORY:
   return new InMemoryService();
  case HIBERNATE:
   return new HibernateService();
  default:
   throw new IllegalStateException();
  }
 }

 @Override
 public Class getObjectType() {
  return MyService.class;
 }

 @Override
 public boolean isSingleton() {
  return false;
 }
}
Nearly finished. The last thing is putting the runtime choosen parameter into the spring config. But how do we do that? Spring comes with a fine thing called PropertyPlaceholderConfigurer. This allows putting property placeholders into the bean definition, which are substituted by the respective value during the ApplicationContext's refresh. Now our bean definition looks like
<bean name="inMemoryService" class="foo.InMemoryService"/>

<bean name="hibernateService" class="foo.HibernateService" />

<bean name="client" class="foo.Client">
 <!--  choose the actual service -->
 <property name="myService" ref="${app.persist.service}"/>
</bean>

And finally we need something like following ApplicationContext
String [] configs = {"my-services.xml"};
  ConfigurableApplicationContext appContext = 
   new ClassPathXmlApplicationContext(configs);
  
  PreferencesPlaceholderConfigurer placeholderConfigurer = 
   new PreferencesPlaceholderConfigurer();
  Properties props = new Properties();
  props.put("app.persist.service", "IN_MEMORY");
  placeholderConfigurer.setProperties(props);
  appContext.addBeanFactoryPostProcessor(placeholderConfigurer);
  appContext.refresh();
That's all.