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

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

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

      

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

     

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

    

我知道如何使用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; } 

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

         

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

其他评论赞赏。

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

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

随着Spring 3.0.7

    

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

         

我不确定,但可能声明使用primary="true"自定义bean可能会对你有所帮助。

使用最新的Spring版本,您可以使用基于SpEL的默认值定义:

 @Required @Value("#{new com.my.company.DefaultStrategy()}") public void setStrategy(final MyStrategy strategy) { this.strategy = strategy; } 

如果从Spring上下文设置此属性,则将注入您在上下文中定义的bean。 否则,容器注入由@Value注释指定的bean。

spring-boot-starter 1.4.0.RELEASE(spring-core 4.3.2.RELEASE)
或者你可以这样做:

 public interface SomeService { } ------------------------------------------------------------------------ public interface CustomSomeService extends SomeService { } ------------------------------------------------------------------------ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.stereotype.Service; @Service @ConditionalOnMissingBean(CustomSomeService.class) public class DefaultSomeService implements SomeService { } ------------------------------------------------------------------------ import org.springframework.stereotype.Service; @Service public class AdvancedSomeService implements CustomSomeService { } ------------------------------------------------------------------------ class Application{ @Autowired private SomeService someService; /* Now if ApplicationContext contains CustomSomeService implementation 'someService' use custom implementation. If CustomSomeService is missing 'someService' contains DefaultSomeService implementation. */ } ------------------------------------------------------------------------ import static org.junit.Assert.assertTrue; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @ContextConfiguration(classes = { DefaultSomeService.class, AdvancedSomeService.class }) public class SomeServiceTest { @Autowired private SomeService someService; @Test public void test() { assertTrue(AdvancedSomeService.class.isInstance(someService)); } } ------------------------------------------------------------------------ import static org.junit.Assert.assertTrue; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @ContextConfiguration(classes = { DefaultSomeService.class}) public class SomeServiceTest { @Autowired private SomeService someService; @Test public void test() { assertTrue(DefaultSomeService.class.isInstance(someService)); } }