Niveau : | Intermédiaire |
Java SE 5 a introduit les annotations qui sont des métadonnées incluses dans le code source. Les annotations ont été spécifiées dans la JSR 175 : leur but est d'intégrer au langage Java des métadonnées.
Des métadonnées étaient déjà historiquement mises en oeuvre avec Java notamment avec Javadoc ou exploitées par des outils tiers notamment XDoclet : l'outil open source XDoclet propose depuis longtemps des fonctionnalités similaires aux annotations. Avant Java 5, seul l'outil Javadoc utilisait des métadonnées en standard pour générer une documentation automatique du code source.
Javadoc propose l'annotation @deprecated qui bien qu'utilisée dans les commentaires permet de marquer une méthode comme obsolète et de faire afficher un avertissement par le compilateur.
Le défaut de Javadoc est d'être trop spécifique à l'activité de génération de documentation même si le tag deprecated est aussi utilisé par le compilateur.
Depuis leur introduction dans Java 5, les annotations sont de plus en plus utilisées dans le développement d'applications avec les plate-formes Java SE et Java EE.
Ce chapitre contient plusieurs sections :
Les annotations de Java 5 apportent une standardisation des métadonnées dans un but généraliste. Ces métadonnées associés aux entités Java peuvent être exploitées à la compilation ou à l'exécution.
Java a été modifié pour permettre la mise en oeuvre des annotations :
Les annotations peuvent être utilisées avec quasiment tous les types d'entités et de membres de Java : packages, classes, interfaces, constructeurs, méthodes, champs, paramètres, variables ou annotations elles-mêmes.
Java 5 propose plusieurs annotations standard et permet la création de ses propres annotations.
Une annotation précède l'entité qu'elle concerne. Elle est désignée par un nom précédé du caractère @.
Il existe plusieurs catégories d'annotations :
Les arguments fournis en paramètres d'une annotation peuvent être de plusieurs types : les chaînes de caractères, les types primitifs, les énumérations, les annotations, le type Class.
Les annotations sont définies dans un type d'annotation. Une annotation est une instance d'un type d'annotation. Les paramètres d'une annotation peuvent avoir des valeurs par défaut.
La disponibilité d'une annotation est définie grâce à une retention policy.
Les usages des annotations sont nombreux : génération de documentations, de code, de fichiers, ORM (Object Relational Mapping), ...
Les annotations ne sont guère utiles sans un mécanisme permettant leur exploitation.
Une API est proposée pour assurer ces traitements : elle est regroupée dans les packages com.sun.mirror.apt, com.sun.mirror.declaration, com.sun.mirror.type et com.sun.mirror.util.
L'outil apt (annotation processing tool) permet un traitement des annotations personnalisées durant la phase de compilation (compile time). L'outil apt permet la génération de nouveaux fichiers mais ne permet pas de modifier le code existant.
Il est important de se souvenir que lors du traitement des annotations le code source est parcouru mais il n'est pas possible de modifier ce code.
L'API reflexion est enrichie pour permettre de traiter les annotations lors de la phase d'exécution (runtime).
Java 6 intègre deux JSR concernant les annotations :
L'Api Pluggable Annotation Processing permet d'intégrer le traitement des annotations dans le processus de compilation du compilateur Java ce qui évite d'avoir à utiliser apt.
Les annotations vont évoluer dans la plate-forme Java notamment au travers de plusieurs JSR qui sont en cours de définition :
Les annotations fournissent des informations sur des entités : elles n'ont pas d'effets directs sur les entités qu'elles concernent.
Les annotations utilisent leur propre syntaxe. Une annotation s'utilise avec le caractère @ suivi du nom de l'annotation : elle doit obligatoirement précéder l'entité qu'elle annote. Par convention, les annotations s'utilisent sur une ligne dédiée.
Les annotations peuvent s'utiliser sur les packages, les classes, les interfaces, les méthodes, les constructeurs et les paramètres de méthodes.
Exemple : |
@Override
public void maMethode() {
}
Une annotation peut avoir un ou plusieurs attributs : ceux-ci sont précisés entre parenthèses, séparés par une virgule. Un attribut est de la forme clé=valeur.
Exemple : |
@SuppressWarnings(value = "unchecked")
void maMethode() { }
Lorsque l'annotation ne possède qu'un seul attribut, il est possible d'omettre son nom.
Exemple : |
@SuppressWarnings("unchecked")
void maMethode() { }
Un attribut peut être de type tableau : dans ce cas, les différentes valeurs sont fournies entre accolades, chaque valeur placée entre guillemets et séparée de la suivante par une virgule.
Exemple : |
@SuppressWarnings(value={"unchecked", "deprecation"})
Le tableau peut contenir des annotations.
Exemple : |
@TODOItems({
@Todo(importance = Importance.MAJEUR,
description = "Ajouter le traitement des erreurs",
assigneA = "JMD",
dateAssignation = "07-11-2007"),
@Todo(importance = Importance.MINEURE,
description = "Changer la couleur de fond",
assigneA = "JMD",
dateAssignation = "13-12-2007")
})
Les annotations prennent une place de plus en plus importante dans la plate-forme Java et dans de nombreuses API open source.
Les utilisations des annotations concernent plusieurs fonctionnalités :
Les annotations peuvent être mises en oeuvre pour permettre la génération de documentations indépendantes de JavaDoc : listes de choses à faire, de services ou de composants, ...
Il peut par exemple être pratique de rassembler certaines informations mises sous la forme de commentaires dans des annotations pour permettre leur traitement.
Par exemple, il est possible de définir une annotation qui va contenir les métadonnées relatives aux informations sur une classe. Traditionnellement, une classe débute par un commentaire d'en-tête qui contient des informations sur l'auteur, la date de création, les modifications, ... L'idée est de fournir ces informations dans une annotation dédiée. L'avantage est de facilement extraire et manipuler ces données qui ne seraient qu'informatives sous leur forme de commentaires.
Les trois annotations fournies en standard avec la plate-forme entrent dans cette catégorie qui consiste à faire réaliser par le compilateur quelques contrôles basiques.
Les annotations sont particulièrement adaptées à la génération de code source afin de faciliter le travail des développeurs notamment sur des tâches répétitives.
Attention, le traitement des annotations ne peut pas modifier le code existant mais simplement créer de nouveaux fichiers sources.
Les API standard ou les frameworks open source nécessitent fréquemment l'utilisation de fichiers de configuration ou de déploiement généralement au format XML.
Les annotations peuvent proposer une solution pour maintenir le contenu de ces fichiers par rapport aux entités incluses dans le code de l'application.
La version 5 de Java EE fait un important usage des annotations dans le but de simplifier les développements de certains composants notamment les EJB, les entités et les services web. Pour cela, l'utilisation de descripteurs est remplacée par l'utilisation d'annotations ce qui rend le code plus facile à développer et plus clair.
De nombreuses API standard utilisent les annotations depuis leur intégration dans Java notamment :
De nombreuses API open source utilisent aussi les annotations notamment JUnit, TestNG, Hibernate, ...
Java 5 propose plusieurs annotations standard.
Cette annotation a un rôle similaire au tag de même nom de Javadoc.
C'est un marqueur qui précise que l'entité concernée est obsolète et qu'il ne faudrait plus l'utiliser. Elle peut être utilisée avec une classe, une interface ou un membre (méthode ou champ)
Exemple : |
public class TestDeprecated {
public static void main(String[] args) {
MaSousClasse td = new MaSousClasse();
td.maMethode();
}
}
@Deprecated
class MaSousClasse {
/**
* Afficher un message de test
* @deprecated methode non compatible
*/
@Deprecated
public void maMethode() {
System.out.println("test");
}
}
Les entités marquées avec l'annotation @Deprecated devraient être documentées avec le tag @deprecated de Javadoc en lui fournissant la raison de l'obsolescence et éventuellement l'entité de substitution.
Il est important de tenir compte de la casse : @Deprecated pour l'annotation et @deprecated pour Javadoc.
Lors de la compilation, le compilateur donne une information si une entité obsolète est utilisée.
Exemple : |
C:\Documents and Settings\jmd\workspace\Tests>javac TestDeprecated.java
Note: TestDeprecated.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
L'option -Xlint :deprecation permet d'afficher le détail sur les utilisations obsolètes.
Exemple : |
C:\Documents and Settings\jmd\workspace\Tests>javac -Xlint:deprecation TestDepre
cated.java
TestDeprecated.java:7: warning: [deprecation] MaSousClasse in unnamed package ha
s been deprecated
MaSousClasse td = new MaSousClasse();
^
TestDeprecated.java:7: warning: [deprecation] MaSousClasse in unnamed package ha
s been deprecated
MaSousClasse td = new MaSousClasse();
^
TestDeprecated.java:8: warning: [deprecation] maMethode() in MaSousClasse has be
en deprecated
td.maMethode();
^
3 warnings
Il est aussi possible d'utiliser l'option -deprecation de l'outil javac.
Cette annotation est un marqueur utilisé par le compilateur pour vérifier la réécriture de méthodes héritées.
@Override s'utilise pour annoter une méthode qui est une réécriture d'une méthode héritée. Le compilateur lève une erreur si aucune méthode héritée ne correspond.
Exemple : |
@Override
public void maMethode() {
}
Son utilisation n'est pas obligatoire mais recommandée car elle permet de détecter certains problèmes.
Exemple : |
public class MaClasseMere {
}
class MaClasse extends MaClasseMere {
@Override
public void maMethode() {
}
}
Ceci est particulièrement utile pour éviter des erreurs de saisie dans le nom des méthodes à redéfinir.
Exemple : |
public class TestOverride {
private String nom;
private long id;
public int hasCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + (int) (id ^ (id >>> 32));
result = PRIME * result + ((nom == null) ? 0 : nom.hashCode());
return result;
}
}
Dans l'exemple ci-dessous, le développeur souhaitait redéfinir la méthode hashCode() mais suite à une faute frappe a simplement défini une nouvelle méthode nommée hasCode(). Cette classe se compile parfaitement mais elle comporte une erreur qui est signalée en utilisant l'annotation @Override.
Exemple : |
public class TestOverride {
private String nom;
private long id;
@Override
public int hasCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + (int) (id ^ (id >>> 32));
result = PRIME * result + ((nom == null) ? 0 : nom.hashCode());
return result;
}
}
Résultat : |
C:\Documents and Settings\jmd\workspace\Tests>javac TestOverride.java
TestOverride.java:6: method does not override or implement a method from a super
type
@Override
^
1 error
L'annotation @SuppressWarning permet de demander au compilateur d'inhiber certains avertissements qui sont pris en compte par défaut.
La liste des avertissements utilisables dépend du compilateur. Un avertissement utilisé dans l'annotation non reconnu par le compilateur ne provoque par d'erreur mais éventuellement un avertissement.
Le compilateur fourni avec le JDK supporte les avertissements suivants :
Nom |
Rôle |
deprecation |
Vérification de l'utilisation d'entités déclarées deprecated |
unchecked |
Vérification de l'utilisation des generics |
fallthrough |
Vérification de l'utilisation de l'instruction break dans les cases des instructions switch |
path |
Vérification des chemins fournis en paramètre du compilateur |
serial |
Vérification de la définition de la variable serialVersionUID dans les beans |
finally |
Vérification de l'absence d'instruction return dans une clause finally |
Il est possible de passer en paramètres plusieurs types d'avertissements sous la forme d'un tableau
Exemple : |
@SuppressWarnings(value={"unchecked", "fallthrough"})
L'exemple ci-dessous génère un avertissement à la compilation
Exemple : |
import java.util.ArrayList;
import java.util.List;
public class TestSuppresWarning {
public static void main(String[] args) {
List donnees = new ArrayList();
donnees.add("valeur1");
}
}
Résultat : |
C:\Documents and Settings\jmd\workspace\Tests>javac TestSuppresWarning.java
Note: TestSuppresWarning.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
L'option -Xlint :unchecked permet d'obtenir des détails
Exemple : |
C:\Documents and Settings\jmd\workspace\Tests>javac -Xlint:unchecked TestSuppres
Warning.java
TestSuppresWarning.java:8: warning: [unchecked] unchecked call to add(E) as a me
mber of the raw type java.util.List
donnees.add("valeur1");
^
1 warning
Pour supprimer cet avertissement, il faut utiliser les générics dans la déclaration de la collection ou utiliser l'annotation SuppressWarning.
Exemple : |
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("unchecked")
public class TestSuppresWarning {
public static void main(String[] args) {
List donnees = new ArrayList();
donnees.add("valeur1");
}
}
Il n'est pas recommandé d'utiliser cette annotation mais plutôt d'apporter une solution à l'avertissement.
Les annotations communes sont définies par la JSR 250 et sont intégrées dans Java 6. Leur but est de définir des annotations couramment utilisées et ainsi d'éviter leur redéfinition pour chaque outil qui en aurait besoin.
Les annotations définies concernent :
De plus en plus d'outils ou de frameworks génèrent du code source pour faciliter la tâche des développeurs notamment pour des portions de code répétitives ayant peu de valeur ajoutée.
Le code ainsi généré peut être marqué avec l'annotation @Generated.
Exemple : |
@Generated(
value = "entite.qui.a.genere.le.code",
comments = "commentaires",
date = "12 April 2008"
)
public void toolGeneratedCode(){
}
L'attribut obligatoire value permet de préciser l'outil à l'origine de la génération
Les attributs facultatifs comments et date permettent respectivement de fournir un commentaire et la date de génération.
Cette annotation peut être utilisée sur toutes les déclarations d'entités.
L'annotation @Resource définit une ressource requise par une classe. Typiquement, une ressource est par exemple un composant Java EE de type EJB ou JMS.
L'annotation @Resource possède plusieurs attributs :
Attribut |
Description |
authentificationType |
Type d'authentification pour utiliser la ressource (Resource.AuthenticationType.CONTAINER ou Resource.AuthenticationType.APPLICATION) |
description |
Description de la ressource |
mappedName |
Nom de la ressource spécifique au serveur utilisé (non portable) |
name |
Nom JNDI de la ressource |
shareable |
Booléen qui précise si la ressource est partagée |
type |
Le type pleinement qualifié de la ressource |
Cette annotation peut être utilisée sur une classe, un champ ou une méthode.
Lorsque l'annotation est utilisée sur une classe, elle correspond simplement à une déclaration des ressources qui seront requises à l'exécution.
Lorsque l'annotation est utilisée sur un champ ou une méthode, le serveur d'applications va injecter une référence sur la ressource correspondante. Pour cela, lors du chargement d'une application par le serveur d'applications, celui-ci recherche les annotations @Resource afin d'assigner une instance de la ressource correspondante.
Exemple : |
@Resource(name="MaQueue",
type = "javax.jms.Queue",
shareable=false,
authenticationType=Resource.AuthenticationType.CONTAINER,
description="Queue de test"
)
private javax.jms.Queue maQueue;
L'annotation @Resources est simplement une collection d'annotation de type @Resource.
Exemple : |
@Resources({
@Resource(name = "maQueue" type = javax.jms.Queue),
@Resource(name = "monTopic" type = javax.jms.Topic),
})
Les annotations @PostConstruct et @PreDestroy permettent respectivement de désigner des méthodes qui seront exécutées après l'instanciation d'un objet et avant la destruction d'une instance.
Ces deux annotations ne peuvent être utilisées que sur des méthodes.
Ces annotations sont par exemple utiles dans Java EE car généralement un composant géré par le conteneur est instancié en utilisant le constructeur sans paramètre. Une méthode marquée avec l'annotation @PostConstruct peut alors être exécutée juste après l'appel au constructeur.
Une telle méthode doit respecter certaines règles :
L'annotation @PostConstruct est utilisée en général sur une méthode qui initialise des ressources en fonction du contexte.
Dans une même classe, chacune de ces annotations n'est utilisable que par une seule méthode.
Java propose la possibilité de définir ses propres annotations. Pour cela, le langage possède un type dédié : le type d'annotation (annotation type).
Un type d'annotation est similaire à une classe et une annotation est similaire à une instance de classe.
Sur la plate-forme Java, une annotation est une interface lors de sa déclaration et est une instance d'une classe qui implémente cette interface lors de son utilisation.
La définition d'une annotation nécessite une syntaxe particulière utilisant le mot clé @interface. Une annotation se déclare donc de façon similaire à une interface.
Exemple : le fichier MonAnnotation.java |
package com.jmdoudoux.test.annotations;
public @interface MonAnnotation {
}
Une fois compilée, cette annotation peut être utilisée dans le code. Pour utiliser une annotation, il faut importer l'annotation et l'appeler dans le code en la faisant précéder du caractère @.
Exemple : |
package com.jmdoudoux.test.annotations;
@MonAnnotation
public class MaCLasse {
}
Si l'annotation est définie dans un autre package, il faut utiliser la syntaxe pleinement qualifiée du nom de l'annotation ou ajouter une clause import pour le package.
Il est possible d'ajouter des membres à l'annotation simplement en définissant une méthode dont le nom correspond au nom de l'attribut en paramètre de l'annotation.
Exemple : |
package com.jmdoudoux.test.annotations;
public @interface MonAnnotation {
String arg1();
String arg2();
}
package com.jmdoudoux.test.annotations;
@MonAnnotation(arg1="valeur1", arg2="valeur2")
public class MaCLasse {
}
Les types utilisables sont les chaînes de caractères, les types primitifs, les énumérations, les annotations, les chaînes de caractères, le type Class.
Il est possible de définir un membre comme étant un tableau à une seule dimension d'un des types utilisables.
Exemple : |
package com.jmdoudoux.tests.annotations
public @interface MonAnnotation {
String arg1();
String[] arg2();
String arg3();
}
Il est possible de définir une valeur par défaut, ce qui rend l'indication du membre optionnelle. Cette valeur est précisée en la faisant précéder du mot clé default.
Exemple : |
package com.jmdoudoux.test.annotations;
public @interface MonAnnotation {
String arg1() default "";
String[] arg2();
String arg3();
}
La valeur par défaut d'un tableau utilise une syntaxe raccourcie.
Exemple : |
package com.jmdoudoux.tests.annotations
public @interface MonAnnotation {
String arg1();
String[] arg2() default {"chaine1", "chaine2" };
String arg3();
}
Il est possible de définir une énumération comme type pour un attribut
Exemple : |
package com.jmdoudoux.test.annotations;
public @interface MonAnnotation {
public enum Niveau {DEBUTANT, CONFIRME, EXPERT} ;
String arg1() default "";
String[] arg2();
String arg3();
Niveau niveau() default Niveau.DEBUTANT;
}
La version 5 de Java propose quatre annotations dédiées aux types d'annotations qui permettent de fournir des informations sur l'utilisation.
Ces annotations sont définies dans le package java.lang.annotation
L'annotation @Target permet de préciser les entités sur lesquelles l'annotation sera utilisable. Cette annotation attend comme valeur un tableau de valeurs issues de l'énumération ElementType.
Valeur de l'énumération |
Rôle |
ANNOTATION_TYPE |
Types d'annotation |
CONSTRUCTOR |
Constructeurs |
LOCAL_VARIABLE |
Variables locales |
FIELD |
Champs |
METHOD |
Méthodes hors constructeurs |
PACKAGE |
Packages |
PARAMETER |
Paramètres d'une méthode ou d'un constructeur |
TYPE |
Classes, interfaces, énumérations, types d'annotation |
Si une annotation est utilisée sur une entité non précisée par l'annotation, alors une erreur est émise lors de la compilation.
Exemple : |
package com.jmdoudoux.test.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR })
public @interface MonAnnotation {
String arg1() default "";
String arg2();
}
package com.jmdoudoux.test.annotations;
@MonAnnotation(arg1="valeur1", arg2="valeur2")
public class MaCLasse {
}
Résultat de la compilation : |
C:\Documents and Settings\jmd\workspace\Tests>javac com/jmdoudoux/test/annotatio
ns/MaClasse.java
com\jmdoudoux\test\annotations\MaClasse.java:3: annotation type not applicable t
o this kind of declaration
@MonAnnotation(arg1="valeur1", arg2="valeur2")
^
1 error
Cette annotation permet de préciser à quel niveau les informations concernant l'annotation seront conservées. Cette annotation attend comme valeur un élément de l'énumération RetentionPolicy.
Enumération |
Rôle |
RetentionPolicy.SOURCE |
informations conservées dans le code source uniquement (fichier .java) : le compilateur les ignore |
RetentionPolicy.CLASS |
informations conservées dans le code source et le bytecode (fichiers .java et .class) |
RetentionPolicy.RUNTIME |
informations conservées dans le code source et le bytecode : elles sont disponibles à l'exécution par introspection |
Cette annotation permet de déterminer de quelle façon l'annotation pourra être exploitée.
Exemple : |
@Retention(RetentionPolicy.RUNTIME)
L'annotation @Documented permet de demander l'intégration de l'annotation dans la documentation générée par Javadoc.
Par défaut, les annotations ne sont pas intégrées dans la documentation des classes annotées.
Exemple : |
package com.jmdoudoux.test.annotations;
import java.lang.annotation.Documented;
@Documented
public @interface MonAnnotation {
String arg1() default "";
String arg2();
}
L'annotation @Inherited permet de demander l'héritage d'une annotation aux classes filles de la classe mère sur laquelle elle s'applique.
Si une classe mère est annotée avec une annotation elle-même annotée avec @Inherited alors toutes les classes filles sont automatiquement annotées avec cette annotation.
Pour être profitables, les annotations ajoutées dans le code source doivent être exploitées par un ou plusieurs outils.
La déclaration et l'utilisation d'annotations sont relativement simples par contre leur exploitation pour permettre la production de fichiers est moins triviale.
Cette exploitation peut se faire de plusieurs manières
Pour des traitements simples, il est possible de définir un Doclet et de le traiter avec l'outil Javadoc pour utiliser les annotations.
L'API Doclet est définit dans le package com.sun.javadoc. Ce package est dans le fichier tools.jar fourni avec le JDK.
L'API Doclet définit des interfaces pour chaque entité pouvant être utilisée dans le code source.
La méthode annotation() de l'interface ProgramElementDoc permet d'obtenir un tableau de type AnnotationDesc.
L'interface AnnotationDesc représente une annotation. Elle définit deux méthodes :
Méthode |
Rôle |
AnnotationTypeDoc annotationType() |
Renvoyer le type d'annotation |
AnnotationDesc.ElementValuePair[] elementValues() |
Renvoyer les éléments de l'annotation |
L'interface AnnotationTypeDoc représente un type d'annotation. Elle ne définit qu'une seule méthode :
Méthode |
Rôle |
AnnotationTypeElementDoc[] elements() |
Renvoyer les éléments d'un type d'annotation |
L'interface AnnotationTypeElementDoc représente un élément d'un type d'annotation. Elle ne définit qu'une seule méthode :
Méthode |
Rôle |
AnnotationValue defaultValue() |
Renvoyer la valeur par défaut de l'élément d'un type d'annotation |
L'interface AnnotationValue représente la valeur d'un élément d'un type d'annotation. Elle définit deux méthodes :
Méthode |
Rôle |
String toString() |
Renvoyer la valeur sous forme d'une chaîne de caractères |
Object value() |
Renvoyer la valeur |
Pour créer un Doclet, il faut définir une classe qui contienne une méthode ayant pour signature public static boolean start (RootDoc rootDoc).
Pour utiliser le Doclet, il faut compiler la classe qui l'encapsule et utiliser l'outil javadoc avec l'option -doclet suivi du nom de la classe.
La version 5 du JDK fournit l'outil apt pour le traitement des annotations.
L'outil apt qui signifie annotation processing tool est l'outil le plus polyvalent en Java 5 pour exploiter les annotations.
Apt assure la compilation des classes et permet en simultané le traitement des annotations par des processeurs d'annotations créés par le développeur.
Les processeurs d'annotations peuvent générer de nouveaux fichiers sources, pouvant eux-mêmes contenir des annotations. Apt traite alors récursivement les fichiers générés jusqu'à ce qu'il n'y ait plus d'annotation à traiter et de classe à compiler.
Cette section va créer un processeur pour l'annotation personnalisée Todo
Exemple : l'annotation personnalisée Todo |
package com.jmdoudoux.test.annotations;
import java.lang.annotation.Documented;
@Documented
public @interface Todo {
public enum Importance {
MINEURE, IMPORTANT, MAJEUR, CRITIQUE
};
Importance importance() default Importance.MINEURE;
String[] description();
String assigneA();
String dateAssignation();
}
Apt et l'API à utiliser de concert ne sont disponibles qu'avec le JDK : ils ne sont pas fournis avec le JRE.
Les packages de l'API sont dans le fichier lib/tools.jar du JDK : cette bibliothèque doit donc être ajoutée au classpath lors de la mise en oeuvre d'apt.
L'API est composée de deux grandes parties :
L'API est contenue dans plusieurs sous-packages de com.sun.mirror notamment :
Un processeur d'annotations est une classe qui implémente l'interface com.sun.mirror.apt.AnnotationProcessor. Cette interface ne définit qu'une seule méthode process() qui va contenir les traitements à réaliser pour une annotation.
Il faut fournir un constructeur qui attend en paramètre un objet de type com.sun.mirror.apt.AnnotationProcessorEnvironment : ce constructeur sera appelé par une fabrique pour en créer une instance.
L'interface AnnotationProcessorEnvironment fournit des méthodes pour obtenir des informations sur l'environnement d'exécution des traitements des annotations et créer de nouveaux fichiers pendant les traitements.
L'interface Declaration permet d'obtenir des informations sur une entité :
Méthode |
Rôle |
<A extends Annotation> getAnnotation(Class<A> annotationType) |
Renvoie une annotation d'un certain type associée à l'entité |
Collection<AnnotationMirror> getAnnotationMirrors() |
Renvoie les annotations associées à l'entité |
String getDocComment() |
Renvoie le texte des commentaires de documentations Javadoc associés à l'entité |
Collection<Modifier> getModifiers() |
Renvoie les modificateurs de l'entité |
SourcePosition getPosition() |
Renvoie la position de la déclaration dans le code source |
String getSimpleName() |
Renvoie le nom de la déclaration |
De nombreuses interfaces héritent de l'interface Declaration : AnnotationTypeDeclaration, AnnotationTypeElementDeclaration, ClassDeclaration, ConstructorDeclaration, EnumConstantDeclaration, EnumDeclaration, ExecutableDeclaration, FieldDeclaration, InterfaceDeclaration, MemberDeclaration, MethodDeclaration, PackageDeclaration, ParameterDeclaration, TypeDeclaration, TypeParameterDeclaration
Chacune de ces interfaces propose des méthodes pour obtenir des informations sur la déclaration et sur le type concernés.
L'interface TypeMirror permet d'obtenir des informations sur un type utilisé dans une déclaration.
De nombreuses interfaces héritent de l'interface TypeMirror : AnnotationType, ArrayType, ClassType, DeclaredType, EnumType, InterfaceType, PrimitiveType, ReferenceType, TypeVariable, VoidType, WildcardType.
La classe com.sun.mirror.util.DeclarationFilter permet de définir un filtre des entités annotées avec les annotations concernées par les traitements du processeur. Il suffit de créer une instance de cette classe en ayant redéfini sa méthode match(). Cette méthode renvoie un booléen qui précise si l'entité fournie en paramètre sous la forme d'un objet de type Declaration est annotée avec une des annotations concernées par le processeur.
Exemple : |
package com.jmdoudoux.test.annotations.outils;
import java.util.Collection;
import com.jmdoudoux.test.annotations.Todo;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.util.DeclarationFilter;
public class TodoAnnotationProcessor implements AnnotationProcessor {
private final AnnotationProcessorEnvironment env;
public TodoAnnotationProcessor(AnnotationProcessorEnvironment env) {
this.env = env;
}
public void process() {
// Création d'un filtre pour ne retenir que les déclarations annotées avec Todo
DeclarationFilter annFilter = new DeclarationFilter() {
public boolean matches(
Declaration d) {
return d.getAnnotation(Todo.class) != null;
}
};
// Recherche des entités annotées avec Todo
Collection<TypeDeclaration> types = annFilter.filter(env.getSpecifiedTypeDeclarations());
for (TypeDeclaration typeDecl : types) {
System.out.println("class name: " + typeDecl.getSimpleName());
Todo todo = typeDecl.getAnnotation(Todo.class);
System.out.println("description : ");
for (String desc : todo.description()) {
System.out.println(desc);
}
System.out.println("");
}
}
}
Il faut créer une fabrique de processeurs d'annotations : cette fabrique est en charge d'instancier des processeurs d'annotations pour un ou plusieurs types d'annotations. La fabrique doit implémenter l'interface com.sun.mirror.apt.AnnotationProcessorFactory.
L'interface AnnotationProcessorFactory déclare trois méthodes :
Méthode |
Rôle |
AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env) |
Renvoyer un processeur d'annotations pour l'ensemble de types d'annotations fournis en paramètres. |
Collection<String> supportedAnnotationTypes() |
Renvoyer une collection des types d'annotations dont un processeur peut être instancié par la fabrique |
Collection<String> supportedOptions() |
Renvoyer une collection des options supportées par la fabrique ou par les processeurs d'annotations créés par la fabrique |
Exemple : |
package com.jmdoudoux.test.annotations.outils;
import com.sun.mirror.apt.*;
import com.sun.mirror.declaration.*;
import java.util.Collection;
import java.util.Set;
import java.util.Collections;
import java.util.Arrays;
public class TodoAnnotationProcessorFactory implements AnnotationProcessorFactory {
private static final Collection<String> supportedAnnotations =
Collections.unmodifiableCollection(Arrays
.asList("com.jmdoudoux.test.annotations.Todo"));
private static final Collection<String> supportedOptions = Collections.emptySet();
public Collection<String> supportedOptions() {
return supportedOptions;
}
public Collection<String> supportedAnnotationTypes() {
return supportedAnnotations;
}
public AnnotationProcessor getProcessorFor(
Set<AnnotationTypeDeclaration> atds,
AnnotationProcessorEnvironment env) {
return new TodoAnnotationProcessor(env);
}
}
Dans l'exemple ci-dessus, aucune option n'est supportée et la fabrique ne prend en charge que l'annotation personnalisée Todo.
Pour mettre en oeuvre les traitements des annotations, il faut que le code source utilise ces annotations.
Exemple : une classe annotée avec l'annotation Todo |
package com.jmdoudoux.test;
import com.jmdoudoux.test.annotations.Todo;
import com.jmdoudoux.test.annotations.Todo.Importance;
@Todo(importance = Importance.CRITIQUE,
description = "Corriger le bug dans le calcul",
assigneA = "JMD",
dateAssignation = "11-11-2007")
public class MaClasse {
}
Exemple : une autre classe annotée avec l'annotation Todo |
package com.jmdoudoux.test;
import com.jmdoudoux.test.annotations.Todo;
import com.jmdoudoux.test.annotations.Todo.Importance;
@Todo(importance = Importance.MAJEUR,
description = "Ajouter le traitement des erreurs",
assigneA = "JMD",
dateAssignation = "07-11-2007")
public class MaClasse1 {
}
Pour utiliser apt, il faut que le classpath contienne la bibliothèque tools.jar fournie avec le JDK et les classes de traitements des annotations (fabrique et processeur d'annotations).
L'option -factory permet de préciser la fabrique à utiliser.
Résultat de l'exécution d'apt |
C:\Documents and Settings\jmd\workspace\Tests>apt -cp ".;./bin;C:/Program Files/
Java/jdk1.5.0_07/lib/tools.jar" -factory com.jmdoudoux.test.annotations.outils.T
odoAnnotationProcessorFactory com/jmdoudoux/test/*.java
class name: MaClasse
description :
Corriger le bug dans le calcul
class name: MaClasse1
description :
Ajouter le traitement des erreurs
A partir de l'objet de type AnnotationProcessorEnvironment, il est possible d'obtenir un objet de type com.sun.miror.apt.Filer qui encapsule un nouveau fichier créé par le processeur d'annotations.
L'interface Filer propose quatre méthodes pour créer différents types de fichiers :
Méthode |
Rôle |
OutputStream createBinaryFile(Filer.Location loc, String pkg, File relPath) |
Créer un nouveau fichier binaire et renvoyer un objet de type Stream pour écrire son contenu |
OutputStream createClassFile(String name) |
Créer un nouveau fichier .class et renvoyer un objet de type Stream pour écrire son contenu |
PrintWriter createSourceFile(String name) |
Créer un nouveau fichier texte contenant du code source et renvoyer un objet de type Writer pour écrire son contenu |
PrintWriter createTextFile(Filer.Location loc, String pkg, File relPath, String charsetName) |
Créer un nouveau fichier texte et renvoyer un objet de type Writer pour écrire son contenu |
L'énumération Filter.Location permet de préciser si le nouveau fichier est créé dans la branche source (SOURCE_TREE) ou dans la branche compilée (CLASS_TREE).
Exemple : |
package com.jmdoudoux.test.annotations.outils;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import com.jmdoudoux.test.annotations.Todo;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.apt.Filer;
import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.util.DeclarationFilter;
public class TodoAnnotationProcessor implements AnnotationProcessor {
private final AnnotationProcessorEnvironment env;
public TodoAnnotationProcessor(AnnotationProcessorEnvironment env) {
this.env = env;
}
public void process() {
// Création d'un filtre pour ne retenir que les déclarations annotées avec
// Todo
DeclarationFilter annFilter = new DeclarationFilter() {
public boolean matches(
Declaration d) {
return d.getAnnotation(Todo.class) != null;
}
};
Filer f = this.env.getFiler();
PrintWriter out;
try {
out = f.createTextFile(Filer.Location.SOURCE_TREE, "", new File("todo.txt"), null);
// Recherche des entités annotées avec Todo
Collection<TypeDeclaration> types = annFilter.filter(env.getSpecifiedTypeDeclarations());
for (TypeDeclaration typeDecl : types) {
out.println("class name: " + typeDecl.getSimpleName());
Todo todo = typeDecl.getAnnotation(Todo.class);
out.println("description : ");
for (String desc : todo.description()) {
out.println(desc);
}
out.println("");
}
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Résultat de l'exécution |
C:\Documents and Settings\jm\workspace\Tests>apt -cp ".;./bin;C:/Program Files/
Java/jdk1.5.0_07/lib/tools.jar" -factory com.jmdoudoux.test.annotations.outils.T
odoAnnotationProcessorFactory com/jmdoudoux/test/*.java
C:\Documents and Settings\jm\workspace\Tests>dir
Volume in drive C has no label.
Volume Serial Number is 1D31-4F67
Directory of C:\Documents and Settings\jmd\workspace\Tests
19/11/2007 08:39 <DIR> .
19/11/2007 08:39 <DIR> ..
16/11/2007 08:15 433 .classpath
31/10/2006 14:06 381 .project
14/09/2007 12:45 <DIR> .settings
16/11/2007 08:15 <DIR> bin
02/10/2007 15:22 854 build.xml
29/06/2007 07:12 <DIR> com
15/11/2007 13:01 <DIR> doc
19/11/2007 08:39 148 todo.txt
8 File(s) 1 812 bytes
6 Dir(s) 66 885 595 136 bytes free
C:\Documents and Settings\jm\workspace\Tests>type todo.txt
class name: MaClasse
description :
Corriger le bug dans le calcul
class name: MaClasse1
description :
Ajouter le traitement des erreurs
Concernant les entités à traiter, l'API Mirror fournit de nombreuses autres fonctionnalités qui permettent de rendre très riche le traitement des annotations. Parmi ces fonctionnalités, il y a le parcours des sources par des classes mettant en oeuvre le motif de conception visiteur.
Pour qu'une annotation soit exploitée à l'exécution, il est nécessaire qu'elle soit annotée avec une RetentionPolicy à la valeur RUNTIME.
Exemple : |
package com.jmdoudoux.test.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Todo {
public enum Importance {
MINEURE, IMPORTANT, MAJEUR, CRITIQUE
};
Importance importance() default Importance.MINEURE;
String[] description();
String assigneA();
String dateAssignation();
}
L'interface java.lang.reflect.AnnotatedElement définit les méthodes pour le traitement des annotations par introspection :
Méthode |
Rôle |
<T extends Annotation> getAnnotation(Class<T>) |
Renvoyer l'annotation si le type fourni en paramètre est utilisé sur l'entité, sinon null |
Annotation[] getAnnotations() |
Renvoyer un tableau de toutes les annotations de l'entité. Renvoie un tableau vide si aucune annotation n'est concernée |
Annotation[] getDeclaredAnnotations() |
Renvoyer un tableau des annotations directement associées à l'entité (en ignorant donc les annotations héritées). Renvoie un tableau vide si aucune annotation n'est concernée |
boolean isAnnotationPresent(Class< ? extends Annotation>) |
Renvoyer true si l'annotation dont le type est fourni en paramètre est utilisé sur l'entité. Cette méthode est particulièrement utile dans le traitement des annotations de type marqueur. |
Plusieurs classes du package java.lang implémentent l'interface AnnotatedElement : AccessibleObject, Class, Constructor, Field, Method et Package
Exemple : |
package com.jmdoudoux.test;
import java.lang.reflect.Method;
import com.jmdoudoux.test.annotations.Todo;
import com.jmdoudoux.test.annotations.Todo.Importance;
@Todo(importance = Importance.CRITIQUE,
description = "Corriger le bug dans le calcul",
assigneA = "JMD",
dateAssignation = "11-11-2007")
public class TestInstrospectionAnnotation {
public static void main(
String[] args) {
Todo todo = null;
// traitement annotation sur la classe
Class classe = TestInstrospectionAnnotation.class;
todo = (Todo) classe.getAnnotation(Todo.class);
if (todo != null) {
System.out.println("classe "+classe.getName());
System.out.println(" ["+todo.importance()+"]"+" ("+todo.assigneA()
+" le "+todo.dateAssignation()+")");
for(String desc : todo.description()) {
System.out.println(" _ "+desc);
}
}
// traitement annotation sur les méthodes de la classe
for(Method m : TestInstrospectionAnnotation.class.getMethods()) {
todo = (Todo) m.getAnnotation(Todo.class);
if (todo != null) {
System.out.println("méthode "+m.getName());
System.out.println(" ["+todo.importance()+"]"+" ("+todo.assigneA()
+" le "+todo.dateAssignation()+")");
for(String desc : todo.description()) {
System.out.println(" _ "+desc);
}
}
}
}
@Todo(importance = Importance.MAJEUR,
description = "Implémenter la methode",
assigneA = "JMD",
dateAssignation = "11-11-2007")
public void methode1() {
}
@Todo(importance = Importance.MINEURE,
description = {"Compléter la methode", "Améliorer les logs"},
assigneA = "JMD",
dateAssignation = "12-11-2007")
public void methode2() {
}
}
Résultat d'exécution : |
classe com.jmdoudoux.test.TestInstrospectionAnnotation
[CRITIQUE] (JMD le 11-11-2007)
_ Corriger le bug dans le calcul
méthode methode1
[MAJEUR] (JMD le 11-11-2007)
_ Implémenter la methode
méthode methode2
[MINEURE] (JMD le 12-11-2007)
_ Compléter la methode
_ Améliorer les logs
Pour obtenir les annotations sur les paramètres d'un constructeur ou d'une méthode, il faut utiliser la méthode getParameterAnnotations() des classes Constructor ou Method qui renvoie un objet de type Annotation[][]. La première dimension du tableau concerne les paramètres dans leur ordre de déclaration. La seconde dimension contient les annotations de chaque paramètre.
Dans la version 6 de Java SE, la prise en compte des annotations est intégrée dans le compilateur : ceci permet un traitement à la compilation des annotations sans avoir recours à un outil tiers comme apt.
Une nouvelle API a été définie par la JSR 269 (Pluggable annotations processing API) et ajoutée dans la package javax.annotation.processing.
Cette API est détaillée dans la section suivante.
La version 6 de Java apporte plusieurs améliorations dans le traitement des annotations notamment l'intégration de ces traitements directement dans le compilateur javac grâce à une nouvelle API dédiée.
L'API Pluggable Annotation Processing est définie dans la JSR 269. Elle permet un traitement des annotations directement par le compilateur en proposant une API aux développeurs pour traiter les annotations incluses dans le code source.
Apt et son API proposaient déjà une solution à ces traitements mais cette API standardise le traitement des annotations au moment de la compilation. Il n'est donc plus nécessaire d'utiliser un outil tiers post compilation pour traiter les annotations à la compilation.
Dans les exemples de cette section, les classes suivantes seront utilisées :
Exemple : MaClasse.java |
package com.jmdoudoux.tests;
import com.jmdoudoux.tests.annotations.Todo;
import com.jmdoudoux.tests.annotations.Todo.Importance;
@Todo(importance = Importance.CRITIQUE,
description = "Corriger le bug dans le calcul",
assigneA = "JMD",
dateAssignation = "11-11-2007")
public class MaClasse {
}
Exemple : MaClasse1.java |
package com.jmdoudoux.tests;
import com.jmdoudoux.tests.annotations.Todo;
import com.jmdoudoux.tests.annotations.Todo.Importance;
@Todo(importance = Importance.MAJEUR,
description = "Ajouter le traitement des erreurs",
assigneA = "JMD",
dateAssignation = "07-11-2007")
public class MaClasse1 {
}
Exemple : MaClasse3.java |
package com.jmdoudoux.tests;
@Deprecated
public class MaClasse3 {
}
Un exemple de mise en oeuvre de l'API est aussi fourni avec le JDK dans le sous-répertoire sample/javac/processing.
La mise en oeuvre de cette API nécessite l'utilisation des packages javax.annotation.processing, javax.lang.model et javax.tools.
Un processeur d'annotations doit implémenter l'interface Processor. Le traitement des annotations se fait en plusieurs passes (round). A chaque passe le processeur est appelé pour traiter des classes qui peuvent avoir été générées lors de la précédente passe. Lors de la première passe, ce sont les classes fournies initialement qui sont traitées.
L'interface javax.annotation.processing.Processor définit les méthodes d'un processeur d'annotations. Pour définir un processeur, il est possible de créer une classe qui implémente l'interface Processor mais le plus simple est d'hériter de la classe abstraite javax.annotation.processing.AbstractProcessor.
La classe AbstractProcessor contient une variable nommée processingEnv de type ProcessingEnvironment. La classe ProcessingEnvironment permet d'obtenir des instances de classes qui permettent des interactions avec l'extérieur du processeur ou fournissent des utilitaires :
La méthode getRootElements() renvoie les classes Java qui seront traitées par le processeur dans cette passe.
La méthode la plus importante est la méthode process() : c'est elle qui va contenir les traitements exécutés par le processeur. Elle possède deux paramètres :
Deux annotations sont dédiées aux processeurs d'annotations et doivent être utilisées sur la classe du processeur :
Exemple : |
package com.jmdoudoux.tests.annotations.outils;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import com.jmdoudoux.tests.annotations.Todo;
@SupportedAnnotationTypes(value = { "*" })
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class TodoProcessor extends AbstractProcessor {
@Override
public boolean process(
Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
Messager messager = processingEnv.getMessager();
for (TypeElement te : annotations) {
messager.printMessage(Kind.NOTE, "Traitement annotation "
+ te.getQualifiedName());
for (Element element : roundEnv.getElementsAnnotatedWith(te)) {
messager.printMessage(Kind.NOTE, " Traitement élément "
+ element.getSimpleName());
Todo todo = element.getAnnotation(Todo.class);
if (todo != null) {
messager.printMessage(Kind.NOTE, " affecté le " + todo.dateAssignation()
+ " a " + todo.assigneA());
}
}
}
return true;
}
}
Le compilateur javac est enrichi avec plusieurs options concernant le traitement des annotations :
Option |
Rôle |
-processor |
permet de préciser le nom pleinement qualifié du processeur à utiliser |
-proc |
vérifie si le traitement des annotations et/ou la compilation sont effectués |
-processorpath |
classpath des processeurs d'annotations |
-A |
permet de passer des options aux processeurs d'annotations sous la forme de paires cle=valeur |
-XprintRounds |
option non standard qui permet d'afficher des informations sur le traitement des annotations par les processeurs |
-XprintProcessorInfo |
option non standard qui affiche la liste des annotations qui seront traitées par les processeurs d'annotations |
Le compilateur fait appel à la méthode process() du processeur en lui passant en paramètre l'ensemble des annotations trouvées par le compilateur dans le code source.
Résultat : |
C:\Documents and Settings\jm\workspace\TestAnnotations>javac -cp ".;./bin;C:/Pr
ogram Files/Java/jdk1.6.0/lib/tools.jar" -processor com.jmdoudoux.tests.annotati
ons.outils.TodoProcessor com/jmdoudoux/tests/*.java
Note: Traitement annotation com.jmdoudoux.tests.annotations.Todo
Note: Traitement élément MaClasse
Note: affecte le 11-11-2007 a JMD
Note: Traitement élément MaClasse1
Note: affecte le 07-11-2007 a JMD
Note: Traitement annotation java.lang.Deprecated
Note: Traitement élément MaClasse3
La classe Filer permet de créer des fichiers lors du traitement des annotations.
Exemple : |
package com.jmdoudoux.tests.annotations.outils;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.StandardLocation;
import javax.tools.Diagnostic.Kind;
import com.jmdoudoux.tests.annotations.Todo;
@SupportedAnnotationTypes(value = { "*" })
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class TodoProcessor2 extends AbstractProcessor {
@Override
public boolean process(
Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
Filer filer = processingEnv.getFiler();
Messager messager = processingEnv.getMessager();
Elements eltUtils = processingEnv.getElementUtils();
if (!roundEnv.processingOver()) {
TypeElement elementTodo =
eltUtils.getTypeElement("com.jmdoudoux.tests.annotations.Todo");
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(elementTodo);
if (!elements.isEmpty())
try {
messager.printMessage(Kind.NOTE, "Création du fichier Todo");
PrintWriter pw = new PrintWriter(filer.createResource(
StandardLocation.SOURCE_OUTPUT, "", "Todo.txt")
.openOutputStream());
// .createSourceFile("Todo").openOutputStream());
pw.println("Liste des todos\n");
for (Element element : elements) {
pw.println("\nélément:" + element.getSimpleName());
Todo todo = (Todo) element.getAnnotation(Todo.class);
pw.println(" affecté le " + todo.dateAssignation()
+ " a " + todo.assigneA());
pw.println(" description : ");
for (String desc : todo.description()) {
pw.println(" " + desc);
}
}
pw.close();
} catch (IOException ioe) {
messager.printMessage(Kind.ERROR, ioe.getMessage());
}
else
messager.printMessage(Kind.NOTE, "Rien à faire");
} else
messager.printMessage(Kind.NOTE, "Fin des traitements");
return true;
}
}
Résultat : |
C:\Documents and Settings\jmd\workspace\TestAnnotations>javac -cp ".;./bin;C:/Pr
ogram Files/Java/jdk1.6.0/lib/tools.jar" -processor com.jmdoudoux.tests.annotati
ons.outils.TodoProcessor2 com/jmdoudoux/tests/*.java
Note: Création du fichier Todo
Note: Fin des traitements
C:\Documents and Settings\jmd\workspace\TestAnnotations>type Todo.txt
Liste des todos
élément:MaClasse
affecté le 11-11-2007 a JMD
description :
Corriger le bug dans le calcul
element:MaClasse1
affecté le 07-11-2007 a JMD
description :
Ajouter le traitement des erreurs
La JSR 175 A Metadata Facility for the JavaTM Programming Language
La JSR 269 Pluggable Annotation Processing API
La JSR 250 Common Annotations
La page des annotations dans la documentation du JDK
La page des annotations dans le tutorial Java
La page d'utilisation de l'outil APT dans la documentation du JDK
Le projet open source XDoclet qui propose la génération de code à partir d'attributs dans le code