Vysoká škola ekonomická v Praze Fakulta informatiky a statistiky Katedra informačních technologií
Student Vedoucí bakalářské práce
: Helena Šírová : Ing. Jarmila Pavlíčková
TÉMA BAKALÁŘSKÉ PRÁCE
Anotace v Javě
ROK : 2007
Prohlášení
Prohlašuji, že jsem bakalářskou práci zpracovala samostatně a že jsem uvedla všechny použité prameny a literaturu, ze kterých jsem čerpala.
V Praze dne 16. 8. 2007
............................... podpis
Poděkování
Děkuji vedoucí své práce Ing. Jarmile Pavlíčkové za cenné rady a připomínky při psaní této práce. Dále děkuji rodičům za podporu při studiu.
Abstrakt Tato práce popisuje tvorbu, použití a význam anotací v programovacím jazyce Java. Dále poskytuje popis několika obecně použitelných anotací, které jsou v Javě definovány, a návod na tvorbu vlastních anotací a jejich zpracování pomocí anotačních procesorů. Na příkladech z oblasti tvorby webových služeb pomocí JAX-WS (JavaTM API for XML-Based Web Services) a z oblasti převodu Java tříd do XML formátu pomocí JAXB (JavaTM Architecture for XML Binding) je ukázáno použití předdefinovaných anotací, jejichž použití je již rozšířeno, a jejich přínos pro snadnější psaní aplikací. Práce má shrnout poznatky o anotacích teprve nedávno zařazených do specifikace jazyka Java.
Abstract The following thesis describes main functions, creating and using of Java annotations. Futher it provides the description of several generally applicable annotations defined in Java and the instructions for creating custom annotations and their processing using annotations processors. There are provided two larger examples which demonstate using of predefined annotations which are widely used and their benefit for easy development of applications. One is from the field of creation the Web Services using the specification JAX-WS (JavaTM API for XML-Based Web Services) and the other from the field of mapping Java classes to XML. The thesis summarizes knowledge about annotations recently included in the Java language specification.
Obsah 1 Úvod
8
2 Úvod do anotací 9 2.1 Anotace a jejich význam . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2 Historie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3 Základní pojmy a pravidla 11 3.1 Anotační typ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.2 Anotace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 3.3 Metaanotace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 4 Anotační procesory 4.1 APT . . . . . . . . . . . . . 4.1.1 Vytvoření anotačního 4.2 API pro zpracování anotací 4.3 Použití pro validaci . . . . .
. . . . . . procesoru . . . . . . . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
5 Hlavní využití anotací 5.1 Upozornění pro překladač . . . . . . . . . . . . . . . . 5.2 Generování zdrojových kódů a konfiguračních souborů . 5.3 Omezení hodnot . . . . . . . . . . . . . . . . . . . . . . 5.4 Zjednodušení psaní kódu programátorem . . . . . . . . 6 Předdefinované anotační typy 6.1 @Target . . . . . . . . . . . 6.2 @Retention . . . . . . . . . 6.3 @Inherited . . . . . . . . . . 6.4 @Override . . . . . . . . . . 6.5 @SuppressWarnings . . . . .
. . . . .
. . . . .
. . . . . 6
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . .
. . . .
. . . . .
. . . .
. . . .
. . . . .
. . . .
. . . .
. . . . .
. . . .
. . . .
. . . . .
. . . .
. . . .
. . . . .
. . . .
. . . .
. . . . .
. . . .
. . . .
. . . . .
. . . .
15 15 16 19 21
. . . .
22 22 23 23 25
. . . . .
26 26 27 28 29 29
OBSAH 6.6 6.7 6.8
7 @Deprecated . . . . . . . . . . . . . . @Documented . . . . . . . . . . . . . . Další anotace představené v Java SE 6 6.8.1 @Generated . . . . . . . . . . . 6.8.2 @Resource . . . . . . . . . . . . 6.8.3 @Resources . . . . . . . . . . . 6.8.4 @PostConstruct . . . . . . . . . 6.8.5 @PreDestroy . . . . . . . . . .
7 Anotace ve speciálních oblastech 7.1 Webové služby – JAX-WS 2.0 . 7.1.1 JAX-RPC . . . . . . . . 7.1.2 JAX-WS . . . . . . . . . 7.2 Java do XML – JAXB . . . . .
Javy . . . . . . . . . . . . . . . .
8 Anotace a jiné nástroje 8.1 Srovnání anotací s XDocletem . . . . 8.2 Anotace versus konfigurační soubory 8.3 Metadata v jiných jazycích . . . . . . 8.3.1 C# . . . . . . . . . . . . . . . 8.3.2 C/C++ . . . . . . . . . . . . 9 Závěr
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
. . . .
. . . . .
. . . . . . . .
30 30 30 30 31 32 32 33
. . . .
34 35 36 41 44
. . . . .
50 50 52 52 52 53 55
Kapitola 1 Úvod Programovací jazyk Java byl rozšířen o možnost přidávání doplňkových dat (metadat) do zdrojových kódů. Za tímto účelem byly do specifikace Javy od verze 5.0 přidány anotace. Ač jsou poměrně novým prvkem, jejich použití se již celkem rozšířilo. Anotace jsou využívány v různých oblastech Javy, hojně se používají při tvorbě podnikových aplikací (J2EE). Jejich rozšíření je z velké části způsobeno téměř nutností jejich použití v některých oblastech, například při tvorbě Enterprise JavaBean 3.0, ale tato nutnost většinou také znamená zjednodušení práce. Data, která anotace přidávají do zdrojového kódu, lze využít ke generování nových souborů, k upozorňování překladače na některé skutečnosti a jiným účelům. Anotace mohou v některých případech nahrazovat konfigurační soubory a mohou být alternativou k použití XDocletu či jiných nástrojů pro generování kódu. Tato práce má za cíl shrnout dostupné informace o anotacích v Javě, popsat jejich tvorbu, použití a význam a srovnat je s již používanými nástroji, které mají podobné použití jako anotace. V práci budou dále popsány některé anotace, které jsou v Javě předdefinovány. Uvedeny budou také dva příklady s využitím anotací ze speciálnějších oblastí Javy, ve kterých jsou již anotace významně používány, a to z oblasti tvorby webových služeb a převodu Java tříd do XML formátu. Zdroje informací použité pro vypracování práce jsou výhradně elektronické, protože veškerá dokumentace Javy a souvisejících technologií je dostupná na Internetu.
8
Kapitola 2 Úvod do anotací Tato kapitola vysvětluje, co jsou anotace v Javě, jaké byly důvody jejich zařazení do tohoto programovacího jazyka a jaké jsou hlavní možnosti využití anotací. Na závěr je shrnuta krátká historie anotací.
2.1
Anotace a jejich význam
Účelem anotací, někdy také nazývaných „metadataÿ, je možnost přidávat doplňkové informace do zdrojového kódu. Taková možnost již byla dříve pomocí jiných nástrojů, ale nebyla standardní součástí specifikace jazyka Javy. Proto byly vytvořeny anotace jako způsob, jak vyřešit několik problémů, z nichž nejdůležitější jsou generování konfiguračních souborů, vytváření upozornění pro překladač, zjednodušení psaní kódu programátorem a validace kódu. Zjednodušení práce s pomocí anotací znamená především, že je možné vytvářet méně souborů, informace přidávat přímo k popisovaným prvkům bez nutnosti vytváření dalších konfiguračních souborů a automaticky generovat další soubory podle informací z anotací. Další výhodou, kterou anotace přinášejí, je možnost provádění validace některých prvků kódu během překládání. Příkladem může být anotace @Override, která, pokud ji napíšeme před metodu, upozorní překladač, že tato metoda má přepisovat metodu předka. Může se snadno stát, že použijeme jiný parametr metody nebo změníme metodu v předkovi a zapomeneme ji změnit také v potomkovi. Taková chyba se potom špatně vyhledává. Pomocí anotací lze generovat konfigurační soubory, které se dříve psaly ručně například ve formátu XML. Anotace mohou obsahovat potřebné informace, které má
9
KAPITOLA 2. ÚVOD DO ANOTACÍ
10
konfigurační soubor obsahovat, a to přímo u prvku programu, kterého se informace týká. Programátor je tedy nemusí hledat někde jinde. Anotace mohou vytvářet upozornění pro překladač – například, že použití dané metody je zastaralé a neměla by se používat (anotace @Deprecated), nebo mohou překladač instruovat, aby právě varování o zastaralosti nezobrazoval (anotace @SuppressWarnings). Anotace (přesněji anotační typy – vysvětlení viz další kapitola) si může také každý vytvářet. Typický programátor to pravděpodobně dělat nebude, protože bude využívat už definovaných typů, ale vytváření vlastní typů není nijak zvlášť složité, stejně jako jejich zpracování, pro které se využívá tzv. anotačních procesorů, jejichž vytváření si ukážeme v kapitole 4. Některé funkce, které anotace nabízejí, již nabízejí jiné nástroje. Vkládání doplňkových informací do zdrojového kódu umožňuje například XDoclet, který pro to používá speciální tagy JavaDocu. Bližší srovnání anotací s XDocletem je uvedeno v kapitole 8.
2.2
Historie
Anotace v Javě byly poprvé popsány ve specifikaci JSR 175 – „A Metadata Facility for the JavaTM Programming Languageÿ [2] v roce 2002 a konečné schválení proběhlo v roce 2004. Zařazeny do Javy jsou od verze 5.0. v Java SE 6 byly představeny v souvislosti s anotacemi dvě specifikace a to JSR 250 – „Common Annotations for the JavaTM Platformÿ [3] a JSR 269 – Pluggable Annotation Processing API [4]. První z nich shrnuje několik anotací použitelných pro platformu Java EE i obecně použitelných. Druhý se týká zpracování anotací pomocí anotačních procesorů, o kterých bude zmínka dále.
Kapitola 3 Základní pojmy a pravidla pro tvorbu anotací Faktické informace v této kapitole jsou čerpány především ze specifikace jazyka Java [1] a dokumentu JSR 175 – „A Metadata Facility for the JavaTM Programming Languageÿ [2]. Kapitola popisuje tři hlavní pojmy, které se v problematice anotací v Javě vyskytují a to jsou pojmy „anotaceÿ, „anotační typÿ a „metaanotaceÿ. V další kapitole se poté dozvíme, jak můžeme vytvořené anotace zpracovávat pomocí tzv. anotačních procesorů.
3.1
Anotační typ
Každá anotace musí mít svůj anotační typ. Vztah anotačního typu k anotaci je podobný jako vztah rozhraní k třídě, která toto rozhraní implementuje. Anotační typ vyjmenovává prvky, kterým poté anotace může přiřadit hodnoty. Deklarace anotačního typu se podobá deklaraci rozhraní. Pro rozlišení od normálního rozhraní se píše před slovem interface znak „@ÿ. Všechny anotační typy dědí metody z rozhraní java.lang.annotation.Annotation, i když se to u nich neuvádí. Deklarace anotačního typu má podle JSR 175 [2] následující omezení: • nelze použít klauzuli extends • metody nemohou mít parametry • metody nemohou používat klauzuli throws • metody nemohou používat typové parametry (tj. jsou zakázány generické metody) 11
KAPITOLA 3. ZÁKLADNÍ POJMY A PRAVIDLA
12
• metody mohou vracet – primitivní typy, String, Class, výčtové typy, anotační typy a pole předchozích typů • anotační typy nemohou být parametrizovány U deklarace metody můžeme přednastavit hodnotu pomocí klíčového slova default. Následuje příklad anotačního typu, který deklaruje tři metody a u jedné přednastavuje hodnotu: public @interface ZodpovednaOsoba { int id(); String jmeno(); String funkce() default "projektant"; } Anotační typ nesmí obsahovat členy stejného typu jako je sám. Následující dva příklady jsou nesprávné: public @interface SpatnyAnotacniTyp { SpatnyAnotacniTyp hodnota(); } public @interface TypJedna { TypDva neco(); } public @interface TypDva { TypJedna neco(); }
3.2
Anotace
Anotace se používá jako modifikátor. Je složena z názvu anotačního typu a na rozdíl od dalších modifikátorů obsahuje také položky, které přiřazují hodnoty prvkům definovaným v anotačním typu. Hodnoty musí být přiřazeny všem prvkům, které nemají v deklaraci anotačního typu přednastavenou hodnotu. Použití anotačního typu ZodpovednaOsoba z předchozího odstavce:
KAPITOLA 3. ZÁKLADNÍ POJMY A PRAVIDLA
13
@ZodpovednaOsoba ( id = 12; jmeno = "Petr Kovář"; funkce = "projektant kazetových podhledů" ) public static void navrhniStrop() { ... } Anotace můžeme rozdělit na tři typy – normální anotace, značkovací anotace a jednočlenné anotace. Druhé dva typy jsou pouze zjednodušením normální anotace. Příkladem normální anotace je anotace ZodpovednaOsoba uvedená výše. Značkovací anotace neobsahuje žádné členy, lze ji proto psát bez závorek: @DvojkovaSoustava public void zacniPocitat() { ... } Jde o zkrácený zápis normální anotace: @DvojkovaSoustava() public void zacniPocitat() { ... } Jednočlenná anotace obsahuje pouze jeden prvek. Proto u ní nemusíme uvádět název prvku, stačí jeho hodnota: @Zakladatele({"Jan", "Petr"}) public void zalozKlub() { ... } Jde o zkrácený zápis normální anotace: @Zakladatele(zakladatele = {"Jan", "Petr"}) public void zalozKlub() { ... }
3.3
Metaanotace
Metaanotace jsou tzv. „anotace anotacíÿ, neboli anotace, které jsou uvedeny před deklarací anotačního typu. V následujícím příkladu je ukázána deklarace anotačního typu Strom s metaanotací Meta. @Meta public @interface Strom { String[] vetve(); }
KAPITOLA 3. ZÁKLADNÍ POJMY A PRAVIDLA
14
Pokud je u deklarace metaanotačního typu uvedena anotace Target (popis viz 6.1), může mít pouze hodnotu ANNOTATION TYPE, která povoluje použití anotace jenom před deklarací anotačního typu. Deklarace metaanotačního typu Meta by pak vypadala například takto:
@Target(ElementType.ANNOTATION_TYPE) public @interface Meta { String typStromu(); ... }
Kapitola 4 Anotační procesory Kapitola popisuje, jak lze zpracovávat anotace pomocí tzv. anotačních procesorů, které umožňují vyhledávat anotace ve zdrojovém kódu, zpracovávat informace v nich obsažené a případně generovat nové zdrojové kódy či jiné soubory.
4.1
APT
Součástí JDK 5 je nástroj APT – Annotation Processing Tool, který se používá z příkazové řádky. Slouží ke zpracování anotací ve zdrojových Java kódech. APT zpracovává anotace pomocí tzv. anotačních procesorů, které mohou generovat nové zdrojové kódy a jiné soubory. APT vyhledá anotace v kódu, poté volá třídy implementující rozhraní AnnotationProcessorFactory, které obsahují informace o tom, které anotace se mají zpracovávat a jaké anotační procesory se mají použít. Poté se spustí anotační procesory pro zpracování příslušných anotací. Pokud tyto procesory vygenerují další zdrojové kódy, APT opakuje celý proces znova až do doby, kdy už nejsou vygenerovány žádné nové zdrojové soubory. Nakonec APT nechá všechny nové i původní soubory zkompilovat pomocí javac. APT používá Mirror API, které slouží k modelování struktury programu. Poskytuje reprezentaci prvků programu jako jsou třídy, metody či atributy. Modelují se buď zdrojové nebo přeložené kódy. Jsou to v podstatě obrazy tříd bez ztráty informace. Bližší informace k tomuto rozhraní jsou k dispozici v popisu Mirror API [6].
15
KAPITOLA 4. ANOTAČNÍ PROCESORY
4.1.1
16
Vytvoření anotačního procesoru
Abychom mohli spustit zpracování anotací pomocí nástroje APT, potřebujeme dvě třídy – první z nich je anotační procesor, což je třída, která implementuje rozhraní com.sun.mirror.apt.AnnotationProcessor, druhá je „obslužnáÿ třída implementující rozhraní com.sun.mirror.apt.AnnotationProcessorFactory. Tato druhá třída obsahuje metody, které vrací instanci procesoru a typy anotací, které anotační procesor podporuje. Následuje příklad s použitím těchto tříd. Vytvoříme si jednoduchý anotační typ a třídu, kde anotaci tohoto typu použijeme: package cz.anotace.test; import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target(ElementType.METHOD) public @interface TestAnnotation { String message() default "ahoj"; } package cz.anotace.test; public class Test { @TestAnnotation(message = "AHOJ") public void test1() { } @TestAnnotation public void test2() {} } Vytvoříme třídu TestApf, která bude zpracovávat anotace typu TestAnnotation, a anotační procesor TestAp: package cz.anotace.test; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableCollection; import java.util.Arrays;
KAPITOLA 4. ANOTAČNÍ PROCESORY
17
import java.util.Collection; import java.util.Set; import import import import
com.sun.mirror.apt.AnnotationProcessor; com.sun.mirror.apt.AnnotationProcessorEnvironment; com.sun.mirror.apt.AnnotationProcessorFactory; com.sun.mirror.declaration.AnnotationTypeDeclaration;
public class TestApf implements AnnotationProcessorFactory{ public AnnotationProcessor getProcessorFor( Set
typeDeclaration, AnnotationProcessorEnvironment processorEnv) { return new TestAp(processorEnv); } //anotace, které se mají zpracovat public Collection<String> supportedAnnotationTypes() { String[] supportedTypes = {"cz.anotace.test.TestAnnotation"}; return unmodifiableCollection( Arrays.asList(supportedTypes)); } //žádné další nastavení public Collection<String> supportedOptions() { return emptySet(); } } Metoda supportedAnnotationTypes vrací typ anotací, které jsou podporovány. Pokud bychom chtěli zpracovávat všechny anotace, použili bychom hvězdičku, tedy Arrays.asList("*"). V metodě getProcessorFor určíme typ procesoru, kterým se budou anotace zpracovávat. V anotačním procesoru je nejdůležitější metoda process, která se spustí při vyvolání procesoru, a obsahuje vlastní zpracování anotací:
KAPITOLA 4. ANOTAČNÍ PROCESORY package cz.anotace.test; import java.util.Collection; import import import import import
com.sun.mirror.apt.AnnotationProcessor; com.sun.mirror.apt.AnnotationProcessorEnvironment; com.sun.mirror.declaration.AnnotationMirror; com.sun.mirror.declaration.MethodDeclaration; com.sun.mirror.declaration.TypeDeclaration;
public class TestAp implements AnnotationProcessor { private AnnotationProcessorEnvironment processorEnv; public TestAp(AnnotationProcessorEnvironment processorEnv) { this.processorEnv = processorEnv; } public void process() { Collection typeDeclarations = this.processorEnv.getSpecifiedTypeDeclarations(); for (TypeDeclaration typeDeclaration : typeDeclarations) { System.out.println("Typ " + typeDeclaration); Collection extends MethodDeclaration> methodsDeklarations = typeDeclaration.getMethods(); for (MethodDeclaration methodDeklaration : methodsDeklarations) { Collection annotationMirrors = methodDeklaration.getAnnotationMirrors(); System.out.println("Metoda: " + methodDeklaration.getSimpleName());
18
KAPITOLA 4. ANOTAČNÍ PROCESORY
19
for (AnnotationMirror annotation : annotationMirrors) { System.out.println("Anotace a její hodnota: " + annotation.getAnnotationType().toString() + " " + annotation.getElementValues().toString()); } } } } } V našem případě pouze vypisujeme anotace obsažené ve třídě Test do konzole. Z příkazové řádky můžeme spustit náš procesor příkazem: apt -factorypath -factory cz.anotace.test.TestApf \Test.java Na výstupu se ukáže: Typ cz.anotace.test.Test Metoda: test1 Anotace a její hodnota: cz.anotace.test.TestAnnotation {message()="AHOJ"} Metoda: test2 Anotace a její hodnota: cz.anotace.test.TestAnnotation {}
4.2
API pro zpracování anotací
Součástí Java SE 6 se stala specifikace JSR 269 – „Pluggable Annotation Processing APIÿ. Toto API obsahuje standardizované knihovny pro zpracování anotací a jeho použití se velmi podobá APT API. Oproti APT si ušetříme jednu třídu a můžeme psát už jen samotný anotační procesor. Anotace, které podporuje, jsou vypsány v anotaci @SupportedAnnotationTypes přímo u anotačního procesoru. Zpracování anotací probíhá při spuštění kompilátoru javac. Ten najde anotační procesory a spustí je, poté zkompiluje všechny zdrojové kódy. Ukážeme si příklad se stejnou funkčností jako jsme použili u APT. Anotační procesor vytvoříme jako třídu, která je potomkem třídy javax.annotation.processing.AbstractProcessor:
KAPITOLA 4. ANOTAČNÍ PROCESORY
20
package cz.anotace.test; import java.util.Collection; import java.util.List; import java.util.Set; import import import import import import import import
javax.annotation.processing.AbstractProcessor; javax.annotation.processing.RoundEnvironment; javax.annotation.processing.SupportedAnnotationTypes; javax.annotation.processing.SupportedSourceVersion; javax.lang.model.SourceVersion; javax.lang.model.element.AnnotationMirror; javax.lang.model.element.Element; javax.lang.model.element.TypeElement;
@SupportedAnnotationTypes({"cz.anotace.test.TestAnnotation"}) @SupportedSourceVersion(SourceVersion.RELEASE_6) public class TestAnnotationProcessor extends AbstractProcessor
{
@Override public boolean process( Set extends TypeElement> elements, RoundEnvironment roundEnvironment){ Set extends Element> rootElement = roundEnvironment.getRootElements(); for (Element root: rootElement) { System.out.println("Typ "+ root); List extends Element> elems = root.getEnclosedElements(); for (Element element: elems) { System.out.println("Metoda " + element); Collection extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
KAPITOLA 4. ANOTAČNÍ PROCESORY
21
for (AnnotationMirror annotation : annotationMirrors) { System.out.println("Anotace a její hodnota: " + annotation.getAnnotationType().toString() + " " + annotation.getElementValues().toString()); } } } return true; } } Spustíme překladač javac z příkazové řádky: javac -cp -processor cz.anotace.test.TestAnnotationProcessor \Test.java Dostaneme úplně stejný výsledek jako s APT.
4.3
Použití pro validaci
Vhodné použití APT či JSR 269 je například pro validaci anotací, kterou specifikace anotací (JSR 175) neumožňuje. Nelze například kontrolovat použití dvou vzájemně se vylučujících anotací u jedné třídy, což jsou třeba anotace @Stateless a @Stateful u Session EJB. O této kontrole se zmiňuje článek „Validate Java EE Annotation with Annotation Processorÿ [15].
Kapitola 5 Hlavní využití anotací Využití anotací se dá shrnout do tří hlavních oblastí – upozornění pro překladač, zjednodušení psaní kódu programátorem a generování zdrojových kódů a konfiguračních souborů. Tato kapitola popisuje jednotlivé oblasti o něco podrobněji.
5.1
Upozornění pro překladač
Anotace mohou posílat upozornění (zprávy) překladači. Specifikace jazyka Javy obsahuje tři anotace, které se k tomuto používají – @Override, @Deprecated a @SuppressWarnings. Tyto anotace jsou popsány blíže v kapitole 6 týkající předdefinovaných anotací. Nyní si jen ukážeme příklady, jak je lze využít. Uvedeme-li například @SuppressWarnings před metodou, řekneme tím překladači, že nechceme, aby hlásil varování o použití zastaralé metody (označované jako „deprecatedÿ): @SuppressWarnings("deprecation") public void NejakaMetoda() { //volání zastaralé metody, varování se neobjeví nejakyObjekt.zastaralaMetoda(); } Anotací @Override zase označíme pro překladač metodu, která přepisuje metodu předka: @Override public String PrepisujiciMetoda() { ... } 22
KAPITOLA 5. HLAVNÍ VYUŽITÍ ANOTACÍ
23
Pokud se spleteme například v názvu metody a překladač nenajde stejnou metodu v předkovi, upozorní nás chybou. Anotace @Deprecated označí metodu, kterou má překladač považovat za zastaralou a hlásit varování při jejím použití: @Deprecated public String ZastaralaMetoda() { ... }
5.2
Generování zdrojových kódů a konfiguračních souborů
Anotace umožňují přidávat do kódu informace potřebné pro generování dalších zdrojových kódů a konfiguračních souborů, které vyžadují některé oblasti Javy jako je například tvorba EJB nebo webových služeb. Příklad na anotace pro tvorbu webových služeb pomocí specifikace JAX-WS je uveden v kapitole 7. Při tvorbě Enterprise JavaBean se pomocí anotací dají generovat potřebná rozhraní a deskriptory. Například při vytváření Stateless Session Bean, stačí přidat ke kódu anotaci @Stateless pro označení, že jde o Stateless Session Bean, a nemusíme tyto informace uchovávat v dodatečných souborech: @Stateless public class MojeEJB { ... }
5.3
Omezení hodnot
Anotace lze použít k vytváření omezení pro data, číselné hodnoty apod. Pokud bychom například chtěli omezit číselnou hodnotu na určitý rozsah hodnot, mohli bychom si vytvořit anotační typy @Min a @Max, které budou použitelné na atributy a přístupné za běhu programu pomocí reflexe (bližší vysvětlení anotace @Retention je v kapitole 6): @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
KAPITOLA 5. HLAVNÍ VYUŽITÍ ANOTACÍ
24
public @interface Min { int minimum(); } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Max { int maximum(); } Použijeme je u číselné hodnoty: public class Cisla { @Min(minimum = 15) @Max(maximum = 222) public int hodnota; .... } Pomocí reflexe vyhledáme anotace u atributu hodnota a najdeme anotace @Min a @Max: //najdi atribut hodnota Field field = null; try { field = Cisla.class.getField("hodnota"); } catch (NoSuchFieldException e) { e.printStackTrace(); } //najdi anotace u atributu Annotation[] annotations = field.getAnnotations(); Max max = null; Min min = null; for (Annotation a : annotations) { if (a instanceof Max) { max = (Max)a; }
KAPITOLA 5. HLAVNÍ VYUŽITÍ ANOTACÍ
25
if (a instanceof Min) { min = (Min)a; } } Nyní můžeme validovat podmínky: hodnota = 11599; if (hodnota > max.maximum()) { // kód, který se má vykonat při porušení podmínky System.out.println("Porušena podmínka - max"); } if (hodnota < min.minimum()) { // kód, který se má vykonat při porušení podmínky System.out.println("Porušena podmínka - min"); } Další informace o validaci pomocí anotací lze nalézt v článku [18].
5.4
Zjednodušení psaní kódu programátorem
Zjednodušení psaní kódu souvisí především s generováním kódu. Informace pro generování píšeme přímo k prvku Java kódu, ke kterému informace náleží. Proto není nutné mít informace na různých místech, například v konfiguračních souborech, ale vše potřebné se nachází pohromadě v jednom souboru.
Kapitola 6 Předdefinované anotační typy Některé anotační typy jsou již v Javě předdefinovány. Mnohé typy se váží ke speciálním Java platformám, například k Java 2 Enterprise Edition. V této kapitole si popíšeme několik všeobecně používaných typů, které jsou obsaženy v balíčcích java.lang nebo java.lang.annotation. Můžeme je rozdělit na dvě skupiny – na anotace, které používá překladač, a na metaanotace. Do první skupiny patří @Override, @Deprecated a @SuppressWarnings, do druhé @Target, @Retention, @Inherited a @Documented.
6.1
@Target
Metaanotaci Target můžeme přiřadit k deklaraci anotačního typu a tím určit, u jakého prvku programu je možné anotace tohoto typu použít. Má pouze jeden prvek, který popisuje typ prvku programu, ke kterému lze anotaci přiřadit, pomocí výčtového typu java.lang.annotation.ElementType. Tento výčtový typ obsahuje konstanty ANNOTATION TYPE, CONSTRUCTOR, FIELD, LOCAL VARIABLE, METHOD, PACKAGE, PARAMETER a TYPE. Následující příklad ukazuje anotační typ TestovaciAnotace, který může být použit pouze u deklarace jiné anotace, tzn. jako metaanotace.
@Target(ElementType.ANNOTATION_TYPE) public @interface TestovaciAnotace { ... }
26
KAPITOLA 6. PŘEDDEFINOVANÉ ANOTAČNÍ TYPY
27
Anotační typ „Testÿ lze zase použít jen u deklarací metod: @Target(ElementType.METHOD) public @interface Test { ... } Jestliže je anotace použita jinde, než u prvků programu povolených metaanotací Target, vypíše překladač chybu.
6.2
@Retention
Pomocí anotačního typu Retention můžeme určit, jak dlouho bude anotace, jejíž typ je anotován metaanotací Retention, zachována. Můžeme vybrat jednu ze tří možností – RetentionPolicy.SOURCE, RetentionPolicy.CLASS, RetentionPolicy.RUNTIME. První případ znamená, že anotace označená tímto typem je brána v úvahu pouze na úrovni zdrojových kódů a již se neobjeví v binární podobě třídy. Pokud je uvedena hodnota RetentionPolicy.CLASS je anotace přítomna i v binární podobě třídy. RetentionPolicy.RUNTIME označuje anotaci, která je udržována i za běhu programu. Pokud není tato informace uvedena, je s anotací zacházeno stejně, jako kdyby měla uveden typ RetentionPolicy.CLASS. Pro čtení anotací za běhu se používají „reflexníÿ metody. Metody pro toto použití jsou obsaženy v rozhraní java.lang.reflect.AnnotatedElement. Čtení anotací pomocí reflexe ukazuje následující příklad. Vytvoříme jednoduchý anotační typ „Testÿ: import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Test { String zprava() default "ahoj"; } Třída AnotaceTest prochází svoje metody a zjišťuje, které z nich jsou anotovány anotací Test:
KAPITOLA 6. PŘEDDEFINOVANÉ ANOTAČNÍ TYPY
28
import java.lang.reflect.Method; public class AnotaceTest { public static void main(String[] args) { try { for (Method m : Class.forName("AnotaceTest").getMethods()) { if (m.isAnnotationPresent(Test.class)) { System.out.println("Metoda " +m.getName()+ " - anotace Test je přítomna"); System.out.println("Zpráva: " + m.getAnnotation(Test.class).zprava()); } } } catch (SecurityException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } @Test(zprava = "AHOJ") public void test1() { } @Test public void test2() {} public void test3() {} } Na výstupu uvidíme: Metoda test1 - anotace Test je přítomna Zpráva: AHOJ Metoda test2 - anotace Test je přítomna Zpráva: ahoj
6.3
@Inherited
Použijeme-li u určité třídy anotaci, jejíž typ je anotován metaanotací Inherited, znamená to, že potomci dané třídy anotaci dědí. V následujícím příkladu definujeme
KAPITOLA 6. PŘEDDEFINOVANÉ ANOTAČNÍ TYPY
29
anotační typ DedeniTest a k němu přidáváme metaanotaci Inherited. Následně použijeme DedeniTest u třídy Test. Třída TestPotomek anotaci zdědí. @Inherited public @interface DedeniTest { String getString() default "ahoj"; } @DedeniTest public Class Test { ... } public Class TestPotomek extends Test { ... } Dědění pomocí metaanotace Inherited lze využít u deklarací tříd a metod instancí. V jiném případě nemá použití Inherited vliv.
6.4
@Override
Metoda, která je anotovaná pomocí Override musí přepisovat metodu předka. Jestliže to tak není, vypíše překladač chybu. Předchází se tím například nechtěnému použití přetěžování metody místo přepisování metody.
6.5
@SuppressWarnings
S pomocí anotačního typu SuppressWarnings zabráníme kompilátoru v hlášení některých varování. Anotace obsahuje jeden prvek, pole řetězců, který tato varování popisuje. Má tvar @SuppressWarnings(value = R1 , ... , Rk ), kde R1,..,k jsou hlášení, která se mají vynechat. Například při použití @SuppressWarnings ("deprecation") se nebudou hlásit varování o použití zastaralých prvků Javy v programu.
KAPITOLA 6. PŘEDDEFINOVANÉ ANOTAČNÍ TYPY
6.6
30
@Deprecated
Anotační typ Deprecated se používá k označení metody, atributu či konstruktoru, jejichž použití je zastaralé. Pokud některý z prvků označený touto anotací použijeme, překladač tuto skutečnost ohlásí varováním. V specifikaci Javy [1] jsou vyjmenovány tři případy, kdy se varování týkající se použitého prvku nezobrazí: • použití zastaralého prvku je uvnitř objektu, který je sám označen jako zastaralý • označení zastaralého prvku anotací a použití tohoto prvku je ve stejné třídě • varování je zakázáno pomocí anotačního typu SuppressWarnings
6.7
@Documented
Pokud uvedeme u deklarace anotačního typu metaanotaci Documented, bude anotační typ obsažen v generované Javadoc dokumentaci. Bez jejího uvedení by se anotační typy neměly v dokumentaci objevit.
6.8
Další anotace představené v Java SE 6
Součástí uvedení JSE 6 byl souhrn několika dalších anotací v dokumentu JSR 250 – „Common Annotations for the JavaTM Platformÿ [3]. Dokument uvádí několik anotací použitelných pro platformu Java EE a několik obecněji použitelných. Anotace určené pro Java EE jsou obsaženy v balíčku javax.annotation.security, obecnější anotace v javax.annotation. Krátce si popíšeme anotace pro širší použití, tedy z balíčku javax.annotation.
6.8.1
@Generated
Anotace Generated označuje generovaný zdrojový kód. Anotace obsahuje tři položky – název generátoru kódu, datum, kdy byl kód vygenerován, a případný komentář vytvořený generátorem ke generovanému kódu. Jediná povinná položka je název generátoru. Anotaci lze použít na třídu, metodu či atribut.
KAPITOLA 6. PŘEDDEFINOVANÉ ANOTAČNÍ TYPY
6.8.2
31
@Resource
Anotace Resource se používá k deklarování odkazu na datový či jiný zdroj. Může být použit na třídu, metodu nebo atribut. Pokud je použit na atribut nebo metodu, kontejner vyvolá instanci požadovaného zdroje při inicializaci aplikace. Pokud je použit na třídu, bude se zdroj volat až při běhu aplikace. Definice anotačního typu Resource převzatá z JSR 250 [3] vypadá takto: package javax.annotation; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface Resource { public enum AuthenticationType { CONTAINER, APPLICATION } String name() default ""; Class type() default Object.class; AuthenticationType authenticationType() default AuthenticationType.CONTAINER; boolean shareable() default true; mappedName() default ""; description() default ""; } K jednotlivým položkám: • name – JNDI název zdroje • type – Java typ zdroje • authenticationType – typ autentizace, který zdroj používá • shareable – vyjadřuje, zda může být zdroj sdílen • description – popis zdroje
KAPITOLA 6. PŘEDDEFINOVANÉ ANOTAČNÍ TYPY
32
• mappedName – specifický název (závislý na produktu), na který se zdroj mapuje. Aplikační servery mohou vyžadovat mapování na názvy, které jsou jim známé. Příklad použití u atributu: @Resource(name="jdbc/test") private javax.sql.DataSource mujDS; A u metody: @Resource(name="jdbc/test") private void setMujDS(javax.sql.DataSource ds){ mujDS = ds; }; private javax.sql.DataSource mujDS; Pokud neuvedeme název a typ, vytvoří se automaticky podle typu a názvu atributu. V našem případě bude doplněn typ jako javax.sql.DataSource.class. Pokud je anotace uvedena u třídy, musí být název a typ specifikovány.
6.8.3
@Resources
Anotace resource se používá k deklarování odkazů na více datových zdrojů. Obsahuje pouze jednu položku – pole obsahující odkazy na datové zdroje v podobě anotací Resource popsaných v předchozí sekci. Například: @Resources({ @Resource(name="jdbc/test" type=javax.sql.DataSource) @Resource(name="jms/topic" type=javax.jms.TopicConnectionFactory) })
6.8.4
@PostConstruct
Anotace PostConstruct se používá pro označení metody, kterou je potřeba spustit pro vykonání určité inicializace ještě předtím, než je určitá třída dále používána. Používá se pro třídy spojené tzv. injekční závislostí (dependency injection). Anotace nemá žádné parametry. Lze ji použít pouze na jednu metodu. Na metodu označenou touto anotací jsou ve specifikaci kladena následující omezení:
KAPITOLA 6. PŘEDDEFINOVANÉ ANOTAČNÍ TYPY
33
• návratový typ je void • metoda nesmí být statická, kromě případu, kdy jde o aplikačního klienta • může být finální kromě použití u EJB • nesmí mít parametry kromě případu, kdy jde o EJB interceptor • nemůže vyhazovat kontrolované výjimky (checked exceptions) Příklad na použití je uveden v další části společně s použitím anotace PreDestroy.
6.8.5
@PreDestroy
Anotace PreDestroy se používá u metod k zachytávání signálu o začínajícím procesu zničení instance kontejnerem. Metoda je spuštěna předtím, než je zničena instance, a většinou vykonává „čistícíÿ úlohy jako je například uzavření spojení s datovým zdrojem. Anotace nemá žádné položky. Na metodu označenou touto anotací jsou kladena velmi podobná omezení jako u anotace PostConstruct, přesné vymezení viz JSR 250 [3]. Příklad použití anotací PostConstruct a PreDestroy: private Connection con; @Resource private DataSource mujDS; @PostConstruct private void createConnection(){ con = mujDS.getConnection(); } @PreDestroy private void closeConnection(){ con.close(); }
Kapitola 7 Anotace ve speciálních oblastech Javy Použití anotací se od svého vzniku již rozšířilo do několika Java technologií, ve kterých jsou úspěšně používány. Promítlo se do tvorby aplikací v J2EE, tvorby webových služeb a podobně. Tato kapitola má proto za cíl ukázat použití anotací, které byly vytvořeny právě pro účely použití v některé speciální oblasti a které by měly zjednodušit nebo alespoň zpřehlednit psaní zdrojových kódů a umožnit automatické generování konfiguračních souborů. Pro tuto kapitolu byly vybrány dva příklady týkající se oblastí: • tvorba webových služeb pomocí JAX-RPC a JAX-WS • mapování Java tříd na XML soubory pomocí JAXB První příklad ukazuje srovnání tvorby webové služby bez anotací pomocí starší specifikace JAX-RPC [8] a webové služby tvořené s anotacemi pomocí novější JAX-WS [7]. Jelikož tvorba webové služby není úplně triviálním úkolem a v této práci není možné celou tuto technologii popisovat, předpokládá se v tomto případě alespoň základní čtenářova znalost JAX-RPC, práce s některým aplikačním serverem podporujícím JAX-WS (použit je server GlassFish V2 [12]) a nástrojem Ant, který byl použit k sestavení webové aplikace. Příklad byl vybrán proto, že na něm lze vhodně ukázat ulehčení práce při použití anotací oproti dřívější verzi bez nich. Druhý příklad je pochopitelný bez znalosti JAXB a ukazuje, jak lze bez použití konfiguračních souborů jednoduše umístit potřebné informace přímo do zdrojového kódu.
34
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
7.1
35
Webové služby – JAX-WS 2.0
Jednou z oblastí, kde se anotace začaly využívat a kde by se mělo jejich použití i v budoucnu dále rozšiřovat, jsou webové služby vytvářené pomocí specifikace JAX-WS 2.0 (Java API for XML Web Services) [7], která se používá pro tvorbu webových služeb a klientů pro webové služby využívajících vzdálené volání procedur (RPC). Tato specifikace by měla nahrazovat starší JAX-RPC (Java API for XML-Based Remote Procedure Calls) [8]. Změny, kterými anotace přispěly ke zpřehlednění a zjednodušení tvorby webových služeb, si budeme demonstrovat na srovnání webové služby vytvořené pomocí staršího API JAX-RPC a stejné webové služby vytvořené pomocí JAX-WS 2.0. V příkladu vytvoříme jednoduchou webovou službu, která obsahuje dvě metody. První vrací vstupní hodnotu vyjadřující teplotu ve stupních Fahrenheita převedenou na teplotu ve stupních Celsia a druhá převádí jednotky v opačném směru. K webové službě vytvoříme i klienta. Serverová část aplikace je sestavena pomocí nástroje Ant [11] a vyzkoušena na aplikačním serveru GlassFish V2 [12]. Třídy potřebné pro klienta jsou poté vygenerovány z příkazové řádky. Následuje seznam knihoven, které jsou potřebné pro běh obou aplikací. Ke stažení jsou například na webových stránkách projektu GlassFish [12]. Předpokládá se, že jsou uloženy v jednom adresáři. Java TM API for XML Web Services (JAX-WS) 2.1: jaxws-tools.jar jaxws-rt.jar jaxws-api.jar jsr250-api.jar jsr181-api.jar jsr173_api.jar http.jar FastInfoset.jar activation.jar mail.jar sjsxp.jar JavaTM API for XML-based RPC (JAX-RPC) 1.1.3: jaxrpc-api.jar jaxrpc-impl.jar jaxrpc-spi.jar
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
36
Java TM Architecture for XML Binding (JAXB) 2.1.4: jaxb-xjc.jar jaxb-impl.jar jaxb-api.jar SOAP with Attachments API for JavaTM (SAAJ) 1.3: saaj-impl.jar saaj-api.jar
7.1.1
JAX-RPC
Začneme použitím staršího rozhraní JAX-RPC 1.1. Pro vytvoření webové služby jsou třeba dva Java soubory. Prvním je rozhraní (tzv. service endpoint interface), které musí být potomkem rozhraní java.rmi.Remote, a dále třída implementující toto rozhraní. package webservice; import java.rmi.Remote; import java.rmi.RemoteException; public interface PrevodSEI extends Remote { public double vratStupneFahrenheita(int vstup) throws RemoteException; public double vratStupneCelsia(int vstup) throws RemoteException; } package webservice; public class Prevod implements PrevodSEI { public double vratStupneFahrenheita(int vstup) throws java.rmi.RemoteException { return 1.8 * vstup + 32; } public double vratStupneCelsia(int vstup)
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
37
throws java.rmi.RemoteException { return (vstup - 32) / 1.8; } } K webové službě potřebujeme ještě dva popisné soubory web.xml a jaxrpc-ri.xml. web.xml <web-app> Aplikace pro převod stupňů <description>Aplikace pro převod stupňů Celsia na stupně Fahrenheita a naopak <session-config> <session-timeout>30 jaxrpc-ri.xml <webServices xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd" version="1.0" targetNamespaceBase="http://test/wsdl" typeNamespaceBase="http://test/types" urlPatternBase="/webservice"> <endpoint name="Prevod" displayName="Převod stupňů" description="Jednoduchá webová služba pro převod stupňů"
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
38
interface="webservice.PrevodSEI" implementation="webservice.Prevod"/> <endpointMapping endpointName="Prevod" urlPattern="/prevod"/> Použijeme následující skript build.xml pro sestavení aplikace, vygenerování potřebných souborů a zabalení do archívu WAR. Pro vygenerování potřebných popisných souborů a Java tříd a zabalení do archívu WAR, který lze spustit na serveru, použijeme úlohu wsdeploy, která je součástí JAX-RPC. Výsledný WAR soubor lze umístit na server pomocí administrační konzole aplikačního serveru nebo adresáře autodeploy. <project name="WSWithoutAnnotations" default="build" basedir="."> <property <property <property <property
name="home.dir" value="-! cesta k projektu !-" /> name="lib.dir" value="-! cesta ke knihovnám !-" /> name="target.dir" value="${home.dir}/target" /> name="build.dir" value="${home.dir}/build" />
<path id="ws.classpath"> <mkdir dir="${build.dir}/classes" /> <mkdir dir="${build.dir}/generated" />
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY <mkdir dir="${build.dir}/client" /> <mkdir dir="${target.dir}" /> <mkdir dir="${build.dir}/WEB-INF/classes" /> <javac srcdir="${home.dir}/webservice" destdir="${build.dir}/classes" includes="*.java" classpathref="ws.classpath" /> <exclude name="*.java" /> <jar jarfile="${target.dir}/prevodOld.war"> <wsdeploy classpathref="ws.classpath" tmpdir="${build.dir}/generated" outWarFile="${target.dir}/prevodOld_deploy.war" inWarFile="${target.dir}/prevodOld.war" />
39
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
40
<delete dir="${build.dir}" /> <delete dir="${target.dir}" /> Nyní vytvoříme statického klienta k aplikaci. Použijeme nástroj wscompile, který je také součástí JAX-RPC. Vytvoříme jednoduchý konfigurační soubor config.xml obsahující umístění WSDL a spustíme wscompile.bat z příkazové řádky. Vygenerují se potřebné vazební soubory pro klienta. <wsdl location="http://localhost:8080/prevodOld_deploy/prevod?WSDL" packageName="client"/> wscompile.bat -gen -s config.xml Teď už jen vytvoříme Java třídu s metodou main a zkusíme zavolat obě metody na převod stupňů: package wsclient; import client.Prevod; import client.PrevodSEI; import client.Prevod_Impl; public class PrevodClient { public static void main(String[] args) { try { Prevod prevod = new Prevod_Impl(); PrevodSEI prevodSEIPort = prevod.getPrevodSEIPort();
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
41
System.out.println(prevodSEIPort.vratStupneCelsia(25)); System.out.println(prevodSEIPort.vratStupneFahrenheita(25)); } catch (Exception ex) { ex.printStackTrace(); } } }
7.1.2
JAX-WS
Nyní si ukážeme stejný příklad s použitím rozhraní JAX-WS a anotací. Pro webovou službu již nemusíme vytvářet SEI rozhraní, stačí pouze jedna třída: package webservice; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; @WebService(name="Prevod", serviceName="SluzbaPrevod") public class Prevod { @WebMethod(operationName="PrevodNaFahrenheit") public double vratStupneFahrenheita( @WebParam(name="stupneCelsia") int vstup) throws java.rmi.RemoteException { return 1.8 * vstup + 32; } @WebMethod(operationName="PrevodNaCelsius") public double vratStupneCelsia( @WebParam(name="stupneFahrenheita") int vstup) throws java.rmi.RemoteException { return (vstup - 32) / 1.8; } } JAX-WS podporuje anotace definované v JSR 181 [9] a dále anotace z JSR 224 [7], z nichž některé se přímo nepoužívají, ale jsou generovány. Použitá ano-
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
42
tace @WebService označuje třídu, která implementuje webovou službu. Můžeme uvést název (element wsdl:portType), název služby (wsdl:service), název portu (wsdl:portName) a několik dalších informací. Rozhraní SEI nemusíme vytvářet, protože je vygenerováno. Jsou do něj přidány všechny metody označené anotací @WebMethod, pokud neobsahují atribut exclude s hodnotou true. Položka operationName označuje název operace (wsdl:operation) příslušné metodě označené anotací. Můžeme tedy přímo v kódu definovat všechny položky, které potřebujeme. Díky anotacím nemusíme vytvářet popisné soubory, protože potřebné informace jsou již uvedeny v anotacích. Pro dokončení příkladu už tedy není potřeba nic jiného než spustit obdobný Ant skript build.xml a umístit výsledný soubor na server přes administrační konzoli nebo adresář autodeploy. Místo nástroje wsdeploy využijeme nástroj wsgen, jehož výstupem není WAR soubor, ale pouze vygenerované soubory, které poté zabalíme do WAR souboru. <project name="WSWithAnnotations" default="build" basedir="."> <property <property <property <property
name="home.dir" value="-! cesta k projektu !-" /> name="lib.dir" value="-! cesta ke knihovnám !-" /> name="target.dir" value="${home.dir}/target" /> name="build.dir" value="${home.dir}/build" />
<path id="ws.classpath"> <mkdir dir="${build.dir}/classes" /> <mkdir dir="${build.dir}/generated" /> <mkdir dir="${target.dir}" /> <mkdir dir="${build.dir}/WEB-INF/classes" />
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY <javac srcdir="${home.dir}/webservice" destdir="${build.dir}/classes" includes="*.java" classpathref="ws.classpath" /> <exclude name="*.java" /> <jar jarfile="${target.dir}/prevod.war"> <wsgen classpathref="ws.classpath" classpath="${home.dir}" destdir="${build.dir}/generated" sourcedestdir="${build.dir}/classes" keep="true" debug="true" sei="webservice.Prevod" /> <delete dir="${build.dir}" /> <delete dir="${target.dir}" />
43
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
44
Soubory pro klienta opět vygenerujeme z příkazové řádky, tentokrát s použitím nástroje wsimport, kterému dáme jako parametr umístění WSDL: wsimport.bat -d http://localhost:8080/prevod/SluzbaPrevod?wsdl Java třída s metodou main, pomocí které vyzkoušíme webovou službu: package client; import javax.xml.ws.WebServiceRef; import webservice.SluzbaPrevod; import webservice.PrevodPort; public class Prevod { @WebServiceRef( wsdlLocation="http://localhost:8080/prevod/SluzbaPrevod?wsdl") public static void main(String[] args) { SluzbaPrevod service = new SluzbaPrevod(); Prevod port = service.getPrevodPort(); System.out.println(port.prevodNaCelsius(25)); System.out.println(port.prevodNaFahrenheit(25)); } } Zde vidíme použití anotace @WebServiceRef, která se používá k vytvoření reference na webovou službu. Parametr wsdlLocation odkazuje na WSDL běžící služby. Po spuštění klienta se vypíší na konzoli stejné údaje jako v předchozím případě.
7.2
Java do XML – JAXB
V dalším příkladu se podíváme na propojení Java tříd a XML, které umožňuje JAXB (JavaTM Architecture for XML Binding) [10]. Ve verzi 2.0 je možné mapovat Java
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
45
třídy na XML soubory a to je usnadněno použitím anotací, které řídí celý převod. Není tedy potřeba používat jiné soubory, které by mapování popisovaly. Anotacemi jednoduše popíšeme strukturu XML dokumentu, což si ukážeme na příkladu. JSR 222 definuje více než třicet anotací, z nichž použijeme několik základních. Požadovaný XML dokument bude popisovat zaměstnance s údaji jméno, příjmení a adresa, přičemž adresa bude složena z údajů ulice, město a PSČ. Struktura je popsána následujícími dvěma třídami: package xml; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlRootElement @XmlType(propOrder={"jmeno", "prijmeni", "adresa"}) public class Zamestnanec { private String jmeno; private String prijmeni; private Adresa adresa; public Zamestnanec() { } public Zamestnanec(String jmeno, String prijmeni, Adresa adresa) { this.jmeno = jmeno; this.prijmeni = prijmeni; this.adresa = adresa; } public Adresa getAdresa() { return adresa; } public void setAdresa(Adresa adresa) { this.adresa = adresa; } public String getJmeno() { return jmeno;
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY } public void setJmeno(String jmeno) { this.jmeno = jmeno; } public String getPrijmeni() { return prijmeni; } public void setPrijmeni(String prijmeni) { this.prijmeni = prijmeni; } } package xml; import javax.xml.bind.annotation.XmlType; @XmlType(propOrder={"ulice", "mesto", "psc"}) public class Adresa { private String ulice; private String mesto; private int psc; public Adresa() { } public Adresa(String ulice, String mesto, int psc) { this.ulice = ulice; this.mesto = mesto; this.psc = psc; } public String getMesto() { return mesto; } public void setMesto(String mesto) { this.mesto = mesto; }
46
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
47
public String getUlice() { return ulice; } public void setUlice(String ulice) { this.ulice = ulice; } public int getPsc() { return psc; } public void setPsc(int psc) { this.psc = psc; } } Anotace @XmlRootElement označuje kořenový element XML dokumentu. Může se použít na třídu nebo výčtový typ. Kód označený anotací @XmlType se mapuje na jednoduchý nebo komplexní typ XML schématu. V atributu propOrder můžeme určit pořadí elementů. Ke zpracování Java tříd použije následující kód a výsledek zapíšeme do souboru: package xml; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; public class Main { public static void main(String[] args) throws Exception { OutputStreamWriter file = new OutputStreamWriter( new FileOutputStream("c:\\Temp\\vysledek.xml"), "UTF8"); Zamestnanec zam = new Zamestnanec("Karla", "Mráčková", new Adresa("U Koberců", "Hliník", 78372)); JAXBContext context = JAXBContext.newInstance(Zamestnanec.class); Marshaller marshaller = context.createMarshaller();
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
48
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(zam, file); } } Výsledkem bude: <jmeno>Karla <prijmeni>Mráčková U Koberců <mesto>Hliník 78372 XML schéma ze zdrojových tříd můžeme vygenerovat pomocí nástroje schemagen, který je součástí JAXB, z příkazové řádky: schemagen xml\*.java S výsledkem: <xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="zamestnanec" type="zamestnanec"/> <xs:complexType name="adresa"> <xs:sequence> <xs:element name="ulice" type="xs:string" minOccurs="0"/> <xs:element name="mesto" type="xs:string" minOccurs="0"/> <xs:element name="psc" type="xs:int"/> <xs:complexType name="main"/>
KAPITOLA 7. ANOTACE VE SPECIÁLNÍCH OBLASTECH JAVY
49
<xs:complexType name="zamestnanec"> <xs:sequence> <xs:element name="jmeno" type="xs:string" minOccurs="0"/> <xs:element name="prijmeni" type="xs:string" minOccurs="0"/> <xs:element name="adresa" type="adresa" minOccurs="0"/> Další anotace, které je možné ve zdrojových kódech použít, jsou například anotace @XMLAttribute označující položku, která se mapuje na XML atribut, anotace @XmlSchemaType mapující Java typy na typy XML schématu a jiné anotace popsané v JAXB API.
Kapitola 8 Anotace a jiné nástroje 8.1
Srovnání anotací s XDocletem
Jedním z nejpoužívanějších nástrojů pro generování kódu v Javě je XDoclet [13]. XDoclet je open-source nástroj pro generování zdrojových kódů. Tato kapitola přináší srovnání XDocletu a anotací, které se soustředí hlavně na oblasti použití, snadnost použití a způsob použití obou nástrojů. Hlavní body srovnání jsou stručně uvedeny v tabulce 1, v dalším textu si je trochu rozebereme. Shrneme nejdůležitější výsledky srovnání. Anotace nabízejí obecnější použití, XDoclet se používá především pro generování kódu a konfiguračních souborů v J2EE. V této oblasti má již XDoclet propracovanou strukturu definovaných tagů pro různá prostředí (pro různé aplikační servery, frameworky apod.), ale jelikož je používán mnohem déle než anotace, které byly uvedeny do Javy později, tak se nabízené množství anotací pro použití v těchto oblastech nedá s XDocletem zatím srovnávat. Anotace mají významnou výhodu v tom, že mohou být přístupné i za běhu aplikace pomocí reflexního rozhraní. XDoclet poskytuje pouze informace pro proces sestavování aplikace, při kterém se generují další soubory. Jako výhoda anotací by se dala uvést i jejich přítomnost v definici jazyka Javy a možnost použití se standardním API Java SE (od verze 5.0). Pro použití XDocletu potřebujeme naopak doplňkové knihovny a můžeme ho použít pouze v kombinaci s nástrojem Ant, tzn. musíme umět použít další doplňkový nástroj. Srozumitelnou syntaxi a snadno pochopitelný způsob použití mají oba dva nástroje. Oba nástroje mají také dobrou rozšiřitelnost o vlastní anotace (tagy). XDoclet umožňuje editovat nebo vytvářet šablony, které obsahují zdrojový kód s vloženými XML tagy. XML tagy jsou potom při zpracování nahrazeny kódem generovaným s využitím zdrojových kódů a parametrů z námi vytvořených XDocletových tagů 50
KAPITOLA 8. ANOTACE A JINÉ NÁSTROJE
51
ve zdrojových kódech. Námi vytvořené anotace můžeme zpracovávat pomocí anotačních procesorů (Java tříd), popisovaných v kapitole 4, jejichž tvorba je trochu obtížnější, zato však můžeme při zpracování anotací vykonat libovolný Java kód. Výsledkem srovnání je, že anotace mají více výhod než XDoclet a mohly by i v budoucnu nahradit XDoclet, ovšem za předpokladu, že by postupem času nabízely použitelné anotace pro mnoho oblastí, které již XDoclet pokrývá.
Popisovaná oblast
XDoclet
Anotace
hlavní možnosti použití
původně pouze pro generování kódu nebo XML souborů pro EJB, postupně obecně pro generování kódu (XML souborů) pro J2EE přidávání dat (metadat) do zdrojových kódů JavaDoc tagy ve zdrojových kódech Apache Ant
generování kódu či jiných souborů, upozornění pro překladač, validace kódu
nutnost znalosti Antu, doplňkové knihovny snadno pochopitelná syntaxe editace nebo vytváření šablon – Java kód s XML tagy
přímo součástí Java SE (od verze 5.0) snadno pochopitelná syntaxe anotační processory – Java třídy
pouze při překladu
při překladu i za běhu aplikace (reflexe)
způsob použití umístění dat zpracování pomocí snadnost použití srozumitelnost syntaxe způsob použití vlastních tagů (anotací) přístupnost (viditelnost)
přidávání dat (metadat) do zdrojových kódů přímo v Java kódu APT nebo javac
Tabulka 1: XDoclet vs. anotace Předchozí srovnání vychází z praktických zkušeností s XDocletem verze 1.x. V nedávné době se objevila nová verze XDoclet2 [14], která se od XDocletu 1.x v některých věcech liší. XDoclet2 není zahrnut do srovnání, ale většina závěrů by měla
KAPITOLA 8. ANOTACE A JINÉ NÁSTROJE
52
být podobná. Zmíníme se pouze o tom, že umožňuje odstranit závislost na použití Antu, lze ho používat například z příkazové řádky.
8.2
Anotace versus konfigurační soubory
Některé anotace nahrazují použití konfiguračních souborů ve formátu XML či jiném. Zda je lepší použití anotací nebo konfiguračních souborů je sporné. Objevují se otázky, zda je vhodné umísťovat konfigurační data do prostého Java kódu a míchat tak do něj informace, o tom, jak se budou prvky programu překládat, umísťovat na aplikační server či spouštět. Ovšem vzhledem k tomu, že některé Java technologie vyžadují mnoho dodatkových informací ke spuštění aplikace a ty musíme rozepisovat do různých souborů, je jistě pro programátora užitečné mít vše pohromadě přímo u prvku, kterého se informace týká, a nemuset je vyhledávat na různých místech. Nevýhodou anotací je, že nemůžeme přepsat informace v popisných datech a použít je, aniž bychom museli znovu přeložit zdrojové kódy. To je ovšem ve spoustě případů ztráta pár vteřin, která by nemusela vadit. Zřejmě zůstanou příznivci jak konfiguračních souborů, tak anotací.
8.3
Metadata v jiných jazycích
Metadata ve zdrojových kódech se objevují i v jiných programovacích jazycích než je Java. Z jazyků, jejichž používání je výrazněji rozšířeno, jsou zde vybrány dva, které obsahují konstrukce podobné anotacím, a to jazyky C# a C (C++).
8.3.1
C#
Programovací jazyk C# nabízí možnost přidávat metadata do zdrojových kódů ve formě tzv. atributů. Atributy mají mnoho společných rysů s anotacemi. Obsahují data, které chceme připojit k nějakému elementu programu. Používají se, podobně jako anotace, pro instrukce pro překladač a doplňková data použitá při dalším zpracování. Atributy se umísťují do hranatých závorek a stejně jako anotace se dají přidat k různým prvkům programu - třídám, metodám, konstruktorům apod. K atributům lze také přistupovat pomocí reflexe za běhu programu. Je možné vytvářet i vlastní atributy. Jako příklad můžeme uvést často používaný atribut Serializable, který označuje třídu, kterou lze serializovat:
KAPITOLA 8. ANOTACE A JINÉ NÁSTROJE
53
[serializable] class NějakáTřída Nebo atribut Obsolete, který označuje prvek programu, který by se již neměl používat (obdoba anotace @Deprecated): [System.Obsolete("Tuto metodu nepoužívat", false)] public void ZastaralaMetoda() { } V závorce jsou uvedeny parametry atributu. První obsahuje text, který se objeví při překládání, druhý určuje, zda má překladač zobrazit pouze varování (false) nebo skočit chybou (true). Podrobnější informace o atributech lze najít v [16].
8.3.2
C/C++
Za obdobu anotací by se daly považovat direktivy preprocesoru v jazyce C/C++. Direktivy jsou instrukce pro preprocesor, které se přidávají do zdrojového kódu. Preprocesor se v jazyce C/C++ spouští před vlastním překladem a provádí úpravy textu, vkládá soubory, zaměňuje symboly apod. Direktiva se píše na jeden řádek a začíná znakem #. Direktivy mohou sloužit k definování konstant a maker, k tomu se používá direktiva #define ve tvaru: #define identifikátor hodnota Př.: #define MAX_LENGTH 20 Direktivy mohou také řídit tok kódu pomocí podmínek: #ifdef MAX_LENGTH int table[MAX_LENGTH]; #endif Kód se vykoná jen v případě, že je definována konstanta MAX LENGTH. Anotacím se nejvíce podobá direktiva #pragma. Dá se použít pro mnoho specifických instrukcí. Její použití je závislé na konkrétní platformě a překladači. Uvedeme alespoň dvě možnosti použití. S parametrem warning můžeme například zakázat, aby se zobrazily konkrétní varování překladače: #pragma warning(disable : 4507)
KAPITOLA 8. ANOTACE A JINÉ NÁSTROJE
54
Tímto se zakáže hlášení varování s číslem 4507. Můžeme také označit prvek programu, který by se již neměl používat, pomocí parametru deprecated (obdoba anotace @Deprecated a atributu Obsolete): #pragma deprecated(NejakaTrida) class NejakaTrida { public: void fce(){} }; Bližší informace k direktivám lze najít v referenci jazyka C/C++ [17] nebo v článku [19].
Kapitola 9 Závěr V práci jsou popsány hlavní oblasti použití anotací, způsob, jakým lze anotace vytvářet, některé předdefinované anotace a je uvedeno srovnání s jinými nástroji. Příklad na tvorbu webových služeb se srovnáním starší a nové specifikace naznačuje, jakým způsobem může použití anotací usnadnit tvorbu podobných aplikací. Stejně tak druhý jednoduchý příklad na převod Java tříd do XML formátu demonstruje práci s anotacemi, která je jednodušší, než při používání dalších konfiguračních souborů. Kromě nemožnosti validace anotací, zmíněné v kapitole 4 o anotačních procesorech, nebyly shledány při vytváření práce žádné výraznější nevýhody jejich používání. Při dalším vývoji anotací by měla být přidána možnost jak určit, které anotace mohou být použity společně u jednoho prvku, zda mohou být použity vícekrát v jedné třídě, u jakých typů atributů mohou být použity a podobně. Toto lze vyřešit právě přidáním validace do anotačního procesoru, nejedná se ovšem o triviální řešení. Stačilo by vytvořit více metaanotací typu @Target, která určuje, zda lze anotaci použít u třídy, metody, či atributu. Například pro označení toho, že anotace může být použita ve třídě pouze jednou, by mohla existovat metaanotace @Once apod. Na základě svých osobních zkušeností s vývojem JAVA aplikací shledávám používání anotací přínosným a bude zajímavé sledovat další vývoj v této oblasti.
55
Literatura [1] GOSLING, James, et al. The JavaTM Language Specification Third Edition [online]. http://java.sun.com/docs/books/jls/>. 13. 8. 2007 [2] JSR 175 – A Metadata Facility for the JavaTM Programming Language (Final Release) [online]. Dostupný z WWW: . 13. 8. 2007 [3] JSR 250 – Common Annotations for the JavaTM Platform (Final Release) [online]. Dostupný z WWW: . 13. 8. 2007 [4] JSR 269 – Pluggable Annotation Processing API [online]. Dostupný z WWW: . 13. 8. 2007 [5] JavaTM 2 Platform Standard Edition 5.0, API Specification [online]. Dostupný z WWW: . 13. 8. 2007 [6] Mirror API [online]. Dostupný z WWW: . 13. 8. 2007 [7] JSR 224 – JavaTM API for XML-Based Web Services (JAX-WS) 2.0 (Maintenance Release 2) [online]. Dostupný z WWW: . 13. 8. 2007 [8] JSR 101 – JavaTM APIs for XML based RPC (Maintenance Release) [online]. Dostupný z WWW: . 13. 8. 2007 [9] JSR 181 – Web Services Metadata for the JavaTM Platform (Maintenance Release) [online]. Dostupný z WWW: . 13. 8. 2007 56
[10] JSR 222 – JavaTM Architecture for XML Binding (JAXB) 2.0 (Maintenance Release) [online]. Dostupný z WWW: . 13. 8. 2007 [11] The Apache Ant Project [online]. Stránky projektu dostupné na WWW: . 13. 8. 2007 [12] Projekt GlassFish [online]. Stránky projektu dostupné na WWW: . Aplikační server dostupný na WWW: . 13. 8. 2007 [13] Projekt XDoclet [online]. Stránky projektu dostupné na WWW: . 13. 8. 2007 [14] Projekt XDoclet2 [online]. Stránky projektu dostupné na WWW: . 13. 8. 2007 [15] LI, Jason Zhicheng. Validate Java EE Annotations with Annotation Processors [online]. Dostupný z WWW: . 13. 8. 2007 [16] LIBERTY, Jesse. Programming C# - Chapter 18 Attributes and Reflection [online]. Dostupný z WWW: . 13. 8. 2007 [17] C/C++ Preprocessor Reference - Preprocessor Directives [online]. Dostupný z WWW: . 13. 8. 2007 [18] HOLMGREN, Anders. Using Annotations to add Validity Constraints to JavaBeans Properties [online]. Dostupný z WWW: . 13. 8. 2007 [19] SOULIE, Juan. Preprocessor directives [online]. Dostupný z WWW: . 13. 8. 2007
57
Terminologický slovník API
Aplikační server
APT
J2EE
JavaDoc JAX-RPC
JAX-WS
JAXB
Application Program Interface – sada funkcí či tříd obsažených v knihovně (knihovnách), které může programátor využít. API specifikuje, jakým způsobem se mají funkce knihoven používat. software, který posktytuje prostředí pro běh aplikace na cílovém zařízení a řídí komunikaci aplikace s dalšími aplikacemi či datovými zdroji. Annotation Processing Tool – nástroj používaný z příkazové řádky, který slouží ke zpracování anotací ve zdrojových Java kódech. Java 2 Platform, Enterprise Edition – platforma pro vývoj aplikací, která je využívána především pro tvorbu podnikových aplikací s vícevrstvou architekturou. nástroj pro generování dokumentace ve formátu HTML z komentářů napsaných ve zdrojových kódech. Java API for XML-Based Remote Procedure Calls – API pro tvorbu webových služeb a klientů pro webové služby, které využívá vzdálené volání procedur (RPC). Toto API je postupně nahrazováno novějším JAX-WS, které využívá anotace. Java API for XML Web Services – API pro tvorbu webových služeb a klientů pro webové služby, které využívá vzdálené volání procedur (RPC). Nahrazuje starší JAXRPC a přidává podporu anotací. Java Architecture for XML Binding – specifikace obsahující API, které umožňuje generování Java tříd z XML schémat. Od verze 2.0 je také možné generovat XML schémata z Java tříd. 58
TERMINOLOGICKÝ SLOVNÍK JNDI
RPC
XDoclet
WSDL
XML
XML schéma
Java Naming and Directory Interface – API, které umožňuje vyhledávat data a objekty v distribuovaných sítích pomocí unikátního názvu. Remote procedure call – způsob odesílání zpráv, který umožňuje aplikaci volat z jednoho počítače služby dostupné na jiných počítačích v rámci sítě. nástroj pro generování kódu pro Javu. Kód se generuje podle doplňkových informací (metadat), které jsou přidávány do JavaDoc komentářů. Web Services Description Language – používá se k popisu webové služby. Obsahuje informace o tom, jaké funkce webová služba nabízí, jakým způsobem je volat atd. Zapisuje se ve formátu XML. eXtensible Markup Language – značkovací jazyk vyvinutý a standardizovaný konsorciem W3C. Používá se k definici struktury dokumentu a publikování a výměnu dat a dokumentů. jazyk pro popis XML dokumentu, který určuje pravidla, které musí XML dokument splňovat. Při validaci oproti danému schématu se určí, zda je dokument validní nebo ne.
59