Spring Source were referring to your issue when they created the ServiceLocatorFactoryBean back in version 1.1.4. In order to use it you need to add an interface similar to the one below:
public interface ServiceLocator {
//ServiceInterface service name is the one
//set by @Component
public ServiceInterface lookup(String serviceName);
}
You need to add the following snippet to your applicationContext.xml
<bean id="serviceLocatorFactoryBean"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface"
value="org.haim.springframwork.stackoverflow.ServiceLocator" />
</bean>
Now your ServiceThatNeedsServiceInterface will look similar to the one below:
@Component
public class ServiceThatNeedsServiceInterface {
// What to do here???
// @Autowired
// ServiceInterface service;
/*
* ServiceLocator lookup returns the desired implementation
* (ProductAService or ProductBService)
*/
@Autowired
private ServiceLocator serviceLocatorFactoryBean;
//Let’s assume we got this from the web request
public RequestContext context;
public void useService() {
ServiceInterface service =
serviceLocatorFactoryBean.lookup(context.getQualifier());
service.someMethod();
}
}
ServiceLocatorFactoryBean will return the desired service based on the RequestContext qualifier.
Apart from spring annotations your code is not depended on Spring.
I executed the following unit test for the above
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:META-INF/spring/applicationContext.xml" })
public class ServiceThatNeedsServiceInterfaceTest {
@Autowired
ServiceThatNeedsServiceInterface serviceThatNeedsServiceInterface;
@Test
public void testUseService() {
//As we are not running from a web container
//so we set the context directly to the service
RequestContext context = new RequestContext();
context.setQualifier("ProductAService");
serviceThatNeedsServiceInterface.context = context;
serviceThatNeedsServiceInterface.useService();
context.setQualifier("ProductBService");
serviceThatNeedsServiceInterface.context = context;
serviceThatNeedsServiceInterface.useService();
}
}
The console will display
Hello, A Service
Hello, B Service
A word of warning. The API documentation states that
“Such service locators … will typically be used for prototype beans, i.e. for factory methods that are supposed to return a new instance for each call… For singleton beans, direct setter or constructor injection of the target bean is preferable.”
I cannot understand why this may cause an issue. In my code it returns the same service on two sequence calls to serviceThatNeedsServiceInterface.useService();
You can find the source code for my example in GitHub