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,但这很有效。 也许添加自定义架构元素可以使它更清晰。
其他评论赞赏。
使用FactoryBean是最简单的解决方案 – 您可以描述您想要的任何算法。 更多信息请访问
您可以使用@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)); } }