« Article précédent : Article suivant : »

Initiation à la Programmation Orientée Aspect

Par 15 sept, 2008

Introduction

Le but de cet article est de vous donner un aperçu des possibilités offertes par la Programmation Orientée Aspect, ou Aspect Oriented Programming (AOP). En partant d’un exemple concret, nous définirons les concepts de Weaver, PointCut, JoinPoint, Advice, inter-type declaration, … Nous parlerons ensuite des usages possibles de cette technologie dans vos projets, pour terminer par quelques réflexions sur les paradigmes de programmation.

Problèmes

Imaginons que vous arrivez sur un projet Java avec un lourd passé, comportant plusieurs dizaines de milliers de lignes de code pas ou peu commentées, des styles de codes hétérogènes, des dizaines de classes de plus de 3000 lignes, des interactions alambiquées entre le client, le serveur, la base de données, les applications exernes, le tout avec des traitements multithreads. Les « développeurs » coupables de ces méfaits ne sont évidemment plus là, et vous êtes chargé de la maintenance de cette magnifique application qui est part ailleurs critique pour ses utilisateurs. Cette situation périlleuse (toute ressemblance avec des personnes ou des faits ayant existé ne serait que pure coïncidence) vous impose de

  1. Ne surtout toucher à rien tant que ça fonctionne
  2. Tracer. Les développeurs précédents n’ayant évidemment pas pris le soin d’utiliser un framework de logging tel que Log4J, vous n’avez pas la moindre idée de ce qui se passe lorsque vous lancez cette &%°@ d’application.
  3. Analyser les performances. Vous aimeriez bien savoir quelles portions de code la font tant ramer, car vos utilisateurs réclament depuis longtemps des améliorations à ce niveau.
  4. Mettre de l’ordre. Pour cela, il serait utile de trouver toutes les violations des règles d’architecture. (par exemple, ne pas appeler de DAO depuis une Servlet)
  5. Refaire votre CV pour pour pouvoir quitter ce traquenard au plus vite. Ca fera toujours bien de pouvoir mettre « AOP » dedans.

Solutions

Nous allons voir comment utiliser l’AOP pour régler ces problèmes. L’exemple cite volontairement un projet existant, car l’AOP n’est pas une technologie réservée à des nouveaux projets, elle peut être introduite sur des projets existants, parfois sans recompilation. L’exemple cite un projet Java, mais les concepts introduits ici peuvent également s’appliquer sur d’autres langages tels que C# ou même sur le bon vieux langage C.

Pour résoudre le point 2 – Tracer, la méthode classique serait de

  1. identifier ce que l’on trace, par exemple « à l’entrée et à la sortie de toutes les méthodes publiques du package com...services« 
  2. déterminer le format de la trace : niveau (DEBUG, INFO, …), impression des arguments, de la stacktrace, …
  3. modifier tout le code. Travail de singe répétitif et sans intérêt
  4. s’apercevoir qu’on a oublié de tracer quelque chose d’essentiel
  5. Goto 2

En AOP, nous allons procéder de manière similaire :

1. Identifier

Les endroits du code qui vont faire l’objet d’une trace sont des JoinPoint. Un JoinPoint est un point précis d’exécution de code. Cela peut être l’entrée dans une méthode, l’appel d’une méthode, la lecture d’un attribut, l’entrée dans une clause catch. Grosso modo c’est un endroit sur lequel votre débugger peut s’arrêter.

La sélection des JoinPoint pour lesquels on va écrire une trace (ici, « à l’entrée et à la sortie de toutes les méthodes publiques du package com...services« ) est un PointCut. Une syntaxe particulière va nous permettre de définir notre PointCut.

Avec AspectJ, nous écrirons :

Object around() : tracedMethods() {
    System.out.println("Avant" + message + thisJoinPoint.getSignature().toString());
    try {
        return proceed();
    }
    finally {
        System.out.println("Après" + message + thisJoinPoint.getSignature().toString());
    }
}

Ce qui signifie « à la place des JoinPoint définis par le PointCut tracedMethods(), exécute le code qui suit ». Si on n’appelle pas proceed(), on peut même shunter complètement l’exécution de la méthode interceptée. L’exemple montre un around advice, il existe également des before et des after advices.

Un Aspect est un ensemble de PointCuts et d’Advices, de la même manière qu’une classe est un ensemble d’attributs et de méthodes. Avec AspectJ, un Aspect est par ailleurs également une classe Java. Il peut donc également recevoir des attributs et des méthodes.

Pour notre exemple, nous écrirons :

public aspect TraceAspect {
    private final String message = " exécution de ";
    public pointcut tracedMethods() : execution(public * com..services.*.*(..));
    Object around() : tracedMethods() {
        System.out.println("Avant" + message + thisJoinPoint.getSignature().toString());
        try {
            return proceed();
        }
        finally {
            System.out.println("Après" + message + thisJoinPoint.getSignature().toString());
        }
    }
}

3. Tisser

La mécanique qui permet de tisser notre Advice aux différent JoinPoint est le Weaving. Selon le framework d’Aspects que vous utiliserez, vous aurez le choix entre une ou plusieurs de ces techniques de Weaving :

  • Compile time Weaving. Le weaving se fait à la compilation. On utilise un autre compilateur que le compilateur Java standard. Cela peut avoir un impact sur votre processus de construction des livrables et sur votre environnement de développement.
  • Bytecode Weaving. Le weaving se fait sur les classes déjà compilées. L’impact sur l’environnement de développement est moins important, mais vous serez un peu limité au niveau du langage (pas d’appels de méthodes injectées par inter-type declaration, voir plus loin). L’environnement de développement sera moins agréable (pas d’affichage des JoinPoint weavés).
  • Load Time Weaving. Par une substitution du ClassLoader, le weaving se fait au chargement de la classe, lors de l’exécution du code. Ce type de weaving est celui qui a le moins d’impact sur l’environnement de développement (il faut juste ajouter un paramètre de lancement -javaagent:(…)/aspectjweaver.jar). Il souffre par contre des mêmes inconvénients que le Bytecode weaving. De plus, si votre application a beaucoup de classes, cela peut fortement ralentir son chargement.
  • Proxy. C’est le mécanisme utilisé par Spring AOP. Il est beaucoup plus limité que les autres types de weaving. (uniquement des joinpoint method execution, pas de weaving sur les appels de méthodes de la même classe, obligation d’instancier par la ProxyFactory, … ) Il est par contre très souple d’utilisation (définition des pointcuts dans le .xml) et peut être suffisant pour des cas simples. Il est simple à mettre en place si vous utilisez déjà Spring.

4. Vous avez toujours oublié quelque chose

5. Vous êtes bien outillé

Si vous avez oublié de tracer quelque chose, il est très simple de modifier le PointCut ou l’Advice et de reweaver. Si vous utilisez le plugin AspectJ pour Eclipse, des marqueurs vous indiquent après la compilation les JoinPoint « Advised » :

grey Initiation à la Programmation Orientée Aspect

grey Initiation à la Programmation Orientée Aspect

Autres concepts

1. inter-type declaration

Une telle déclaration permet par exemple de dire à votre tisseur « je veux que toutes les classes *Dto implémentent l’interface Serializable », ou encore « je veux que toutes les classes *Dto aient un attribut private Long id avec un getter et un setter associé ». Cela permet en plus de contourner une limitation du langage Java qui est de ne pas autoriser d’héritage multiple.

Exemple AspectJ :

public aspect DomainAspect {
    declare parents : com..domain.* implements Serializable, KeyedDto;
    public interface KeyedDto {}
    private Long KeyedDto.id;
    public Long KeyedDto.getId() {
        return this.id;
    }
    public void KeyedDto.setId(Long newId) {
        this.id = newId;
    }
}

Vous pouvez ensuite appeler les méthodes injectées dans les classes du package domain :

    Customer cust = new Customer();
    cust.setId(10l);

2. Error declaration

Grâce à une déclaration légèrement différente, vous pouvez aussi améliorer votre compilateur pour qu’il vous signale des violations de vos règles d’architecture ou de codage. Cela vient un peu en concurrence d’outils comme PMD ou CheckStyle, mais si vous êtes déjà familiarisés avec la syntaxe AOP, vous n’aurez pas à apprendre une nouvelle syntaxe pour définir vos règles.

Exemple : « Je veux que mon compilateur signale une erreur si un domain object appelle directement un service »

Exemple AspectJ :

declare error : within(com..domain.*) && call(* com..services.*.*(..)) : "Un domain object ne doit pas appeler de service";

Utilisations dans des « vrais » projets

Avec ces quelques concepts, vous êtes déjà capables d’imaginer la puissance de l’AOP. Le logging est un exemple classique, mais on peut également utiliser l’AOP pour :

  • gérer des transactions (démarrage d’une transaction à l’entrée d’une méthode, commit ou rollback à la sortie). Voir AnnotationTransactionAspect.
  • gérer la sécurité. Par exemple, la méthode ClientServiceImpl.getClients renverra une liste de Clients filtrée pour ne garder que les clients visibles par l’utilisateur connecté. Le bouton sauvegarder ne sera actif que pour les utilisateurs ayant le profil Gestionnaire. La sécurité étant un domaine transversal qui change souvent selon l’environnement et les besoins des utilisateurs, il est beaucoup plus simple de la maintenir dans quelques aspects plutôt que dans de multiples classes.
  • pouvoir utiliser l’injection de dépendances de Spring sur des objets instanciés par un simple new MyObject(). Voir @Configurable.
  • vérifier que certaines méthodes s’exécutent toujours dans un thread particulier, ce qui est très utile si vous utilisez les Threads avec Swing. Voir EdtRuleChecker.
  • forcer l’exécution de certaines méthodes dans un thread particulier (par exemple un SwingWorker). En Swing, on peut ainsi afficher un indicateur d’occupation (sablier ou animation) durant l’exécution de traitements longs, au lieu de figer l’interface.
  • éviter de recoder systématiquement des appels à firePropertyChange dans les setters de vos JavaBeans observables. Voir BoundPoint.
  • d’une manière générale, tout code redondant ne pouvant pas être factorisé de manière simple par une conception objet, peut être avantageusement remplacé par un aspect.

Conclusion

L’AOP, nouveau paradigme de programmation ?

Dans l’histoire des langages de programmation, nous avons tout d’abord programmé en assembleur, ensuite sont nés les langages procéduraux comme le C ou le Pascal. Puis sont venus les langages objets comme SmallTalk, C++, Eiffel, Java, Parallèlement nous avons vu se développer des langages fonctionnels comme LISP, ML, Erlang …. A chaque nouveau paradigme de programmation, nous prenons de la distance par rapport à la machine, nous manipulons des concepts toujours plus abstraits, qui aboutissent à des programmes de plus en plus riches.

La programmation orientée aspects, ou Aspect Oriented Programming, propose également une nouvelle approche. Ce n’est par contre pas une approche visant à remplacer une famille de langages ou une autre, elle vient en complément aux langages existants. Je dirais qu’elle est orthogonale aux langages. Elle ne remplace pas un langage, mais l’enrichit par une autre vision.

L’intérêt de ce type de programmation est de pouvoir alléger au maximum le code qui va effectivement traiter les règles métier de votre application. Le code n’est plus pollué par du code technique ayant un rapport avec telle ou telle technologie, par des constructions complexes, redondantes, et sources d’erreurs. Il devient plus lisible, plus facile à tester et à maintenir ; il peut être appréhendé par des juniors.

L’AOP permet également de gagner en flexibilité. Vous n’êtes plus obligés de penser dès le démarrage du projet à la façon dont vous gérez la sécurité, le logging ou le look and feel de votre application. Vous savez que vous avez un outil puissant qui vous permettra d’ajouter tout ça plus tard, à partir du moment ou chaque élément de votre architecture est bien défini (DAO, DTO, Service, Factory, Contrôleur, …).

Malheureusement, bien que les outils soient maintenant bien au point, l’AOP reste peu utilisée en entreprise. C’est d’ailleurs un peu par militantisme que j’écris cet article. Lorsque les frameworks majeurs l’utiliseront et proposeront des aspects utilisables directement, je pense qu’on verra cette programmation se déployer. C’est déjà un peu le cas avec Spring et comme beaucoup de personnes c’est via ce framework que je me suis intéressé à l’AOP.

On pourrait également assister à la naissance de frameworks d’Aspects, qu’il suffirait de configurer ou sous-classer (on peut avec AspectJ définir des Aspects abstraits contenant des PointCuts abstraits) pour les adapter à nos besoins. Peut être qu’on pourra à partir de ce moment parler d’un nouveau paradigme de programmation.

Vous avez aimé cet article ? Vous aimerez sûrement aussi...

 

Vous devriez nous suivre sur Twitter ici et rejoindre notre groupe sur Facebook ici.

Mots-clefs :, , , , , , Catégories : apple, article, games, high-tech
Trackbacks & Pingbacks
13 commentaires

Excellent article Mikael!
Vivement la suite ;)

M-L (le 15 septembre 2008)  - #1

Tout à fait d’accord avec M-L, un excellent article qui explique assez bien ce qu’est l’AOP aux initiés Java.
En attendant de passer à la pratique…

En tout cas, Geekeries gagnerait beaucoup à être plus fourni en articles de cette qualité ;)

Soso (le 15 septembre 2008)  - #2

Excellent article ! J’ai du coup très envie de m’y mettre !!! Bravo !!!

Par contre, j’aimerai bien savoir comment ça se passe à l’execution. Il faut rajouter un truc quelque part ? Un appel au framework ? Les aspects sont des fichiers .java inclus dans le classpath ?

Merci d’avance.

twingocerise (le 18 septembre 2008)  - #3

Si tu utilises AspectJ en compile time weaving, à l’exécution il faut juste ajouter un petit jar aspectjrt.jar.
Pour démarrer, le mieux est d’installer le plugin AspectJ pour Eclipse http://www.eclipse.org/ajdt/ et de lancer les exemples.

Mikaël (le 19 septembre 2008)  - #4

Si tous les blogs pouvaientt être aussi jolis & intéressants que celui-ci, ce serait biien ;) bonne continuation ! a+

Arrangeur (le 27 septembre 2008)  - #5

Merci pour les encouragements !

Soso (le 29 septembre 2008)  - #6

Super article !
Vive l’AOP o/

Johan (le 19 novembre 2008)  - #7

simple et tres interessant ….merci :)

souma (le 3 février 2009)  - #8

Merci pour cet article clair et ludique. Je ne connait pas l’AOP mais ça donne envie de pratiquer

Seb (le 11 juin 2009)  - #9

I found this site using google.com And i want to thank you for your work. You have done really very good site. Great work, great site! Thank you!

Sorry for offtopic

CurryType (le 14 novembre 2009)  - #10

j’aimerais bien de m’aider pour rouver des livres qui explique bien les concepte s de la POA merci

selma (le 18 janvier 2010)  - #11

Désolé, les commentaires sont fermés pour le moment.