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
- The client needs to decide which of both services to take when he starts the application
- 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.
app.persist.service=IN_MEMORYNow 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.