Jedným z významných prínosov Spring 2.5 je podpora anotácií, ktoré môžu slúžiť ako alternatíva na deklarovanie vzťahov a závislostí medzi jednotlivými komponentami a riešenie dependency injection. K dispozícii je podpora pre springovskú sadu anotácií i pre štandardizované anotácie zo špecifikácie JSR-250 (Commons Annotations for Java Platform).
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 klasickom Springu by sme mohli nadeklarovať v aplikačnom kontexte nasledovné beany:
<bean id="hardwiredQuoteGenerator"
class="sk.novotnyr.quotes.HardwiredQuoteGenerator" />
<bean id="quotePrinter"
class="sk.novotnyr.quotes.QuotePrinter" />
a použiť vypisovač citátov nasledovne:
ClassPathXmlApplicationContext ctx
= new ClassPathXmlApplicationContext(
"applicationContext.xml");
QuotePrinter printer = (QuotePrinter) ctx.getBean("quotePrinter");
printer.print();
V novom Springu 2.5 sú k dispozícii anotácie @Autowired a @Component, ktoré reprezentujú alternatívny spôsob deklarácie a wiringu beanov. Triedy anotované ako komponenty nie je potrebné deklarovať v popisovači aplikačného kontextu. Odhalia sa automaticky v CLASSPATHe (viď nižšie). Poznamenajme, že k anotácii @Component jestvujú jej špecializácie @Service, @Repository a @Controller, ktoré je možné používať pre anotáciu služieb, úložísk (t. j. DAO objektov) a kontrolérov (v MVC vrstve).
@Component
public class HardwiredQuoteGenerator implements QuoteGenerator {
public String getQuote() {
return "Spring is gr8";
}
}
A asociáciu s vypisovačom vykonáme pomocou autowiringu:
@Component
public class QuotePrinter {
@Autowired
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 @Autowired na inštančnej premennej, nemusíme dokonca poskytnúť gettre a settre.
Ak chceme 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), môžeme použiť anotáciu @Qualifier.
@Autowired
@Qualifier("hardwiredQuoteGenerator")
private QuoteGenerator quoteGenerator;
Tento príklad skúsi nawireovať triedu HardwiredQuoteGenerator implementujúcu interfejs QuoteGenerator (názov beanu v @Qualifier sa odvodí z názvu triedy).
Samotný QuotePrinter má byť takisto beanom v aplikačnom kontexte, preto ho analogicky označíme ako @Component.
Z popisovača aplikačného kontextu teda môžeme vynechať beany, ktoré zodpovedajú anotovaným triedam. Musíme však zapnúť podporu pre načítavanie anotovaných tried.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="sk.novotnyr.quotes" />
</beans>
Element component-scan zapne podporu pre anotácie @Component, @Autowired, @Qualifier a pod. a zaregistruje komponentové beany (nachádzajúce sa v príslušnom balíčku) ako beany.
Vypisovač citátov naštartujeme a spustíme zvyčajným spôsobom:
ClassPathXmlApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
QuotePrinter printer = (QuotePrinter) ctx.getBean("quotePrinter");
printer.print();
Používanie anotácií z JSR-250
Spring podporuje aj používanie anotácií z JSR-250 (@Resource, @PostDestroy, @PreDestroy atď) na reprezentovanie beanov a ich wiringu. Tieto anotácie sú k dispozícii v Java SE 6 a Java 5 EE automaticky. Ak používame staršiu verziu Javy, JAR súbor s triedami si môžeme stiahnuť zo stránok JSR-250. Náš generátor citátov môžeme upraviť, namiesto springovskej anotácie @Component použijeme @Resource:
import javax.annotation.Resource;
@Resource
public class HardwiredQuoteGenerator implements QuoteGenerator {
public String getQuote() {
return "Spring is gr8";
}
}
Analogicky upravíme aj QuotePrinter. Anotácia @Resource má dvojakú sémantiku: v prípade, že anotuje triedu, indikuje tým komponent, ktorý má byť vyhľadaný počas behu. Ak anotuje inštančnú premennú, reprezentuje tým cieľ pre dependency injection.
@Resource
public class QuotePrinter {
@Resource(name="randomQuoteGenerator")
private QuoteGenerator quoteGenerator;
public void print() {
String quote = quoteGenerator.getQuote();
System.out.println(quote);
}
}
V prípade, že potrebujeme vyriešiť nejednoznačnosť beanov, môžeme použiť atribút name, v ktorom uvedieme identifikátor beanu, ktorý sa má nawireovať (podobne ako v prípade springovských anotácií sa identifikátor odvodí od názvu triedy).
Budeme musieť ešte upraviť popisovač aplikačného kontextu. Element component-scan totiž v CLASSPATH vyhľadáva len triedy anotované ako @Component, @Repository, @Service a @Component. Ak chceme detekovať aj @Resource, musíme to uviesť konfigurácii tohto elementu.
<context:component-scan base-package="sk.novotnyr.quotes.jsr250">
<context:include-filter type="annotation"
expression="javax.annotation.Resource"/>
</context:component-scan>
Vytvorenie vlastnej anotácie
Spring umožňuje používať pre automatickú registráciu beanov v kontexte akúkoľvek anotáciu (nielen @Component, @Resource atď.) Principiálne jestvujú dva spôsoby:
- vytvorenie vlastnej anotácie a jej zavedenie cez
<context:include-filter>. To sme demonštrovali na predošlom príklade anotáciejavax.annotation.Resource. - vytvorenie vlastnej anotácie, ktorá je špecializáciou springovskej anotácie
@Component. V tejto časti rozoberieme práve tento spôsob.
Java anotácie nepodporujú dedičnosť (anotácia nemôže dediť prvky a metódy od inej anotácie). V prípade springovskej špecializácie je možné tento nedostatok obísť - stačí, keď oanotujeme vlastnú anotáciou pomocou @Component:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Bean {
String value() default "";
}
Inštančná premenná value nie je povinná, ale pomocou nej môžeme dodávať alternatívny názov pre anotovaný bean.
Všetky bean anotované pomocou @Bean zaregistrujú automaticky - jediná vec, ktorú treba špecifikovať, je zapnutie automatickej detekcie v konfiguračnom súbore:
<context:component-scan base-package="sk.novotnyr.spring.tools" />
Príklad použitia je potom nasledovný:
@Bean
public class HardwiredQuoteGenerator implements QuoteGenerator {
public String getQuote() {
return "Spring is gr8";
}
}
Bean získame z kontextu nasledovným spôsobom:
ClassPathXmlApplicationContext ctx
= new ClassPathXmlApplicationContext("ctx.xml");
QuoteGenerator tool
= (QuoteGenerator) ctx.getBean("hardWiredQuoteGenerator");
Samozrejme, bean môžeme aj aliasovať
@Bean("quoteGenerator")
public class HardwiredQuoteGenerator implements QuoteGenerator {
...
}
a v tom prípade bude jeho získanie z kontextu prebiehať nasledovne:
QuoteGenerator tool
= (QuoteGenerator) ctx.getBean("quoteGenerator");
Referencie
- Článok na InfoQ
- Oficiálna dokumentácia
- Configuring the Spring Container , prezentácia Roda Johnsona.