Spring ServiceLocator or pure factory pattern?

11,402

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);
}
Share:
11,402

Related videos on Youtube

Kakawait
Author by

Kakawait

Backend devoloper at Dassault Systèmes

Updated on June 04, 2022

Comments

  • Kakawait
    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
    Kakawait over 10 years
    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?
  • Maksym Demidas
    Maksym Demidas over 10 years
    Yes, it is possible. I am actually use this approach in my current project. Go down to the typed collections example.
  • Kingz
    Kingz over 10 years
    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.
  • Kakawait
    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
    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
    Kakawait about 10 years
    @Maksym Demidas Thanks I will test that.
  • Kingz
    Kingz about 10 years
    @Kakawait - I added a code example to illustrate my point in the answer section.