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.
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
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
En AOP, nous allons procéder de manière similaire :
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 :
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 :
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 :
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 » :
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 :
Vous pouvez ensuite appeler les méthodes injectées dans les classes du package domain :
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 :
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 :
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.
Excellent article Mikael!
Vivement la suite
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é
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.
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.
Si tous les blogs pouvaientt être aussi jolis & intéressants que celui-ci, ce serait biien bonne continuation ! a+
Merci pour les encouragements !
Super article !
Vive l’AOP o/
simple et tres interessant ….merci
Merci pour cet article clair et ludique. Je ne connait pas l’AOP mais ça donne envie de pratiquer
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
j’aimerais bien de m’aider pour rouver des livres qui explique bien les concepte s de la POA merci
Désolé, les commentaires sont fermés pour le moment.
Introduction
Problèmes
Solutions
Autres concepts
Utilisations dans des « vrais » projets
Conclusion
Initiation à la Programmation Orientée Aspect
1. Identifier
3. Tisser
4. Vous avez toujours oublié quelque chose
5. Vous êtes bien outillé
1. inter-type declaration
2. Error declaration
Vous avez aimé cet article ? Vous aimerez sûrement aussi…
Vous devriez nous suivre sur Twitter ici et rejoindre notre groupe sur Facebook ici.
Trackbacks & Pingbacks
13 commentaires
- 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.
- 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 à
firePropertyChangedans 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.
-
Rétrolien by
www.fuzz.fr on
18 septembre 2008 @
10:18
– #3
-
Ping by
Un peu plus loin avec Wicket et Spring : Vive l’AOP ! – MyTekLog on
2 juillet 2009 @
23:32
– #11
- Ne surtout toucher à rien tant que ça fonctionne
- 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.
- 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.
- 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)
- Refaire votre CV pour pour pouvoir quitter ce traquenard au plus vite. Ca fera toujours bien de pouvoir mettre « AOP » dedans.
- identifier ce que l’on trace, par exemple « à l’entrée et à la sortie de toutes les méthodes publiques du package
com...services« - déterminer le format de la trace : niveau (
DEBUG,INFO, …), impression des arguments, de la stacktrace, … - modifier tout le code. Travail de singe répétitif et sans intérêt
- s’apercevoir qu’on a oublié de tracer quelque chose d’essentiel
- Goto 2
Object around() : tracedMethods() {
System.out.println("Avant" + message + thisJoinPoint.getSignature().toString());
try {
return proceed();
}
finally {
System.out.println("Après" + message + thisJoinPoint.getSignature().toString());
}
}
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());
}
}
}
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;
}
}
Customer cust = new Customer();
cust.setId(10l);
declare error : within(com..domain.*) && call(* com..services.*.*(..)) : "Un domain object ne doit pas appeler de service";
M-L
Soso
twingocerise
Mikaël
Arrangeur
Soso
Johan
souma
Seb
CurryType
selma MikaëlAOPaspectAspectJjavaparadigmeprogrammationspringapplearticlegameshigh-tech #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 Par
15 sept, 2008Catégories :
, , ,