2011-03-29 11 views
12

Me gustaría configurar Spring a través de XML, de manera que si existe un bean en particular, se inyectará en el bean objetivo. Si no existe, se inyectará un bean diferente, predeterminado.Spring 3: Inyectar beans predeterminados a menos que haya otro Bean Presente

Por ejemplo si tengo un archivo así

<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> 

y cargarlo, me gustaría que el defaultCar inyecta en el conductor. Sin embargo, si también carga el siguiente archivo:

<bean id="customCar" class="FlyingCar"> 
    <property name="name" value="Rocket Car"/> 
    <property name="maxAltitude" value="80000"/> 
</bean> 

Me gustaría que el frijol customCar a ser utilizado en lugar del grano defaultCar. Mi primer intento no funciona, pero creo que ilustra lo que estoy tratando de lograr:

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

Yo sé cómo hacer esto con un PropertyPlaceholderConfigurer, pero no quiero tener que proporcionar un archivo de propiedades/Propiedad de VM/variable de entorno/etc. además del archivo que contiene el bean personalizado. ¡Gracias!


Actualización:

Sobre la base de la "utilización de un grano de fábrica de" comentarios, miré en esto y se le ocurrió la siguiente solución. En primer lugar, he creado un grano de fábrica genérico que le permite especificar un nombre de frijol por defecto y un nombre de frijol de anulación:

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

Para configurar mi ejemplo conductor del coche, usted podría hacer esto:

<bean id="carDriver" class="Driver"> 
    <property name="car"> 
    <bean class="DefaultOverrideFactoryBean"> 
     <property name="defaultBeanName" value="defaultCar"/> 
     <property name="overrideBeanName" value="customCar"/> 
    </bean> 
    </property> 
</bean> 

I Hubiera preferido usar SpEL, pero esto funciona. Quizás agregar un elemento de esquema personalizado sea más limpio.

Comentarios adicionales apreciados.

+1

¿Por qué no crea beans FactoryCar? Luego haga referencia a esa fábrica dentro de su carDriver. – chris

Respuesta

5

Uso JavaConfig: basado en SpEL

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

y

<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

Con la versión de primavera más reciente se puede usar la definición de y nuestro valor predeterminado:

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

Si establece esta propiedad desde el contexto de Spring, se inyectará el bean que definió en el contexto. De lo contrario, el contenedor inyecta el bean especificado por la anotación @Value.

3

No estoy seguro, pero probablemente declarar bean personalizado con primary="true" podría ayudarlo.

+0

Primario = cierto es EL camino a seguir para este uso. – Rolf

7

Con muelle 3.0.7

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

Para mí, necesitaba hacer lo siguiente SpEL: # {getBeanFactory(). ContainsBean ('customCar')? customCar: defaultCar} ' – Blaine

6

Es posible que se usa @Qualifier para elegir una versión de coche (personalizado o por defecto), pero se sabrá el nombre específico de lo que va a utilizar, y es posible que desee utilizar simplemente:

@Autowired 
private Car car; 

También puede usar @Primary para resolver esto, pero solo da preferencia para evitar la ambigüedad y se crearán las versiones no deseadas. Así lo recomiendo utilizar la anotación

org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean 

por lo que sólo un grano instantate si otro no se crea. Es especialmente útil cuando los granos se declaran en diferentes módulos.

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

y

//Car module creates the wanted prototype car 
@Bean() 
Car car() 
{ 
    return new Toyota(); 
} 
+1

@ConditionalOnMissingBean funcionará solo cuando se use Spring Boot. – Xdg

0

1.4.0.RELEASE resorte de arranque-motor de arranque (4.3.2.RELEASE primavera-core)
o se puede hacer como esto:

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

} 
Cuestiones relacionadas