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

Sin*_*hot 15 java xml spring spring-el

我想通过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>
Run Code Online (Sandbox Code Playgroud)

加载它,我想defaultCar注入驱动程序.但是,如果我还加载以下文件:

<bean id="customCar" class="FlyingCar">
  <property name="name" value="Rocket Car"/>
  <property name="maxAltitude" value="80000"/>
</bean>
Run Code Online (Sandbox Code Playgroud)

我希望使用customCarbean而不是defaultCarbean.我最初的尝试不起作用,但我认为说明了我想要实现的目标:

<bean id="carDriver" class="Driver">
  <property name="car" value="#{ @customCar eq null ? 'defaultCar' : 'customCar' }"/>
</bean>
Run Code Online (Sandbox Code Playgroud)

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


更新:

基于"使用工厂bean"评论,我调查了这个并提出了以下解决方案.首先,我创建了一个通用工厂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;
}
Run Code Online (Sandbox Code Playgroud)

要配置我的示例汽车驱动程序,您可以这样做:

<bean id="carDriver" class="Driver">
  <property name="car">
    <bean class="DefaultOverrideFactoryBean">
      <property name="defaultBeanName" value="defaultCar"/>
      <property name="overrideBeanName" value="customCar"/>
    </bean>
  </property>
</bean>
Run Code Online (Sandbox Code Playgroud)

我宁愿使用SpEL,但这很有效.也许添加一个自定义架构元素可以使它更清洁.

其他评论赞赏.

Eli*_*iuX 10

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

 @Autowired
 private Car car;
Run Code Online (Sandbox Code Playgroud)

您也可以使用@Primary来解决这个问题,但它只是偏好避免歧义,并且会创建不需要的版本.所以我建议使用注释

org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
Run Code Online (Sandbox Code Playgroud)

所以如果没有创建另一个bean,你只会立即使用一个bean.当bean在不同的模块中声明时,它特别有用.

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

//Car module creates the wanted prototype car
@Bean()
Car car()
{
  return new Toyota();
}
Run Code Online (Sandbox Code Playgroud)

  • @ConditionalOnMissingBean 仅在使用 Spring Boot 时有效。 (3认同)

Eug*_*kov 7

使用FactoryBean是最简单的解决方案 - 您可以描述您想要的任何算法.更多信息请访问

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

  • 您可以添加一个简单的示例 (2认同)

vdr*_*vdr 7

随着Spring 3.0.7

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


Sla*_*hin 5

我不确定,但可能声明自定义 beanprimary="true"可能对您有所帮助。