I would like to configure Spring via XML such that if a particular bean exists, it will be injected into the target bean. If it does not exist, a different, default bean, will be injected.
For example if I have a file like this
<bean id="carDriver" class="Driver">
<property name="car" value="SOME EXPRESSION GOES HERE, SEE ATTEMPT BELOW"/>
</bean>
<bead id="defaultCar" class="Car">
<property name="name" value="Honda Accord"/>
</bean>
And load it, I would like the defaultCar
injected into the driver. However, if I also load the following file:
<bean id="customCar" class="FlyingCar">
<property name="name" value="Rocket Car"/>
<property name="maxAltitude" value="80000"/>
</bean>
I would want the customCar
bean to be used instead of the defaultCar
bean. My initial attempt does not work, but I think illustrates what I'm trying to achieve:
<bean id="carDriver" class="Driver">
<property name="car" value="#{ @customCar eq null ? 'defaultCar' : 'customCar' }"/>
</bean>
I know how to do this with a PropertyPlaceholderConfigurer
, but I don't want to have to provide a property file / VM property / environment variable / etc. in addition to the file that contains the custom bean. Thanks!
Update:
Based on the "use a factory bean" comments, I looked into this and came up with the following solution. First, I created a generic factory bean that allows you to specify a default bean name and an override bean name:
public class DefaultOverrideFactoryBean implements FactoryBean, BeanFactoryAware {
public Object getObject() throws Exception {
return beanFactory.containsBean(overrideBeanName) ?
beanFactory.getBean(overrideBeanName) :
beanFactory.getBean(defaultBeanName);
}
public Class<?> getObjectType() {
return Object.class;
}
public boolean isSingleton() {
return true;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public void setDefaultBeanName(String defaultBeanName) {
this.defaultBeanName = defaultBeanName;
}
public void setOverrideBeanName(String overrideBeanName) {
this.overrideBeanName = overrideBeanName;
}
private String defaultBeanName;
private String overrideBeanName;
private BeanFactory beanFactory;
}
To configure my example car driver, you would do this:
<bean id="carDriver" class="Driver">
<property name="car">
<bean class="DefaultOverrideFactoryBean">
<property name="defaultBeanName" value="defaultCar"/>
<property name="overrideBeanName" value="customCar"/>
</bean>
</property>
</bean>
I would have preferred to use SpEL, but this works. Perhaps adding a custom schema element woud make this cleaner.
Additional comments appreciated.
For a class annotated with @Component , specifying them is required for the autowired constructor but in a @Bean declaration you don't need to provide a parameter to specify the MyObject dependency to use (while it will work) if that is accessible in the current class, which is your case.
In Spring Boot, we can use Spring Framework to define our beans and their dependency injection. The @ComponentScan annotation is used to find beans and the corresponding injected with @Autowired annotation. If you followed the Spring Boot typical layout, no need to specify any arguments for @ComponentScan annotation.
Here, @Bean instantiates two beans with ids the same as the method names and registers them within the BeanFactory (Spring container) interface. Next, we can initialize the Spring container and request any of the beans from the Spring container. This strategy also makes it simple to achieve dependency injection.
You may used @Qualifier to choose one version of Car (custom or default), but you shall know the specific name of what you gonna use, and you may want to use just:
@Autowired
private Car car;
You may also use @Primary to solve this, but it just gives a preference to avoid ambiguity and it will be created the unwanted versions. So i would recomend to use the annotation
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
So you will only instantate one bean if another is not created. Its specially usefull when the beans are declared in differents modules.
//Core module creates a default Car
@Bean()
@ConditionalOnMissingBean(Car.class)
Car car()
{
return new DefaultCar();
}
and
//Car module creates the wanted prototype car
@Bean()
Car car()
{
return new Toyota();
}
Using FactoryBean is the simplest solution - you can describe any algorithm you want. More information is at
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/FactoryBean.html
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-extension-factorybean
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With