IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Persistance des données Java Discussion :

Annotations - Un cas d'application pour la désérialisation avec XStream


Sujet :

Persistance des données Java

  1. #1
    Membre actif
    Avatar de bobuse
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    232
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 232
    Points : 278
    Points
    278
    Par défaut Annotations - Un cas d'application pour la désérialisation avec XStream
    Salut le forum,

    Un petit article sous forme de post, en espérant avoir vos avis/retours.

    L'objectif est de montrer une utilisation concrête des annotations et de l'introspection, afin de montrer leur intérêt. Pour moi, l'intérêt majeur est la possibilité d'ajouter des fonctionnalités dans un code source sans trop en altérer la structure.

    Contexte

    J'utilise la bibliothèque XStream pour sérialiser et désérialiser des objets Java. En particulier dans le cadre du développement d'un framework, l'utilisateur du framework spécialise des objets par héritage et ajoute des propriétés à ces objets. L'intérêt d'XStream est alors d'obtenir un fichier XML qui permet d'initialiser toutes ces propriétés qui sont ensuite utilisées dans le framework.

    Limite d'Xstream

    Xstream utilise le mot-clé transient pour exclure des attributs de classe du processus de sérialisation et désérialisation. C'est très pratique dans mon cas, puisque les attributs de classe définis par l'utilisateur peuvent des propriétés intéressantes pour la configuration du comportement global, mais ces attributs peuvent aussi n'être que fonctionnels et servir à stocker des objets liés au calcul par exemple.

    Dans le processus de désérialisation, touts les attributs propriétés (non transient) sont donc correctement initialisés. En revanche, j'ai aussi souvent besoin d'initialiser les attributs fonctionnels (transient) avant toute exécution de mon processus global.

    Exemple

    Par exemple, si dans une de mes classes, j'utilise une distribution statistique pour générer des nombres pseudo-aléatoires qui a besoin de deux paramètres pour s'initialiser, j'aurai trois attributs dans ma classe, les deux paramètres de la distribution (non transient), et l'objet représentant ma distribution qui lui est purement fonctionnel et sera donc transient.

    Ce qui donne quelquechose comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class MaClasse {
    	double a,b;
    	transient MaDistribution maDistribution;
    }
    Une fois ma classe désérialisée, j'aurai mes attributs a et b correctement initialisés, par contre l'attribut maDistribution sera null.
    Il faut donc que je crée une méthode qui effectuera cette initialisation, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    	public void initMaDistribution() {
    		maDistribution = new MaDistribution(a,b);
    	}
    Oui mais ensuite comment appeler cette méthode au bon moment dans le processus de désérialisation ?

    Une solution pourrait bien sûr de définir une interface avec une méthode qui serait appelée systématiquement. Il suffirait donc que MaClasse implémente cette interface, et définisse le corps de la fonction spécifié par l'interface. Ce serait simple.
    Oui, mais. Cette étape d'initialisation n'est pas systématique, et obligerait donc à mettre la fonction de l'interface avec un corps vide.

    Et c'est là que l'annotation est utile. Car elle va permettre de proposer une fonctionalité qui est très peu invasive dans le code.

    L'annotation et son utilisation

    Voilà l'annotation que je définis :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    	@Retention(RetentionPolicy.RUNTIME)
    	@Target(ElementType.METHOD)
    	public static @interface InitTransientParameters {}
    L'utilisation est simple, il suffit d'annoter la méthode que l'on veut exécuter à l'initialisation, comme cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    class MaClasse {
    	double a,b;
    	transient MaDistribution maDistribution;
     
    	@InitTransientParameters
    	public void initMaDistribution() {
    		maDistribution = new MaDistribution(a,b);
    	}
    }
    Ensuite comment appeler les méthodes annotées ? Et bien, en plus de l'annotation, j'ai défini deux méthodes.
    La première permet de récupérer les méthodes annotées de la sorte dans une classe, en prenant soin de chercher ces méthodes dans toutes les super-classes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    	public static List<Method> fetchInitMethods(Class type) {
    		List<Method> methods = new ArrayList<Method>();
    		Class inheritanceNavigator = type;
    		while (inheritanceNavigator != null) {
    			for (Method method : inheritanceNavigator.getDeclaredMethods()) {
    				if (method.isAnnotationPresent(InitTransientParameters.class))
    					methods.add(method);
    			}
    			inheritanceNavigator = inheritanceNavigator.getSuperclass();
    		}
    		return methods;
    	}
    Et la deuxième qui permet à l'aide de la première, d'exécuter ces méthodes sur un objet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    	public static void callAnnotedMethods(Object object) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
    		for (Method method : TransientParameters.fetchInitMethods(object.getClass())) {
    			method.setAccessible(true);
    			method.invoke(object);
    		}
    	}
    À partir de là, je peux dans mon framework, après avoir désérialiser un objet, appeler la méthode callAnnotedMethods sur cet objet pour effectuer les éventuels traitement d'initialisation.

    Remarques

    • La méthode annotée ne doit pas avoir d'argument, sinon une exception est levée
    • L'introspection nécessaire pour obtenir les méthodes annotées a un certain coût à l'exécution. Dans mon contexte, il ne s'agit que de la phase d'initialisation qui est négligeable par rapport à l'exécution complète.
    • Il y avait peut-être d'autres solutions possibles, mais je n'ai pas trop cherché. Cette solution me paraissait simple et séduisante.

    Merci de m'avoir lu

  2. #2
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    Les annotations permettent en effet plein de chose et peuvent être très utile !

    Dans ton cas une solution avec une interface aurait pu être plus simple à implémenter, puisqu'il aurait suffit de vérifier que l'objet implémente bien l'interface :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if (object instanceof MonInterface) {
    	( (MonInterface)object ).maMethodeInit();
    }
    Mais les annotations apportent ici deux avantages : on peut utiliser n'importe quelle méthode (avec n'importe quelle visibilité), et on peut utiliser plusieurs méthodes d'initialisation...


    Juste une remarque toutefois : évites d'appeler setAccessible() dans tous les cas. Il vaudrait mieux le faire seulement en cas de besoin :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if ( !method.isAccessible() ) {
    	method.setAccessible(true);
    }
    method.invoke(object);
    En effet setAccessible() nécessite certains droits qui nécessitent de signer l'application si elle est déployé par JavaWebStart ou en applet.
    Or si c'est pour appeler une méthode public c'est un peu dommage



    Citation Envoyé par bobuse
    La méthode annotée ne doit pas avoir d'argument, sinon une exception est levée
    A ce propos il existe un mécanisme qui permet de contrôler cela dès la compilation : APT (Annotation Processing Tool). Grosso modo à la compilation tu peux rechercher les éléments annoté et les vérifier (avec une API proche de celle de la réflection).

    J'en parle dans mon tutoriel sur les Annotations de Java 5.0, toutefois ce n'était pas standard et cela impliquait d'utiliser un compilateur spécifique (apt justement).

    C'est devenu standard avec Java 6, même si cela a été grandement modifié (je n'ai malheureusement pas encore eu le temps d'y jeter un coup d'oeil).

    a++

  3. #3
    Membre actif
    Avatar de bobuse
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    232
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 232
    Points : 278
    Points
    278
    Par défaut
    Citation Envoyé par adiGuba
    Salut,


    Les annotations permettent en effet plein de chose et peuvent être très utile !

    Dans ton cas une solution avec une interface aurait pu être plus simple à implémenter,[...]

    Mais les annotations apportent ici deux avantages : on peut utiliser n'importe quelle méthode (avec n'importe quelle visibilité), et on peut utiliser plusieurs méthodes d'initialisation...
    Oui, le fait de pouvoir annoter plusieurs méthodes est très pratique.

    Citation Envoyé par adiGuba
    Juste une remarque toutefois : évites d'appeler setAccessible() dans tous les cas. Il vaudrait mieux le faire seulement en cas de besoin :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if ( !method.isAccessible() ) {
    	method.setAccessible(true);
    }
    method.invoke(object);
    En effet setAccessible() nécessite certains droits qui nécessitent de signer l'application si elle est déployé par JavaWebStart ou en applet.
    Or si c'est pour appeler une méthode public c'est un peu dommage
    Ha oui, merci pour l'explication. C'est bon à savoir, ceci dit, je ne compte pas l'utiliser dans le contexte jws ou applet, donc bon ... Mais je le note.

    Citation Envoyé par adiGuba
    A ce propos il existe un mécanisme qui permet de contrôler cela dès la compilation : APT (Annotation Processing Tool). Grosso modo à la compilation tu peux rechercher les éléments annoté et les vérifier (avec une API proche de celle de la réflection).
    Ha oui, tiens c'est vrai !
    Ça pourrait effectivement être intéressant ... j'ai encore jamais utilisé APT, mais pour vérifier la validité de certaines conditions, ça pourrait être pas mal ...


    Merci pour ces remarques

  4. #4
    Membre actif
    Avatar de bobuse
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    232
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 232
    Points : 278
    Points
    278
    Par défaut
    Je déterre cette discussion juste pour dire que j'ai trouvé dans la doc de XStream la solution qui évite de faire tout ce que j'ai fait

    http://xstream.codehaus.org/faq.html#Serialization

    Voilà, il suffit d'ajouter la fonction readResolve dans son objet, et cette méthode sera appelée automatiquement après désérialisation.

Discussions similaires

  1. Application pour Freebox Player avec libfbxqml
    Par Tinto dans le forum Qt Quick
    Réponses: 5
    Dernier message: 14/01/2015, 00h25
  2. GENERER APPLICATION POUR NOKIA E75 AVEC JAVA
    Par denouezechias dans le forum Mobiles
    Réponses: 0
    Dernier message: 24/05/2013, 19h13
  3. Diagramme des cas d'utilisation pour une application de traitement d'images
    Par anubis_1001 dans le forum Cas d'utilisation
    Réponses: 1
    Dernier message: 18/07/2010, 16h57
  4. [Xstream] Problème désérialisation avec xstream et les annotations
    Par riderfun dans le forum Persistance des données
    Réponses: 1
    Dernier message: 26/04/2010, 17h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo