| Spring 3.0, JEE 6 a anotácie pre automatické prepájanie komponentov (Java) |
Obsah (skryť)
Od čias Springu 2.5 je k dispozícii sada anotácií, ktoré riešia dependency injection. (Viď existujúci článok.) V nej boli dané k dispozícii jednak springovské anotácie (@Component a pod.) a jednak anotácie z JSR-250 (najmä @Resource).
Od tých čias sa situácia ešte ,,skomplikovala". Medzičasom totiž vyšla finálna špecifikácia JEE6, ktorá rieši dependency injection v rámci dvoch JSR.
Spočiatku to vyzeralo tak, že špecifikácie vznikli vzájomne ,,na truc". Vo úvodnej fáze review Red Had (teda JBoss) vyjadril hlboké pochybnosti o zmysle JSR-330 (niet divu, veď sa to isté snažil riešiť po svojom). Po troche politických bojov sa však obe špecifikácie zladili, a Red Hat ju odsúhlasil s dodatkom, že očakáva ďalšiu spoluprácu medzi oboma štandardmi. Ako tvrdí viacero článkov, oba treba chápať ako vzťah známy JDBC a JPA. Existuje v nich prienik, ale jedna (299) je nadstavbou druhej (330). (Je len zaujímavé, že IBM neskôr zmenila svoj názor, pretože vidí v JSR-330 značné technické nedostatky a nesplnenie zámeru.)
Táto špecifikácia definuje päť anotácií a jeden interfejs, ktorými možno dosiahnuť DI. Samotnú implementáciu a spôsob prepájania však ponecháva na konkrétnu implementáciu prislušného DI frameworku.
Všetky anotácie sú z balíčka javax.inject.
@Inject - anotuje konštruktory, metódy či inštančné premenné, do ktorých majú byť injektnuté inštancie beanov.
@Qualifier - anotácia anotácií, ktorými možno bližšie obmedziť / špecifikovať injektovanie.
@Scope - metaanotácia, ktorá definuje anotácie zodpovedajúce scopeom, teda rozsahom platnosti. V prípade injektovania sa implicitne (ak chýba anotácia) predpokladá prototype scope (teda non-singleton), čiže s každým injektovaním sa vytvára nová inštancia injektovaného beanu
@Singleton — anotácia typu @Scope, ktorá indikuje, že anotovaný typ bude pri injektovaní používaný ako singleton.
@Named - umožní bližšie špecifikovať injektovaný bean. Odporúča sa používať len v prípade kontajnerov, ktoré označujú beany identifikátorom (čo je prípad Springu). V špecifikácii JEE6 má ešte jeden význam: ak je ňou anotovaná trieda, možno k nej pristupovať v rámci expression language (napr. v JSF šablónach).
Ukážme si jednoduchý príklad v ktorom sa používajú anotácie.
Majme interfejs na generovanie citátov:
a jeho jednoduchú implementáciu:
A okrem toho majme triedu, ktorá bude vypisovať citáty na štandardný výstup:
V JSR-330 je k dispozícii anotácia @Inject, ktorá indikuje nutnosť injektovania inštancie. Keďže QuotePrinter vyžaduje inštanciu QuoteGeneratora, injektovanie označíme anotáciou.
Do quoteGeneratora sa automaticky nawireuje implementácia príslušného rozhrania (prebieha detekcia podľa typu, v kontexte sa musí nájsť práve jedna implementácia, inak nastane výnimka). Ak použijeme @Inject na inštančnej premennej, nemusíme dokonca poskytnúť gettre a settre.
Ostáva ešte jedna ,,drobnosť: ako zaregistrovať beany v aplikačnom kontexte? Klasických možností je viac:
@Component
Treťou možnosťou je využitie JSR-330 anotácie Named. Ak je ňou anotovaný typ, Spring ho automaticky odhalí a zaradí do aplikačného kontextu.
a
Teraz už môžeme vytvoriť aplikačný kontext. V tomto prípade môžeme s výhodou využiť triedu AnnotationConfigApplicationContext:
Trieda automaticky vyhľadá v balíčku sk.novotnyr.quote nielen triedy anotované ako Named, ale aj všetky triedy so springovským stereotypom @Component (teda všetky triedy anotované ako @Component, @Repository, @Service a @Controller).
Anotácia @Named môže byť použitá v prípade, že chceme pri injektovaní presnejšie vyšpecifikovať použitý bean (napr. v prípade, že sa v aplikačnom kontexte nachádza viacero implementácií daného interfejsu):
V tomto prípade sa injektne ten bean, ktorý má v triede anotáciu @Named("HardwiredQuoteGenerator").
Nezabúdajme na to, že v tomto prípade prebehne prototype injektovanie. Vytvorí sa teda toľko inštancií QuoteGeneratora, koľko je inštancií QuotePrintera. Ak chceme mať QuoteGenerator ako singleton, musíme ho tak označiť.
Z predošlého článku si pamätáme, že injektovanie možno dosiahnuť aj pomocou anotácií javax.annotation.Resource a javax.annotation.WebServiceRef, kde prvá z nich slúžila jednak v úlohe anotovania beanu, ktorý sa má zaviesť do kontextu, jednak v úlohe anotácie @Inject a zároveň v nej bolo možné pomenovávať beany a bližšie špecifikovať odkaz na ne (teda obe úlohy @Named).
Špecifikácia JSR-250 však uvádza, že ,,The @Resource annotation is used to declare a reference to a resource such as a data source, an enterprise bean, or an environment entry," teda anotácia @Resource deklaruje odkaz na zdroj/prostriedok, ako napr. dátový zdroj enterprise bean či premenná prostredia.
Podľa dokumentácie (The Java EE 6 Tutorial, Volume I) a špecifikácie JSR-250 je úloha tejto anotácie skôr spätá s JNDI. V prípade jej použitia na triede sa z atribútu name odvodí JNDI meno, pod ktorým sa inštancia zaregistruje do JNDI kontextu. Ak je použitá na mene, z názvu premennej sa odvodí kľúč do JNDI kontextu, z ktorého sa inštancia vytiahne.
| Spring anotácia | JSR-330 anotácia |
|---|---|
@Component | @Named na type |
@Autowire | @Inject |
@Qualifier na inštančnej premennej/metódy | @Named |