Spring 3:注入默认豆,除非存在另一个豆子

2022-09-02 19:50:50

我想通过XML配置Spring,这样如果存在特定的bean,它将被注入到目标bean中。如果它不存在,将注入一个不同的默认 Bean。

例如,如果我有一个这样的文件

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

并加载它,我希望注入到驱动程序中。但是,如果我也加载以下文件:defaultCar

<bean id="customCar" class="FlyingCar">
  <property name="name" value="Rocket Car"/>
  <property name="maxAltitude" value="80000"/>
</bean>

我希望使用豆子而不是豆子。我最初的尝试没有奏效,但我认为这说明了我试图实现的目标:customCardefaultCar

<bean id="carDriver" class="Driver">
  <property name="car" value="#{ @customCar eq null ? 'defaultCar' : 'customCar' }"/>
</bean>

我知道如何使用 ,但除了包含自定义 Bean 的文件之外,我不想提供属性文件/ VM属性/环境变量/等。谢谢!PropertyPlaceholderConfigurer


更新:

根据“使用工厂豆”的评论,我对此进行了研究并提出了以下解决方案。首先,我创建了一个通用的工厂Bean,它允许您指定默认的Bean名称和覆盖Bean名称:

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

要配置我的示例汽车驱动程序,您将执行以下操作:

<bean id="carDriver" class="Driver">
  <property name="car">
    <bean class="DefaultOverrideFactoryBean">
      <property name="defaultBeanName" value="defaultCar"/>
      <property name="overrideBeanName" value="customCar"/>
    </bean>
  </property>
</bean>

我本来更喜欢使用SpEL,但这有效。也许添加自定义架构元素会使这更干净。

其他评论意见表示赞赏。


答案 1

您可以使用@Qualifier来选择一个版本的汽车(自定义或默认),但您应该知道您将要使用的内容的具体名称,并且您可能只想使用:

 @Autowired
 private Car car;

您也可以使用@Primary来解决此问题,但它只是给出了避免歧义的首选项,并且它将创建不需要的版本。所以我建议使用注释

org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean

因此,如果未创建一个 Bean,则只会实例化另一个 Bean。当bean在不同的模块中声明时,它特别有用。

//Core module creates a default Car
@Bean()
@ConditionalOnMissingBean(Car.class)
Car car()
{
  return new DefaultCar();
}

//Car module creates the wanted prototype car
@Bean()
Car car()
{
  return new Toyota();
}

推荐