Spring 3.0, JEE 6 a anotácie pre automatické prepájanie komponentov

2010/01/11

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 Hat (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.)

JSR-330

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.

Ukážme si jednoduchý príklad v ktorom sa používajú anotácie.

Springovské anotácie

Majme interfejs na generovanie citátov:

package sk.novotnyr.quotes;

public interface QuoteGenerator {
  public String getQuote();
}

a jeho jednoduchú implementáciu:

package sk.novotnyr.quotes;

public class HardwiredQuoteGenerator implements QuoteGenerator {

  public String getQuote() {
    return "Spring is gr8";
  }

}

A okrem toho majme triedu, ktorá bude vypisovať citáty na štandardný výstup:

public class QuotePrinter {
  private QuoteGenerator quoteGenerator;
  
  public void print() {
    String quote = quoteGenerator.getQuote();
    System.out.println(quote);
  }

  // gettre a settre
}

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.

public class QuotePrinter {
  @Inject
  private QuoteGenerator quoteGenerator;
  
  public void print() {
    String quote = quoteGenerator.getQuote();
    System.out.println(quote);
  }
}

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:

@Named
public class QuotePrinter {

a

@Named
public class QuoteGenerator {

Teraz už môžeme vytvoriť aplikačný kontext. V tomto prípade môžeme s výhodou využiť triedu AnnotationConfigApplicationContext:

AnnotationConfigApplicationContext context 
   = new AnnotationConfigApplicationContext("sk.novotnyr.quotes");
QuotePrinter printer = context.getBean(QuotePrinter.class);
for (int i = 0; i < 3; i++) {
  printer.print();
}

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

@Inject
@Named("HardwiredQuoteGenerator")
private QuoteGenerator quoteGenerator;

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ť.

@Named("HardwiredQuoteGenerator")
@Singleton
public class HardwiredQuoteGenerator

Vzťah k JSR-250

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.

Vzťah k Springu

Spring JSR-330
@Component @Named nad triedou
@Autowire @Inject
@Qualifier na inštančnej premennej či metóde @Named

Referencie

>> Home