Как внедрять различные службы во время выполнения на основе свойства с помощью Spring без XML

Я использую автономное приложение Spring Boot для Java. У меня есть bean, который использует сервис. Я хочу внедрить различные реализации этой службы во время выполнения, основываясь на свойстве в файле свойств с Spring (4, если на то пошло).


Это похоже на шаблон Factory, но Spring также позволяет использовать аннотации для решения проблемы, например.

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

Затем в файле beans.xml у меня есть псевдоним, поэтому я могу использовать свойство в @Qualifier.

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

И в моих разных реализациях у меня были бы разные квалификаторы.

@Component("Selector1")
public class MyServiceImpl1

@Component("Selector2")
public class MyServiceImpl2

application.properties

selector.property = Selector1

selector.property = Selector2

В то время как в отношении шаблона Factory в Spring вы можете использовать ServiceLocatorFactoryBean для создания Factory, который предоставит вам ту же функциональность.

<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 для определения factory, и я хотел бы использовать только аннотации.


Итак, вопрос в том, есть ли способ использовать ServiceLocatorFactoryBean (или что-то подобное), используя только аннотации, или я вынужден использовать способ @Autowired @Qualifier, если я не хочу определять beans в XML? Или есть ли другой способ внедрить разные службы во время выполнения на основе свойства с Spring 4, избегая XML? Если ваш ответ просто использует @Autowired @Qualifier с псевдонимом, пожалуйста, укажите причину, почему это лучше, чем использование известного шаблона Factory.

Использование дополнительного XML заставляет меня использовать @ImportResource("classpath:beans.xml") в моем классе Launcher, который я бы тоже не использовал.

Спасибо.

Ответ 1

Собственно, вы можете использовать ServiceLocatorFactory без XML, объявив его как bean в вашем файле конфигурации.

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

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

Затем вы все равно можете использовать factory, как обычно, но 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

Я использую Spring профили

Например, с помощью dataSources Используя его, вы можете определить столько источников данных, сколько хотите

@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/