Spring: cómo cambiar las implementaciones de interfaz en tiempo de ejecución

Como desarrollador de Java, con frecuencia necesito elegir entre diferentes implementaciones de mis interfaces. Algunas veces esta elección se puede haceruna vez, mientras que otras veces necesito diferentes implementaciones en respuesta a las diferentes entradas que recibe mi programa. En otras palabras, necesito podercambio implementación en tiempo de ejecución. Esto se puede lograr fácilmente a través de un objeto auxiliar que convierte alguna clave (basada en la entrada del usuario) en una referencia a una implementación de interfaz adecuada.

Con Spring puedo diseñar un objeto como un bean e inyectarlo donde lo necesite:

public class MyClass {

    @Autowired
    private MyHelper helper;

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

}

Ahora, ¿cómo debo implementar el ayudante? Comencemos con esto:

@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 ...
    }

}

Tal solución tiene varios defectos. Uno de los peores es el hecho de que las instancias devueltas del asistente son desconocidas para el contenedor y, por lo tanto, no pueden beneficiarse de la inyección de dependencia. En otras palabras, incluso si defino elFoo clase como esta:

@Service
public class Foo {

    @Autowired
    private VeryCoolService coolService;

    ...

}

...instancias deFoo devuelto porMyHelper no tendrá elcoolService campo debidamente inicializado.

Para evitar esto, una solución alternativa sugerida con frecuencia es inyectarcada posible implementación dentro del ayudante:

@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 ...
    }

}

Pero no soy un gran admirador de tales soluciones. Encuentro algo más elegante y fácil de mantener como este:

@Service
public class MyHelper {

    @Autowired
    private ApplicationContext app;

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

}

Esto se basa en Spring'sApplicationContext.

Una solución similar es usar elServiceLocatorFactoryBean clase:

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;
}

Pero como no soy un experto en Spring, me pregunto si hay enfoques aún mejores.

Respuestas a la pregunta(3)

Su respuesta a la pregunta