我想通过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>
我希望使用customCar
bean而不是defaultCar
bean。我最初的尝试不起作用,但我认为说明了我想要实现的目标:
<bean id="carDriver" class="Driver">
<property name="car" value="#{ @customCar eq null ? 'defaultCar' : 'customCar' }"/>
</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;
}
要配置我的示例汽车司机,您可以这样做:
<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,但这样可行。也许添加自定义架构元素可以使它更清洁。
赞赏其他评论。
答案 0 :(得分:8)
您可以使用@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();
}
答案 1 :(得分:7)
使用FactoryBean是最简单的解决方案 - 您可以描述您想要的任何算法。 更多信息,请访问
答案 2 :(得分:7)
使用Spring 3.0.7
<bean id="carDriver" class="Driver">
<property name="car" value="#{ getBeanFactory().containsBean('customCar') ? getBeanFactory().getBean('customCar') : defaultCar }"/>
</bean>
答案 3 :(得分:4)
使用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;
}
}
和
<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>
答案 4 :(得分:4)
我不确定,但可能会声明primary="true"
的自定义bean可能对您有帮助。
答案 5 :(得分:1)
使用最新的Spring版本,您可以使用基于SpEL的默认值定义:
@Required
@Value("#{new com.my.company.DefaultStrategy()}")
public void setStrategy(final MyStrategy strategy) {
this.strategy = strategy;
}
如果从Spring上下文设置此属性,则将注入您在上下文中定义的bean。否则,容器注入由@Value
注释指定的bean。
答案 6 :(得分:1)
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));
}
}