java - que - Cómo inyectar diferentes servicios en tiempo de ejecución basado en una propiedad con Spring sin XML
que hace autowired spring (2)
En realidad, puede usar ServiceLocatorFactory sin XML declarándolo como un bean en su archivo de configuración.
@Bean
public ServiceLocatorFactoryBean myFactoryServiceLocatorFactoryBean()
{
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(MyServiceFactory.class);
return bean;
}
@Bean
public MyServiceFactory myServiceFactory()
{
return (MyServiceFactory) myFactoryServiceLocatorFactoryBean().getObject();
}
Entonces aún puede usar la fábrica como siempre, pero no hay XML involucrado.
@Value("${selector.property}") private String selectorProperty;
@Autowired @Qualifier("myServiceFactory") private MyServiceFactory myServiceFactory;
private MyService myService;
@PostConstruct
public void postConstruct()
{
this.myService = myServiceFactory.getMyService(selectorProperty);
}
Estoy usando la aplicación independiente Spring Boot para Java. Tengo un bean que hace uso de un servicio. Quiero inyectar diferentes implementaciones de ese servicio en tiempo de ejecución, basadas en una propiedad en un archivo de propiedades con Spring (4 para el caso).
Esto suena como el patrón de Factory, pero Spring también permite usar anotaciones para resolver el problema, como este.
@Autowired @Qualifier("selectorProperty") private MyService myService;
Luego, en el archivo beans.xml tengo un alias, de modo que puedo usar la propiedad en @Qualifier.
<alias name="${selector.property}" alias="selectorProperty" />
Y en mis diferentes implementaciones tendría diferentes calificativos.
@Component("Selector1")
public class MyServiceImpl1
@Component("Selector2")
public class MyServiceImpl2
application.properties
selector.property = Selector1
selector.property = Selector2
Mientras que con respecto al patrón de fábrica, en Spring puede usar ServiceLocatorFactoryBean para crear una fábrica que le dé la misma funcionalidad.
<bean
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean"
id="myServiceFactory">
<property
name="serviceLocatorInterface"
value="my.company.MyServiceFactory">
</property>
</bean>
public interface MyServiceFactory
{
MyService getMyService(String selector);
}
Y luego, en su bean, puede usar algo como esto para obtener la implementación correcta en el tiempo de ejecución, dependiendo del valor de la propiedad.
@Value("${selector.property}") private String selectorProperty;
@Autowired private MyServiceFactory myServiceFactory;
private MyService myService;
@PostConstruct
public void postConstruct()
{
this.myService = myServiceFactory.getMyService(selectorProperty);
}
Pero el problema con esta solución es que no pude encontrar una manera de evitar el uso de XML para definir la fábrica, y me gustaría usar solo las anotaciones.
Entonces, la pregunta sería: ¿hay una manera de usar el ServiceLocatorFactoryBean (o algo equivalente) usando solo anotaciones, o me veo obligado a usar la forma @Autowired @Qualifier si no quiero definir beans en XML? ¿O hay alguna otra forma de inyectar diferentes servicios en tiempo de ejecución basados en una propiedad con Spring 4 evitando XML? Si su respuesta es simplemente usar @Autowired @Qualifier
con el alias, explique por qué es mejor que usar un patrón de fábrica bien conocido.
Usar el XML adicional me obliga a usar @ImportResource("classpath:beans.xml")
en mi clase Launcher, que prefiero no usar tampoco.
Gracias.
Estoy usando perfiles de primavera
Por ejemplo, con dataSources Usándolo puedes definir tantas DataSources, como quieras.
@Configuration
@Profile("dev")
public class StandaloneDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("cloud")
public class CloudDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
Y en tiempo de ejecución, especificando
-Dspring.profiles.active = "myProfile"
activa una u otra configuración (todas ellas deben importarse en su configuración principal, simplemente se ignoran según el perfil activo).
Aquí hay un buen artículo: http://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/