2011-03-29 127 views
12

我想通過XML配置Spring,如果某個特定的bean存在,它將被注入到目標bean中。如果它不存在,則會注入一個不同的默認bean。Spring 3:注入默認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> 

我希望被用來代替defaultCar豆的豆customCar。我最初的嘗試不起作用,但我想說明什麼,我想要實現:

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

我知道如何與一個PropertyPlaceholderConfigurer做到這一點,但我不希望有提供屬性文件/ 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,但這是有效的。也許增加一個自定義的模式元素會使這個更清潔。

其他意見讚賞。

+1

爲什麼不創建FactoryCar bean。然後在您的carDriver中引用該工廠。 – chris 2011-03-29 00:24:26

回答

5

使用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> 
1

有了你可以使用最新的春季版規劃環境地政司爲基礎的定義ÿ我們的默認值:

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

如果您設置Spring上下文,您在上下文中定義的bean這個屬性將被注入。否則,容器注入由@Value註釋指定的bean。

3

我不確定,但可能聲明自定義bean與primary="true"可能會幫助你。

+0

主要=真是這種用例的方式。 – Rolf 2015-02-04 09:11:27

7

With Spring 3.0。7

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

對我來說,我需要做以下的SpEL: #{getBeanFactory()。containsBean('customCar')? customCar:defaultCar} – Blaine 2014-05-28 16:21:29

6

您可以使用@Qualifier選擇租車(自定義或默認)的一個版本,但你要知道你要使用特定的名稱,您可能希望只使用:

@Autowired 
private Car car; 

您也可以使用@Primary來解決這個問題,但它只是提供一個避免含糊不清的偏好,它會創建不需要的版本。 所以我會建議使用註釋

org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean 

所以,如果另一個未創建你只會instantate一個豆。當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

@ConditionalOnMissingBean僅在使用Spring Boot時纔有效。 – Xdg 2016-11-17 18:21:07

0

彈簧引導啓動1.4.0.RELEASE(彈簧核心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)); 
    } 

}