vendredi 5 juin 2015

Java et la réflexion [3/4]

Une partie intéressant de la reflexion est la lecture des annotations.

Qu'est-ce que les annotation ?

Les annotation sont des métadonnées (des données supplémentaires) ajoutée à une classe, un attribut, une méthode.

Il existe 3 types d'annotations :
  • SOURCE : ces annotations sont ignorées par le compilateur mais produisent par exemple un message à la compilation comme @Override, @SuppressWarnings,
  • CLASS : ajoute des métadonnées à la classe mais ne sont pas accessibles à l'exécution,
  • RUNTIME : c'est ce type d'annotation qu'on va utiliser.

Reprenons l'exemple de MongoDB. Nous allons créer un simple mapper entre des objets java et du mongo.

Pour cela nous allons créer 3 annotations.
  • @Id : qui permet d'indiquer que la méthode est le getter/setter de l'ID MongoDB,
  • @Ignore : qui indique de ne pas mapper l'attribut de la classe java,
  • @Version : qui indique la version de l'objet

mongodb.tools.annotation.Id :
package mongodb.tools.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * To indicate that setter is for MongoDb Id.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Id {
}

mongodb.tools.annotation.Ignore :
package mongodb.tools.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Ignore method.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Ignore {
}

mongodb.tools.annotation.Version :
package mongodb.tools.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * To indicate that setter is for MongoDb Id.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Version {
    /**
     * Version of object.
     *
     * @return
     */
    public int version();

    /**
     * If strict when unserialize. Raise error if not same version.
     * @return
     */
    public boolean strict() default false;
}

Imaginons la méthode toMongo() qui convertit un object java en object MondoDB.
public static Document toMongo(final Object obj)
        throws InvocationTargetException, IllegalAccessException {
    Document result;

    if (obj == null) {
        result = null;
    } else {
        result = new Document();

        Class tClass = obj.getClass();
        Version versionAnnotation = (Version) tClass.getAnnotation(Version.class);

        if (versionAnnotation != null) {
            result.append(VERSION_FIELD, versionAnnotation.version());
        }
            
...

Nous prenons donc la classe de l'objet et sur cette classe nous récupérons l'annotation de type Version.class.
Il est important de noter que les annotations ne sont pas héritable. C'est à dire que si la classe Toto à l'annotation Version et que la classe Titi hérite de la classe Toto, l'annotation n'est pas reporter sur la classe Toto.
Il faudra remonter dans la hiérarchie des héritages pour prendre toutes les annotations.

Pour les méthodes, c'est à peu près identique :
...

Method[] methods = tClass.getMethods();

for (int i = 0; i < methods.length; i++) {
                methodName = methods[i].getName();

    if (methodName.startsWith("get") && !"getClass".equals(methodName)
            && !methods[i].isAnnotationPresent(Ignore.class)
            && !methods[i].isAnnotationPresent(Id.class)) {
...

Voilà, ce n'est pas plus difficile que ça.

Aucun commentaire:

Enregistrer un commentaire