Spring 3: Inject Default Bean Unless Another Bean Present

23,030

Solution 1

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

Solution 2

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

Solution 3

With Spring 3.0.7

<bean id="carDriver" class="Driver">
   <property name="car" value="#{ getBeanFactory().containsBean('customCar') ? getBeanFactory().getBean('customCar') : defaultCar }"/>
</bean>

Solution 4

I'm not sure but probably declaring custom bean with primary="true" might help you.

Solution 5

Use JavaConfig:

@Configuration
public class CarConfig {

  @Autowired(required=false) @Qualifier("custom")
  Car customCar;

  @Autowired @Qualifier("default")
  Car defaultCar;

  @Bean
  public Car car() {
    return customCar != null ? customCar : defaultCar;
  }
}  

and

<bean id="defaultCar" class="Car">
  <qualifier="default"/>
  <property name="name" value="Honda Accord"/>
</bean>

<!-- customCar defined somewhere else -->

<bean id="carDriver" class="Driver">
  <property name="car" ref="car"/>
</bean> 
Share:
23,030
SingleShot
Author by

SingleShot

Software Engineer. Wargamer.

Updated on July 13, 2022

Comments

  • SingleShot
    SingleShot almost 2 years

    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.