如何在运行时基于没有XML的Spring属性注入不同的服务

2022-09-01 23:39:05

我正在使用Spring Boot for Java独立应用程序。我有一个使用服务的豆子。我想在运行时注入该服务的不同实现,基于Spring属性文件中的属性(就此而言为4)。


这听起来像工厂模式,但Spring还允许使用注释来解决问题,就像这样。

@Autowired @Qualifier("selectorProperty") private MyService myService;

然后在bean.xml文件中我有一个别名,这样我就可以在@Qualifier中使用该属性。

<alias name="${selector.property}" alias="selectorProperty" />

在我不同的实现中,我会有不同的限定符。

@Component("Selector1")
public class MyServiceImpl1

@Component("Selector2")
public class MyServiceImpl2

应用程序.属性

selector.property = Selector1

selector.property = Selector2

而关于工厂模式,在Spring中,您可以使用ServiceLocatorFactoryBean来创建一个可以为您提供相同功能的工厂。

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

然后,在你的bean中,你可以使用这样的东西在运行时获得正确的实现,这取决于属性的值。

@Value("${selector.property}") private String selectorProperty;

@Autowired private MyServiceFactory myServiceFactory;

private MyService myService;

@PostConstruct
public void postConstruct()
{
    this.myService = myServiceFactory.getMyService(selectorProperty);
}

但是这个解决方案的问题在于,我找不到一种方法来避免使用 XML 来定义工厂,并且我只想使用注释。


所以问题是,有没有办法只使用注释来使用ServiceLocatorFactoryBean(或等效的东西),或者如果我不想在XML中定义bean,我是否被迫使用@Autowired @Qualifier方式?或者有没有其他方法可以在运行时基于Spring 4避免XML的属性注入不同的服务?如果您的答案只是使用带有别名的,请给出一个原因,为什么这比使用众所周知的工厂模式更好。@Autowired @Qualifier

使用额外的XML迫使我在我的启动器类中使用,我也宁愿不使用。@ImportResource("classpath:beans.xml")

谢谢。


答案 1

实际上,您可以通过在配置文件中将其声明为bean来使用没有XML的ServiceLocatorFactory。

@Bean
public ServiceLocatorFactoryBean myFactoryServiceLocatorFactoryBean()
{
    ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
    bean.setServiceLocatorInterface(MyServiceFactory.class);
    return bean;
}

@Bean
public MyServiceFactory myServiceFactory()
{
    return (MyServiceFactory) myFactoryServiceLocatorFactoryBean().getObject();
}

然后,您仍然可以像往常一样使用工厂,但不涉及 XML。

@Value("${selector.property}") private String selectorProperty;

@Autowired @Qualifier("myServiceFactory") private MyServiceFactory myServiceFactory;

private MyService myService;

@PostConstruct
public void postConstruct()
{
    this.myService = myServiceFactory.getMyService(selectorProperty);
}

答案 2

我正在使用弹簧配置文件

例如,使用数据源 使用它,您可以根据需要定义任意数量的数据源

@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();
    }

}

在运行时,通过指定

-Dspring.profiles.active=“myProfile”

您激活了一个或另一个配置(所有这些配置都必须导入到您的主配置中,它们只是根据活动配置文件被忽略)。

这是一篇好文章:http://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/


推荐