Date de publication : 05/07/2005. Date de mise à jour : 08/08/2011.
ftp://ftp-developpez.com/adiguba/tutoriels/java/tiger/annotations/annotations.pdf
http://ftp-developpez.com/adiguba/tutoriels/java/tiger/annotations/annotations.pdf).
http://lroux.developpez.com/article/java/tiger/
http://xdoclet.sourceforge.net)
ou encore d'EJBGen (
http://www.beust.com/cedric/ejbgen/),
mais possèdent l'avantage de faire partie du langage lui-même ...
public @interface MonAnnotation {
}
@MonAnnotation
public class MaClasse {
/* ... */
}
public class Maclasse {
/**
* Retourne l'année en cours.
* @return L'année en cours.
*/
@Deprecated
public int getYear () {
return year;
}
}
public class Maclasse {
/**
* Retourne l'année en cours.
* @return L'année en cours.
* @deprecated Retourne en réalité le nombre d'années depuis 1900. Remplacée par getFullYear().
*/
@Deprecated
public int getYear () {
return year;
}
}
@Override
public String toString() {
return "Texte";
}
@SuppressWarnings("deprecation")
public class OldClass {
/* ... */
}
@SuppressWarnings({"deprecation","unckeked"})
public int m () {
/* ... */
}
add support for jsr175's java.lang.SuppressWarnings.
java.lang.annotation :
public @interface SimpleAnnotation {
}
import java.lang.annotation.Documented;
@Documented
public @interface DocAnnotation {
}
public class Exemple {
/**
* Commentaire de la méthode 1.
*/
@SimpleAnnotation
public void method1 () {
/* ... */
}
/**
* Commentaire de la méthode 2.
*/
@DocAnnotation
public void method2 () {
/* ... */
}
}
public void method1()
Commentaire de la méthode 1.
______________________________________________
@DocAnnotation
public void method2()
Commentaire de la méthode 2.
la
documentation de l'outil Javadoc 5.0 sur le site officiel de Sun.
public @interface SimpleAnnotation {
}
import java.lang.annotation.Inherit;
@Inherit
public @interface InheritAnnotation {
}
@SimpleAnnotation
@InheritAnnotation
public class ExempleInherited {
}
| Valeur | Description |
|---|---|
| RetentionPolicy.SOURCE | Les annotations ne sont pas enregistrées dans le fichier *.class. Elles ne sont donc accessibles que par des outils utilisant les fichiers sources (compilateur, javadoc, etc...). |
| RetentionPolicy.CLASS | Les annotations sont enregistrées dans le fichier *.class
à la compilation mais elle ne sont pas utilisées par la machine virtuelle
à l'exécution de l'application. Elles peuvent toutefois être utilisées
par certains outils qui lisent directement les *.class. Il s'agit du comportement par défaut si la méta-annotation n'est pas présente. |
| RetentionPolicy.RUNTIME | Les annotations sont enregistrées dans le fichier *.class à la compilation et elle sont utilisées par la machine virtuelle à l'exécution de l'application. Elles peuvent donc être lues grâce à l'API de réflection (plus de détails dans le chapitre sur l'introspection)... |
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.SOURCE)
@interface SourceAnnotation { }
@Retention(RetentionPolicy.CLASS)
@interface ClassAnnotation { }
@Retention(RetentionPolicy.RUNTIME)
@interface RuntimeAnnotation {}
@SourceAnnotation
@ClassAnnotation
@RuntimeAnnotation
public class Main {
public static void main(String[] args) {
System.out.println ("Liste des annotations de la classe 'Main' :");
System.out.println ();
for (Annotation a : Main.class.getAnnotations() ) {
System.out.println ("\t * Annotation : " + a.annotationType().getSimpleName());
}
}
}
Liste des annotations de la classe 'Main' :
* Annotation : RuntimeAnnotation
| Valeur | Description |
|---|---|
| ElementType.ANNOTATION_TYPE | L'annotation peut être utilisée sur d'autres annotations. |
| ElementType.CONSTRUCTOR | L'annotation peut être utilisée sur des constructeurs. |
| ElementType.FIELD | L'annotation peut être utilisée sur des champs d'une classe. |
| ElementType.LOCAL_VARIABLE | L'annotation peut être utilisée sur des variables locales. |
| ElementType.METHOD | L'annotation peut être utilisée sur des méthodes. |
| ElementType.PACKAGE | L'annotation peut être utilisée sur des packages |
| ElementType.PARAMETER | L'annotation peut être utilisée sur des paramètres d'une méthode ou d'un constructeur. |
| ElementType.TYPE | L'annotation peut être utilisée sur la déclaration d'un type : class, interface (annotation comprise) ou d'une énumération (mot-clef enum). |
@Target(ElementType.CONSTRUCTOR)
public @interface ConstructorAnnotation {
}
@Target( {ElementType.CONSTRUCTOR, ElementType.METHOD} )
public @interface ConstructorAnnotation {
}
public @interface TODO {
}
| Méta-Annotation | Question | Réponse pour @TODO |
|---|---|---|
| @Documented | L'annotation doit-elle être documentée par javadoc ? | Oui, afin d'avoir un aperçu des tâches restantes dans la documentation. |
| @Inherit | L'annotation doit-elle être héritée (seulement si l'annotation est posée sur une classe) ? | Non, on ne souhaite pas impacter les classes filles. |
| @Retention | Quel est la 'durée de vie' de l'annotation ? | SOURCE : Il n'est pas nécessaires de conserver cette annotation dans les fichiers *.class, on n'utilisera donc pas cette méta-annotation. |
| @Target | L'annotation doit-elle se restreindre à certains éléments ? | Non, on souhaite pouvoir annoter tous types d'éléments. |
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@Documented
@Retention(SOURCE)
public @interface TODO {
}
@TODO
public void doSometing () {
/* ... */
}
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@Documented
@Retention(SOURCE)
public @interface TODO {
/** Message décrivant la tâche à effectuer. */
String value();
}
/**
* Commentaire de la méthode...
*/
@TODO(value="La gestion des exceptions est incorrecte...")
public void doSometing () {
/* ... */
}
/**
* Commentaire de la méthode...
*/
@TODO("La gestion des exceptions est incorrecte...")
public void doSometing () {
/* ... */
}
@TODO(value="La gestion des exceptions est incorrecte...")
public void doSometing()
Commentaire de la méthode...
@MonAnnotation (tab={1,2,3,4,5})
public clas MaClasse {
}
@MonAnnotation (tab=1)
public clas MaClasse {
}
@Documented
@Retention(SOURCE)
public @interface TODO {
/** Message décrivant la tâche à effectuer. */
String value();
/** Niveau de criticité de la tâche. */
Level level();
/** Enumération des différents niveaux de criticités. */
public static enum Level { MINEUR, NORMAL, IMPORTANT };
}
@TODO(value="La gestion des exceptions est incorrecte...", level=TODO.Level.NORMAL)
public void doSometing () {
/* ... */
}
import static com.developpez.adiguba.annotation.TODO.Level.*;
/* ... */
@TODO(value="La gestion des exceptions est incorrecte...", level=NORMAL)
public void doSometing () {
/* ... */
}
@Documented
@Retention(SOURCE)
public @interface TODO {
/** Message décrivant la tâche à effectuer. */
String value();
/** Niveau de criticité de la tâche (défaut : NORMAL). */
Level level() default Level.NORMAL;
/** Enumération des différents niveaux de criticités. */
public static enum Level { MINEUR, NORMAL, IMPORTANT };
}
@TODO("La gestion des exceptions est incorrecte...")
public void doSometing () {
/* ... */
}
@TODO(value="La gestion des exceptions est incorrecte...")
public void doSometing () {
/* ... */
}
@TODO(value="La gestion des exceptions est incorrecte...", level=NORMAL)
public void doSometing () {
/* ... */
}
@TODO(level=NORMAL, value="La gestion des exceptions est incorrecte...")
public void doSometing () {
/* ... */
}
@Documented
@Retention(SOURCE)
public @interface TODOs {
/** Le Tableau des différentes annotations TODO. */
TODO[] value();
}
@TODOs({
@TODO("La gestion des exceptions est incorrecte..."),
@TODO(value="NullPointerException possible dans certain cas.",level=IMPORTANT)
})
public void doSometing () {
/* ... */
}
http://java.sun.com/j2se/1.5.0/docs/guide/apt/
http://java.sun.com/j2se/1.5.0/docs/guide/apt/mirror/
public class SimpleAnnotationProcessorFactory implements AnnotationProcessorFactory {
/** Collection contenant le nom des Annotations supportées. */
protected Collection<String> supportedAnnotationTypes =
Arrays.asList( TODO.class.getName(), TODOs.class.getName() );
/** Collection des options supportées. */
protected Collection<String> supportedOptions =
Collections.emptyList();
/**
* Retourne la liste des annotations supportées par cette Factory.
*/
public Collection<String> supportedAnnotationTypes() {
return supportedAnnotationTypes;
}
/**
* Retourne la liste des options supportées par cette Factory.
*/
public Collection<String> supportedOptions() {
return supportedOptions;
}
/**
* Retourne l'AnnotationProcessor associé avec cette Factory...
*/
public AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> atds,
AnnotationProcessorEnvironment env) {
// Si aucune annotation n'est présente on retourne un processeur "vide"
if (atds.isEmpty())
return AnnotationProcessors.NO_OP;
return new SimpleAnnotationProcessor(env);
}
}
public class SimpleAnnotationProcessor implements AnnotationProcessor {
/** L'environnement du processeur d'annotation. */
protected final AnnotationProcessorEnvironment env;
/**
* Constructeur.
* @param env L'environnement du processeur d'annotation.
*/
public SimpleAnnotationProcessor (AnnotationProcessorEnvironment env) {
this.env = env;
}
/**
* Traitement des fichiers sources.
*/
public void process() {
// Instanciation du Visitor
TODOVisitor todoVisitor = new TODOVisitor(env);
// On boucle sur toutes les Annotations :
for ( Declaration d : env.getTypeDeclarations()) {
// On "visite" chacune des déclarations trouvées :
d.accept( DeclarationVisitors.getSourceOrderDeclarationScanner(
todoVisitor, DeclarationVisitors.NO_OP) );
}
}
}
public class TODOVisitor extends SimpleDeclarationVisitor{
protected final AnnotationProcessorEnvironment env;
public TODOVisitor (AnnotationProcessorEnvironment env) {
this.env = env;
}
/**
* Pour tout type de déclaration, on affiche un message si
* l'Annotation @TODO est présente...
* De même, on affiche un message pour tous les @TODO
* contenu dans l'annotation @TODOs
*/
@Override
public void visitDeclaration(Declaration decl) {
// On regarde si la déclaration possède une annotation TODO
TODO todo = decl.getAnnotation(TODO.class);
// Et on l'affiche eventuellement :
if (todo!=null)
printMessage(decl, todo);
// On fait la même chose pour l'annotation TODOs :
TODOs todos = decl.getAnnotation(TODOs.class);
if (todos!=null) {
// On affiche les message pour tout les TODOs :
for ( TODO t : todos.value() )
printMessage(decl,t);
}
}
/**
* Affiche dans la console l'annotation TODO.
* @param decl
* @param todo
*/
public void printMessage (Declaration decl, TODO todo) {
m.printNotice(decl.getSimpleName() + " : " + todo.value() );
}
import com.developpez.adiguba.annotation.TODO;
import static com.developpez.adiguba.annotation.TODO.Level.*;
public class Test {
@TODO("Utiliser une annotation a la place d'une String")
protected String day;
@TODO(value="Ecrire le code de la methode", level=IMPORTANT)
public void method() {
}
}
Note: day : Utiliser une annotation a la place d'une String
Note: method : Ecrire le code de la methode
public class TODOVisitor extends SimpleDeclarationVisitor{
protected final AnnotationProcessorEnvironment env;
/** Indique si l'option -Arelease fait partie de la ligne de commande. */
protected final boolean isRelease;
public TODOVisitor (AnnotationProcessorEnvironment env) {
this.env = env;
isRelease = env.getOptions().containsKey(SimpleAnnotationProcessorFactory.RELEASE);
}
/**
* Pour tout type de déclaration, on affiche un message si
* l'Annotation @TODO est présente...
* De même, on affiche un message pour tous les @TODO
* contenu dans l'annotation @TODOs
*/
@Override
public void visitDeclaration(Declaration decl) {
// On regarde si la déclaration possède une annotation TODO
TODO todo = decl.getAnnotation(TODO.class);
// Et on l'affiche eventuellement :
if (todo!=null)
printMessage(decl, todo);
// On fait la même chose pour l'annotation TODOs :
TODOs todos = decl.getAnnotation(TODOs.class);
if (todos!=null) {
// On affiche les message pour tout les TODOs :
for ( TODO t : todos.value() )
printMessage(decl,t);
}
}
/**
* Affiche dans la console l'annotation TODO.
* @param decl
* @param todo
*/
public void printMessage (Declaration decl, TODO todo) {
if (isRelease)
printReleaseError (decl, todo);
else
printDebugingInfo (decl, todo);
}
/**
* Affiche le message pendant la phase de developpement.
* Les messages IMPORTANT sont affichées comme des 'warnings'.
* Les messages NORMAL sont affichées comme des 'notes'.
* Les messages MINEUR sont affichées comme des 'notes' simples (sans position dans le code).
* @param decl La déclaration qui contient l'annotation.
* @param todo L'Annotation affichée.
*/
public void printDebugingInfo (Declaration decl, TODO todo) {
Messager m = env.getMessager();
switch (todo.level()) {
case IMPORTANT:
m.printWarning(decl.getPosition(),
decl.getSimpleName() + " : " + todo.value() );
break;
case NORMAL:
m.printNotice(decl.getPosition(),
decl.getSimpleName() + " : " + todo.value() );
break;
case MINEUR:
m.printNotice(decl.getSimpleName() + " : " + todo.value() );
break;
}
}
/**
* Affiche le message pendant la compilation en mode release.
* Les messages IMPORTANT sont affichées comme des 'erreurs'.
* Les messages NORMAL sont affichées comme des 'warning'.
* Les messages MINEUR sont affichées comme des 'notes'.
* @param decl La déclaration qui contient l'annotation.
* @param todo L'Annotation affichée.
*/
public void printReleaseError (Declaration decl, TODO todo) {
Messager m = env.getMessager();
switch (todo.level()) {
case IMPORTANT:
m.printError(decl.getPosition(),
decl.getSimpleName() + " : " + todo.value() );
break;
case NORMAL:
m.printWarning(decl.getPosition(),
decl.getSimpleName() + " : " + todo.value() );
break;
case MINEUR:
m.printNotice(decl.getPosition(),
decl.getSimpleName() + " : " + todo.value() );
break;
}
}
}
Test.java:7: Note: day : Utiliser une annotation a la place d'une String
protected String day;
^
Test.java:11: warning: method : Ecrire le code de la methode
public void method() {
^
1 warning
Test.java:7: warning: day : Utiliser une annotation a la place d'une String
protected String day;
^
Test.java:11: method : Ecrire le code de la methode
public void method() {
^
1 error
1 warning
warning: Annotation types without processors
public class StandardAnnotationProcessorFactory implements AnnotationProcessorFactory {
/** Collection contenant le nom des Annotations supportées. */
protected Collection<String> supportedAnnotationTypes =
Arrays.asList(
// Annotation Standard
"java.lang.*",
// Meta-Annotation
"java.lang.annotation.*"
);
/**
* Retourne la liste des annotations supportées par cette Factory.
*/
public Collection<String> supportedAnnotationTypes() {
return supportedAnnotationTypes;
}
/**
* Retourne la liste des options supportées par cette Factory.
*/
public Collection<String> supportedOptions() {
return Collections.emptyList();
}
/**
* Retourne AnnotationProcessors.NO_OP (Pas de traitement).
*/
public AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> atds,
AnnotationProcessorEnvironment env) {
return AnnotationProcessors.NO_OP;
}
}
@Inherited
@Documented
@Target(ElementType.TYPE)
public @interface EmptyConstructor {
}
public class EmptyConstructorVisitor extends SimpleDeclarationVisitor {
/** L'envirronnement du processeur d'annotation. */
protected final AnnotationProcessorEnvironment env;
/**
* Constructeur.
* @param env L'envirronnement du processeur d'annotation.
*/
public EmptyConstructorVisitor (AnnotationProcessorEnvironment env) {
this.env = env;
}
/**
* Méthode appelé pour chaque déclaration d'une classe.
*/
@Override
public void visitClassDeclaration(ClassDeclaration classDecl) {
// Si on n'a pas de constructeur vide et
// que l'annotation EmptyConstructor a été trouvé
// --> On affiche un message d'erreur :
if ( !hasEmptyConstructor(classDecl) &&
hasAnnotation(classDecl,EmptyConstructor.class) ) {
env.getMessager().printError( classDecl.getPosition(),
"Un constructeur vide est requis par @EmptyConstructor.");
}
}
/**
* Cette méthode indique si la déclaration de classe passée en
* paramètre possède un constructeur vide.
* @param classDecl La déclaration de classe à analyser.
* @return true si la classe possède un constructeur vide, false sinon.
*/
public boolean hasEmptyConstructor (ClassDeclaration classDecl) {
// On parcourt la liste des constructeurs :
for ( ConstructorDeclaration c : classDecl.getConstructors() ) {
if ( c.getParameters().isEmpty() )
return true; // On a trouvé un constructeur vide
}
return false; // Pas de constructeur vide
}
/**
* Recherche récursivement dans les interfaces si une Annotation est présente.<br/>
* En effet, les annotations présentes dans les interfaces ne sont pas hérité...
* @param typeDecl Le type de base de la recherche.
* @param annotationClass La classe de l'annotation a rechercher
* @return <b>true</b> si une annotation de type <b>annotationClass</b> est trouvée, <b>false</b> sinon.
*/
public boolean hasAnnotation (TypeDeclaration typeDecl, Class<? extends Annotation> annotationClass) {
// Pour chaque interface directement implémenté :
for ( InterfaceType iType : typeDecl.getSuperinterfaces() ) {
InterfaceDeclaration superInterface = iType.getDeclaration();
// On regarde si l'interface possede l'annotation
if ( superInterface.getAnnotation(EmptyConstructor.class)!=null )
return true; // On en a trouvé une : pas la peine de continuer ;)
// Sinon on regarde dans les interfaces parentes :
if ( hasAnnotation(superInterface,annotationClass) )
return true; // On en a trouvé une : pas la peine de continuer ;)
}
// Si on arrive ici cela signifie que l'on n'a pas trouvé l'Annotation.
return false;
}
}
<javac fork="yes" executable="apt" srcdir="${src}" destdir="${build}">
<classpath>
<pathelement path="tutoriel-annotations.jar"/>
</classpath>
<compilerarg value="-Arelease"/>
</javac>
http://forums.java.net/jive/thread.jspa?messageID=1402&tstart=0
JSR 269 : Pluggable Annotation Processing API.
Introspection en JAVA, présentation de l'API Réflection.
// Instanciation de l'objet
Exemple objet = new Exemple();
// On récupère la classe de l'objet :
Class<Exemple> classInstance = objet.getClass();
// On regarde si la classe possède une annotation :
MonAnnotation annotation = classInstance.getAnnotation(MonAnnotation.class);
if (annotation!=null) {
System.out.println ("Annotation TODO : " + annotation.value() );
}
public MaClasse {
private String config1 = Manager.getInstance().getProperty("property_1");
private String config2 = Manager.getInstance().getProperty("property_2");
private String config3 = Manager.getInstance().getProperty("property_3");
private String config4 = Manager.getInstance().getProperty("property_4");
public MaClasse () {
}
}
/**
* Permet d'indiquer la propriété associé à un champ ou une méthode.
* @author adiGuba
*/
@Documented
@Retention(RUNTIME)
@Target(FIELD)
public @interface Property {
/** Nom de la propriété liée à cet élément. */
String value ();
/** Valeur si l'élément est absent (optionnel / "" par défaut). */
String missing() default "";
}
public MaClasse {
@Property("property_1") private String config1;
@Property("property_2") private String config2;
@Property("property_3") private String config3;
@Property("property_4") private String config4;
public MaClasse () {
Manager.getInstance().initialize(this);
}
}
public void initialize (Object pObjectInstance)
throws PropertyManagerException {
// On récupère la classe de l'objet.
Class<?> lClassInstance = pObjectInstance.getClass();
// Pour tous les champs de l'objet :
for ( Field f : pClassInstance.getDeclaredFields() ) {
// On recherche l'annotation @Property :
Property property = f.getAnnotation(Property.class);
if (property!=null) {
// On recupère la valeur dans le Properties
String value = lProp.getProperty(lPrefix+property.value());
// Si la propriété n'existe pas on met la valeur par defaut:
if (value==null)
value= property.missing();
try {
// Si on n'a pas accès aux champs, on force son accessibilité
// Cela permet de modifier les champs firendly, protected et private :
boolean pAccessible = f.isAccessible();
if (!pAccessible)
f.setAccessible(true);
f.set(pObjectInstance, value);
} catch (Exception lException) {
throw new PropertyManagerException(
"Impossible d'assigner le champ '" +
value + "'.", lException );
}
} // fin property!=null
} // fin for getDeclaredFields()
}
Copyright © 2000-2012 - www.developpez.com