středa 10. ledna 2007

Chudokrevné a plnokrevné IoC, EJB vs. Spring

Inversion of Control (IoC) je návrhový vzor, který přesouvá nutnost získání a inicializace závislostí (objektů) mimo rozsah objektu, který je využívá. Vlastní závislost je nastavena z vnějšku (Dependency Injection).

Mějme objekt A, který ke své práci používá objekt B (A má závislost na B). Objekt A má uvnitř kód, který vytváří a inicializuje objekt B. Kód pro vytvoření a inicializaci objektu B úzce svazuje oba objekty dohromady, navíc je to kód specifický danému prostředí, například JNDI lookup.

V případě IoC je "vázající" kód vytažen ven z objektu. Někdo třetí se stará o to, aby se do objektu A dostala v pravou chvíli zinicializovaná instance objektu B. Objekt B je do objektu A nastaven například settrem nebo přes konstruktor, této formě IoC se říká Dependency Injection (injekce závislosti).

Možná víte, možná ne, Dependency Injection (DI) je jedním z klíčových vlastností implementovaných v J2EE 5.0. Masivní rozšíření konceptu IoC resp. DI jako jeho způsoby popularizoval Spring framework a DI se brzo stal jednou z metel tradičního J2EE světa. Právě verze J2EE 5.0 a zavedeni DI se stalo v mnoha případech pro mnoho vývojářů důvodem zatracení Springu.

Pokud pominu samotný fakt, že Spring nabízí mnohem více než DI, samotná úroveň možností DI je silně rozdílná mezi Springem a EJB kontejnerem. Zrekapitulujme fakta.

EJB DI

  • definice závislosti - anotace, XML descriptor
  • aplikovatelnost - Servlet, listener classes, web services end-point, JAX-RPC handlers, DataSource, JMS, Mail, EJB, Environment entries, EntityManager, UserTransaction, EJB Beans, interceptors, web services end-point, DataSource, JMS, Mail, Environment entries, EntityManager, EJB Context, UserTransaction, TimerService
  • nepřímé závislosti - ne
  • způsob nastavení závislosti - setter, anotace proměnné
  • extension pointy - interceptory, lifecycle rozhrani
  • autowiring - ne

Spring DI

  • definice závislosti - XML descriptor, anotace, properties, konfigurační java kód
  • aplikovatelnost - bez omezení cokoliv
  • nepřímé závislosti - ano
  • způsob nastavení závislosti - setter, anotace proměnné, konstruktor
  • extension pointy - AOP, BeanPostProcessor, Factory, lifecycle rozhrani, typová konverze
  • autowiring - typem, jménem

Definice závislosti a aplikovatelnost

EJB v podstatě nabízí pouze anotace a jejich překrytí v XML descriptoru, případně jejich kombinaci. Oproti tomu Spring, krom vyjmenovaných způsobů a jejich kombinací, nelimituje v používání vlastních řešení pro popis závislostí. Právě tak vzniklo rozšíření v podobě anotací, které jsou sémanticky ekvivalentní možnostem DI anotacím.

Jestliže ve světě J2EE je možnost používání anotací limitována pouze na předem specifikovanou skupinu komponent, v Springu žádné takové omezení není. To je problematické především pro třídy, které nejsou ze své povahy managovatelné komponenty např. helpery, kolekce (mapy, seznamy) a nebo třídy mimo mimo EJB kontejner např. ve web vrstvě. Naopak ve Springu žádné takové omezení není.

Vyjadřovací schopnost standardního XML deskriptoru, oproti Spring descriptoru, je silně omezena. Například není možno definovat kolekce. To je problém pokud má objekt například jako dependency seznam určitých objektů. Spring od verze 2.0 navíc umožňuje definovat vlastní tagy, které mohou popisovat dané beany. Popisnost XML deskriptoru ma pro vývojáže řádově vyšší hodnotu.

Způsob nastavení závislosti

Dalším rozdílem je způsob nastavení závislosti, EJB kontejner nabízí setter nebo proměnnou (přes anotací). Spring nabízí krom obou navíc ještě konstruktor. Objekt může příjmat závislost i kontruktorem, pokud je na to jeho API uzpůsobeno. Velice užitečnou vlastností je podpora nepřímé závislosti (má pouze Spring). Pokud máme objekty, které spolu nesouvisí na úrovni kódu a přesto chceme zaručit, například pořadí inicializace, potřebujeme nepřímou závislost. Ta zaručí, že objekt X bude zinicializován před objektem B.

Extension pointy

Extension pointy nebo česky body rozšíření definují možnost rozšíření chování IoC. EJB kontejner nabízí krom lifecycle rozhraní ještě interceptory pro managed komponenty. Stejnou podporu má i Spring s tím rozdílem, že interceptory jsou nahrazeny aspekty. Spring navíc nabízí BeanPostProcessor, který umožňuje customizovat inicializované beany. Velkým plusem je možnost využití Factory objektů, kdy Spring IoC kontejner může vytváření některých objektů delegovat na specifickou klientskou factory.

Typová konverze je způsob převodu hodnot zapsaných v XML descriptoru na reálné objekty. Pokud například zapíšeme v XML descriptoru hodnotu 1.1.1970 a typem parametru settru je objekt foo.MyDateWrapper, pak Spring nabízí možnost definice PropertyEditoru, na který se kontejner obrátí v případě, že narazí na daný typ. Podporuje opět jenom Spring.

Autowiring

Autowiring je nastavení přímých závislostí bez jejich explicitního vyjmenování. Pokud je objekt A a objekt B řízený IoC kontejnerem, pak kontejner může automatický detekovat, závislost objektu A na B podle typu (A ma setter na objekt B) nebo podle jména (A má setter property, která jménem odpovídá jménu objektu B). Možnost autowiringu komponent podporuje pouze Spring.

To jsou jenom ty nejmarkantnější rozdíly, které mě napadly při srovnání Spring IoC kontejneru a EJB kontejneru. Určitě by se jich dalo najít více, ale cílem tohoto článku bylo upozornit na to, že není IoC jako IoC. I když Java 5 ušla velký kus cesty a díky IoC nabídla větší komfort, stále má Spring své silné stránky. Core součást IoC, na které je postavený celý Spring a potažmo všechny aplikace nad Springem postavené, je jednou z nich.