Question

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 :)

Was it helpful?

Solution

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.

OTHER TIPS

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);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top