Please read my scenario bellow and tell me which approach is better in my case:
Scenario:
- There is an interface, for example ConfigurationManager.
- There are three totally different implementations of it. Each of them sits in separated EAR bounded with the necessary libraries. We have MemoryConfigurationManager, FileConfigurationManager and JpaConfigurationManager.
- There is an application (EAR). It needs to use one inmpementation of ConfigurationManager.
Maybe later, developers will create a 4th implementation of the interface and install it into application server and reconfigure the main App in order to the app uses the newest implementation of the interface. The main App always uses only one implementation of the interface and administrators can reconfigure which implementation is used.
Here I can not use simple CDI because implementations are in different EARs and the application is also in a separated ear so they have different class loaders. Simple @Inject
does not work between EARs.
So I have two ways:
- If I want to use CDI than I need to use
@Produces
method. It provides a way to inject objects that are not beans. - I can use Service Locator Pattern. This kind of lookup works fast because it has an own cache.
Of course, behind the CDI @Produces
method I can use the Service Locator Pattern as well. The service URLs for the different implementations come from an external config file. If somebody creates a new implementetion of ConfigurationManager and install it into the Java EE container then he needs to put the new service url into the config file.
So which approach is better? (1) or (2)? Or there is another clear way to implement this functionality? :-)
I prefer (2).
I think CDI + Producers bring more unnecessary classes.
I would think less about how to get the Service-Proxy, in that case both of your scenarios will work the same (as you already found out, you could even annotate your ServiceLocator with @Produces).
Think about how the class using the Service will get the proxy instance. Do you want to read the properties and locate the service in every constructor? Will you have a factory with one method per implementation? SPI-Service loading? How are you going to test the classes using the service?
In all that cases, dependency injection has a great advantage because it separates concerns and defines clear dependencies between client and implementation. My best practice: Whenever you have the choice, go for (C)DI. If you combine the ServiceLocator/PropertyReader approach with a @Produces annotation you woudn't even have more classes to write, but you gain much in return.