Say I have the following structure with a service interface ServiceInterface
and a couple of components implementing it: ProductAService
and ProductBService
I also have a RequestContext
bean that has a qualifying property that says that we're say currently processing ProductA or ProductB. How can then automatically inject with autowiring or other annotation the correct implementation (ProductAService or ProductBService) into some service that needs it (ServiceThatNeedsServiceInterface
below).
public interface ServiceInterface {
void someMethod();
}
@Component(name="ProductAService")
public class ProductAService implements ServiceInterface {
@Override public void someMethod() {
System.out.println("Hello, A Service");
}
}
@Component(name="ProductBService")
public class ProductBService implements ServiceInterface {
@Override public void someMethod() {
System.out.println("Hello, B Service");
}
}
@Component
public class ServiceThatNeedsServiceInterface {
// What to do here???
@Autowired
ServiceInterface service;
public void useService() {
service.someMethod();
}
}
@Component
@Scope( value = WebApplicationContext.SCOPE_REQUEST )
public class RequestContext {
String getSomeQualifierProperty();
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With