Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject different services at runtime based on a property with Spring without XML

Tags:

I am using Spring Boot for Java standalone application. I have a bean which makes use of a service. I want to inject different implementations of that service at runtime, based on a property in a properties file with Spring (4 for that matter).


This sounds like the Factory pattern, but Spring also allows using annotations to solve the problem, like this.

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

Then in the beans.xml file I have an alias, so that I can use the property in the @Qualifier.

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

And in my different implementations I would have different qualifiers.

@Component("Selector1") public class MyServiceImpl1  @Component("Selector2") public class MyServiceImpl2 

application.properties

selector.property = Selector1  selector.property = Selector2 

Whereas regarding the factory pattern, in Spring you can use ServiceLocatorFactoryBean to create a factory that would give you the same functionality.

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

And then in your bean you can use something like this to get the right implementation at runtime depending on the value of the property.

@Value("${selector.property}") private String selectorProperty;  @Autowired private MyServiceFactory myServiceFactory;  private MyService myService;  @PostConstruct public void postConstruct() {     this.myService = myServiceFactory.getMyService(selectorProperty); } 

But the problem with this solution is that I could not find a way to avoid using XML to define the factory, and I would like to use only annotations.


So the question would be, is there a way to use the ServiceLocatorFactoryBean (or something equivalent) using only annotations, or am I forced to use the @Autowired @Qualifier way if I do not want to define beans in XML? Or is there any other way to inject different services at runtime based on a property with Spring 4 avoiding XML? If your answer is just use the @Autowired @Qualifier with the alias, please give a reason why that is better than using a well known factory pattern.

Using the extra XML is forcing me to use @ImportResource("classpath:beans.xml") in my Launcher class, which I'd rather not use either.

Thanks.

like image 721
Pedro Lopez Avatar asked Oct 20 '14 10:10

Pedro Lopez


People also ask

Can I change application properties at runtime Spring boot?

To change properties in a file during runtime, we should place that file somewhere outside the jar. Then we tell Spring where it is with the command-line parameter –spring. config. location=file://{path to file}.

How do you inject Spring boot properties?

Injecting Properties Using @ValueUsing the @Value annotation, we can inject the values from the application. properties file into class fields in the Spring-managed bean GreetController . Using @Value allows you to set a default value if the requested one, for any reason, isn't available: @Value("${message.


1 Answers

Actually, you can use ServiceLocatorFactory without XML by declaring it as a bean in your configuration file.

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

Then you can still use the factory as usual, but no XML is involved.

@Value("${selector.property}") private String selectorProperty;  @Autowired @Qualifier("myServiceFactory") private MyServiceFactory myServiceFactory;  private MyService myService;  @PostConstruct public void postConstruct() {     this.myService = myServiceFactory.getMyService(selectorProperty); } 
like image 69
Pedro Lopez Avatar answered Sep 18 '22 01:09

Pedro Lopez