1. dubna 2010

Plošné vypnutí povinného @Autowired

Při testování naší agendiové aplikace jsem se již několikrát dostal do stavu, že složitost a propletenost celé aplikace mi neumožňovala napsat rozumně testy. Zejména jsem měl problém vůbec vše potřebné nakonfigurovat, aby se všechny závislosti správně nastavily.

Po nějakém čase mě napadlo, že by možná bylo vhodné pro účely testování vypnout autowiring jako povinný - tedy z @Autowired(required = true), což je implicitní chování, změnit na @Autowired(required = false). Tím získám tu velkou výhodu, že pro testy budu muset inicializovat pouze ty závislosti, které opravdu potřebuji. Tím budu mít méně konfigurace pro jednotlivé testy, což se rovná menším nárokům na údržbu testů, zejména konfigurace testů.

Zatím jsem nezjistil žádnou velkou nevýhodu uvedeného řešení, kromě toho, že je nutné v produkčním kódu plošně používat autowiring pomocí anotací. Trochu mě zaráží, že jsem o této myšlence ještě nikdy neslyšel.

A jak na to? Autowiring pomocí anotace @Autowired se děje pomocí bean post-procesoru AutowiredAnnotationBeanPostProcessor.

Mám dvě možnosti jak plošně změnit chování autowiringu:

  • v konfiguraci bean post-procesoru změním pomocí metody setRequiredParameterValue implicitní hodnotu parametru required.
  • vytvořím si novou implementaci bean post-procesoru, kde přepíšu metodu determineRequiredStatus.

/**
* Vlastni konfigurace {@link AutowiredAnnotationBeanPostProcessor} pro testy.
*
* <p>Jedina zmena je v tom, ze defaultne je anotace {@link Autowired} nastavena na {code required=false},
* tedy pokud se pres auto-wiring nenajde reference, tak to nebude vadit.
*
* <p>Test Context framework pouziva standardne svuj loader {@link AbstractGenericContextLoader}, ktery
* ovsem interne automaticky registruje vsechny mozne bean-post procesory,
* viz {@link AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry)}.
* Proto, aby chodil autowiring s required=false, pak je nutne:
* <ul>
* <li>pouzivat vlastni loader {@link AbstractDaisyTestContextLoader} pri spousteni testu
* <li>dat si pozor na konfiguraci {@link AutowiredAnnotationBeanPostProcessor} v ramci testovaciho Spring kontextu.
* </ul>
*
* @author <a href="mailto:petr.juza@marbes.cz">Petr Juza</a>
*/
public class AutowiredAnnotationBeanPostProcessorForTests extends AutowiredAnnotationBeanPostProcessor {

/**
* Vraci vzdy true, tedy neni nutne pres anotace {@link Autowired} mit dostupne vsechny reference.
*
* @param annotation the Autowired annotation
* @return vzdy true
*/
@Override
protected boolean determineRequiredStatus(Annotation annotation) {
return false;
}
}

Jak je v JavaDocu uvedeno, tak je potřeba si dát pozor na to, že Spring TestContext framework využívá svůj ContextLoader, který automaticky registruje AutowiredAnnotationBeanPostProcessor. Proto je potřeba mít svůj ContextLoader a podstrčit vlastní implementaci bean post-procesoru.

Díky tomuto řešení a díky tomu, že jsem objevil atribut primary u definice beanu, tak postupně měním svůj názor na autowiring (myslím pomocí anotací). Dříve jsem byl celkem konzervativní a byl jsem pro používání setterů, které je potřeba v XML nastavit. Důvod? Vše je jasné, nikde žádná magie, lehce mohu sledovat závislosti.
Pomocí anotací je obecně potřeba méně kódu a z pohledu testování mi to může přinést spoustu zajímavých výhod, jako např. tu zmíněnou v tomto článku.

2 komentáře:

Martin řekl(a)...

Nestacilo by nastavit beany jako lazy, aby se natahly, az kdyz je opravdu treba?

Petr Jůza řekl(a)...

To bych řekl, že nestačí. Pokud je nějaká beana označena jako lazy, tak pak se nebude inicializovat pouze tehdy, pokud na ní žádná jiná beana nebude odkazovat (je jedno jakým typem konfigurace).