15

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
  • 3,929
  • 6
  • 33
  • 60
  • 1
    None of the answers provided answers the original question which is: what is the added value of a service locator – Rhubarb Apr 06 '20 at 16:01

2 Answers2

11

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.

Maksym Demidas
  • 7,707
  • 1
  • 29
  • 36
  • Hum 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? – Kakawait Sep 06 '13 at 11:50
  • 1
    Yes, it is possible. I am actually use this approach in my current project. [Go down to the typed collections example](http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-autowired-annotation). – Maksym Demidas Sep 06 '13 at 12:07
  • A 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. – Kingz Dec 20 '13 at 16:59
  • @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? – Kakawait Jan 28 '14 at 19:02
  • @Kakawait, you can autowire a Map 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](http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-autowired-annotation) – Maksym Demidas Jan 29 '14 at 09:56
  • @Kakawait - I added a code example to illustrate my point in the answer section. – Kingz Jan 29 '14 at 19:23
10

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);
}
Kingz
  • 5,086
  • 3
  • 36
  • 25