Spring: como alterar implementações de interface em tempo de execução

Como desenvolvedor Java, frequentemente preciso escolher entre diferentes implementações de minhas interfaces. Às vezes, essa escolha pode ser feitauma vez, enquanto outras vezes preciso de implementações diferentes em resposta às diferentes entradas que meu programa recebe. Em outras palavras, eu preciso ser capaz demudança implementação em tempo de execução. Isso é facilmente possível através de um objeto auxiliar que converte alguma chave (com base na entrada do usuário) em uma referência a uma implementação de interface adequada.

Com o Spring, posso projetar um objeto como um feijão e injetá-lo onde necessário:

public class MyClass {

    @Autowired
    private MyHelper helper;

    public void someMethod(String someKey) {
        AnInterface i = helper.giveMeTheRightImplementation(someKey);
        i.doYourjob();
    }

}

Agora, como devo implementar o auxiliar? Vamos começar com isso:

@Service
public class MyHelper {

    public AnInterface giveMeTheRightImplementation(String key) {
        if (key.equals("foo")) return new Foo();
        else if (key.equals("bar")) return new Bar();
        else ...
    }

}

Essa solução tem várias falhas. Um dos piores é o fato de que as instâncias retornadas do auxiliar são desconhecidas para o contêiner e, portanto, não podem se beneficiar da injeção de dependência. Em outras palavras, mesmo se eu definir oFoo classe como esta:

@Service
public class Foo {

    @Autowired
    private VeryCoolService coolService;

    ...

}

... instâncias deFoo retornado porMyHelper não terá ocoolService campo inicializado corretamente.

Para evitar isso, uma solução sugerida com freqüência é injetarcada implementação possível dentro do ajudante:

@Service
public class MyHelper {

    @Autowired
    private Foo foo;

    @Autowired
    private Bar bar;

    ...

    public AnInterface giveMeTheRightImplementation(String key) {
        if (key.equals("foo")) return foo;
        else if (key.equals("bar")) return bar;
        else ...
    }

}

Mas eu não sou um grande fã de tais soluções. Acho mais elegante e sustentável algo assim:

@Service
public class MyHelper {

    @Autowired
    private ApplicationContext app;

    public AnInterface giveMeTheRightImplementation(String key) {
        return (AnInterface) app.getBean(key);
    }

}

Isso é baseado no SpringApplicationContext.

Uma solução semelhante é usar oServiceLocatorFactoryBean classe:

public interface MyHelper {

    AnInterface giveMeTheRightImplementation(String key);

}

// Somewhere else, in Java config

@Bean
ServiceLocatorFactoryBean myHelper() {
    ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
    bean.setServiceLocatorInterface(MyHelper.class);
    return bean;
}

Mas como não sou especialista em Spring, me pergunto se existem abordagens ainda melhores.

questionAnswers(3)

yourAnswerToTheQuestion