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