Spring ServiceLocator or pure factory pattern?
Solution 1
As an option you can use list injection:
public class SomeService {
@Autowired
private List<Parser> parsers;
public doSomethingWithParser(...) {
...
Parser parser = getParser(JsonParser.class);
parser.parse(...);
...
}
private Parser getParser(Class<Parser> targetClass) {
Parser result = null;
for(Parser parser : parsers) {
if(parser.getClass().equals(targetClass)){
result = parser;
}
}
return transformer;
}
}
Even better, you can add Parser.isCompatibleWith(SomeInput input)
method to simplify paser detection code.
Solution 2
Below is an example of a service locator class. This returns a service for the given ID from the service registry. The registry is a MAP which is autowired as shown. This is a working example from a production system:
@Service
public class MyServiceLocator {
@Autowired
private Map<String, MyService> myServiceRegistry;
/**
* Service locator to find the right Domain service to interact with the requested data store
*
* @param serviceID
* @return
*/
public MyService locateServiceFor(String serviceID) {
//Using the Given string 'serviceID' as key, lookup the service from the Registry
return myServiceRegistry.get(serviceID);
}
Related videos on Youtube
Comments
-
Kakawait almost 2 years
99% of my dependency is manage with DI pattern via @Autowired Spring annotation.
Nevertheless in a particular scenario, I can't determine which implementation to be used until run-time.
The most known case, is the multiple implementation of parsers.
The first solution is to used multiple @Autowired (ugly mode)
Interface Parser { <T> T parse(); } @Component("JsonParser") class JsonParser implements Parser { ... } @Component("XmlParser") class XmlParser implements Parser { ... } class MyService { @Autowired @Qualifier("XmlParser") Parser xmlParser; @Autowired @Qualifier("JsonParser") Parser jsonParser; ... }
But if I have a large number of implementation that can be not acceptable.
The second solution is to used ServiceLocator from Spring
interface ParserServiceLocatorFactory { public Parser getParser(String parserName); } interface Parser { <T> T parse(); } @Component("JsonParser") class JsonParser implements Parser { ... } @Component("XmlParser") class XmlParser implements Parser { ... } class MyService { @Autowired ServiceFactory parserServiceLocatorFactory; void exampleMethod() { Parser xmlParser = parserServiceLocatorFactory.getParser("XmlParser"); } }
This way to do seems right to me but compared with the third solution?
The third solution is to used pure factory pattern and inject it.
@Component public ParserFactory { Parser getParser(String parserName) { ... } } interface Parser { <T> T parse(); } @Component("JsonParser") class JsonParser implements Parser { ... } @Component("XmlParser") class XmlParser implements Parser { ... } class MyService { @Autowired ParserFactory parserFactory void exampleMethod() { Parser xmlParser = parserFactory.getParser("XmlParser"); } }
If you have pro/con for the previous solutions, or even better solution for my problem?
PS: it's pseudo code I may miss some small things :)
-
Kakawait over 10 yearsHum it's possible to @Autowired a list? I know it's possible using XML but I never heard about annotation. Do you have some doc links?
-
Maksym Demidas over 10 yearsYes, it is possible. I am actually use this approach in my current project. Go down to the typed collections example.
-
Kingz over 10 yearsA slight modification to the approach suggested above will be to use a MAP collection of parser types. This way, you can do the lookup in constant time, using the map key which is the name of the type. It will avoid the FOR loop every time you want to lookup a parser service.
-
Kakawait about 10 years@Kingz I can't test now, but I don't think it's possible to Autowired a map with annotation. You must write XML definition of your map? Or I'm wrong?
-
Maksym Demidas about 10 years@Kakawait, you can autowire a Map<String, Bean> with annotation. The Map values will contain all beans of the expected type, and the keys will contain the corresponding bean names. Go down to map example in the official documentation
-
Kakawait about 10 years@Maksym Demidas Thanks I will test that.
-
Kingz about 10 years@Kakawait - I added a code example to illustrate my point in the answer section.