21. května 2010

Auto-wiring a možná řešení při více beanech stejného typu

Pokud používáme auto-wiring (dále předpokládám auto-wiring podle typu), tak se nám lehce může stát, že máme více beanů (instancí) stejného typu. V tomto případě Spring vyhodí výjimku, protože nemá žádný návod, jak tuto situaci vyřešit.

Zejména v testech se mi toto stává velice často, protože mám bean produkčního kódu, ale pro testy chci použít implementaci určenou pouze pro testy.

Příklad:

context.xml
<bean id="b1" class="FooImpl" />

context-test.xml
<bean id="b2" class="FooTestImpl" />

@ContextConfiguration(locations = {"classpath:/context.xml", "classpath:/context-test.xml"})
public void FooTest() {
@Autowired
private Foo foo;
}


Mám několik možností, jak tento problém řešit:

  • nadefinuji druhý (testovací) bean se stejným ID jako produkční bean a jelikož se testovací bean inicializuje jako druhý (z tohoto důvodu je velice důležité pořadí inicializace souborů Spring konfigurace), tak se přepíše (override) a ve Spring kontejneru bude ve výsledku pouze jeden bean - v tomto příkladu FooTest. Stejně to bude fungovat i když u obou beanů vynechám atribut ID. Pak se ID generují automaticky (viz implementace BeanNameGenerator) a budou tedy také stejná.

  • použiji anotaci @Qualifier a poradím Springu, jakou beanu má přesně vybrat. Ovšem ještě musím upravit konfiguraci beanu b2.

    <bean id="b2" class="FooTestImpl">
    <qualifier value="test">
    </bean>

    @Autowired
    @Qualifier("test")
    private Foo foo;

    Také to jde bez úpravy konfigurace a bez použití tagu <qualifier>. Pokud tento tag není definován, pak defaultní chování je takové, že se vygeneruje qualifier s hodnotou ID daného beanu. Mohu tedy pak napsat následující (ovšem pro tento přístup spíše doporučuji použití anotace @Resource, která je pro to určena):
    @Autowired
    @Qualifier("b2")
    private Foo foo;

  • použití anotace @Resource je pro tento případ vhodnější nez @Qualifier, zejména z pohledu sémantiky obou anotací.
    @Resource(name = "b2")
    private Foo foo;

  • použití primary atributu. Tento atribut dostupný pro každý bean říká, že se jedná o primární (první, doporučený) bean pro auto-wiring, pokud bude více adeptů stejného typu. Tuto vlastnost jsem objevil až nedávno, protože zmínka o ní je celkem zapadlá v dokumentaci.

  • za určitých podmínek by bylo možné ještě použít atribut autowire-candidate u produkčního beanu b1 a nastavit ho na false. Tím pak bude pro auto-wiring určen jen bean b2.

Tak jednoduchý problém a tolik možností řešení :). Já osobně nejčastěji používám řešení s primary a nebo s přepisováním definice beanu.

Žádné komentáře: