Introduction aux bases de données NoSQL faite pour l'Ensim (Ecole Nationale Supérieure d'Ingénieurs du Mans), niveau Master. Introduction à la Meta-programmation puis présentation de l'AOP et de ses cas d'usage. Détails sur AspectJ permettant d'implémenter en Java les principes AOP.
2. Quelques mots...
Laurent Broudoux
Le jour ...
Architecte IT Senior chez MMA
Mots-clés : Java, SOA, Agile, Software factories
La nuit …
Coder, geek, open source comitter (voir http://github.com/lbroudoux)
Me joindre / suivre
@lbroudoux
laurent.broudoux@gmail.com
http://lbroudoux.wordpress.com
4. Que sont les Métadonnées ?
●
Des données à propos d'autres données ou des données
décrivant d'autres données
–
Structurale (conjoint à la création de la donnée)
●
●
Valeurs possibles
●
Relations avec d'autres données
●
–
Type
Objectifs
Descriptives (après la création de la donnée)
●
Conditions de création
●
Contexte d'existence
●
Etc ...
5. Comment définir des Métadonnées ?
●
De façon implicite
–
●
En suivant des conventions
De façon explicite
–
A l'extérieur du code
●
–
Configuration : XML, YML, …
A l'intérieur du code
●
Commentaires
–
●
Javadoc (@author, @version, @since, ...)
Code lui-même
–
–
–
Meta tags HTML (<meta name=''author'' content=''L. Broudoux''>)
Annotations Java (@Override, @Deprecated, …)
Domain Specific Language pour les langages dynamiques
6. Méta Programmation ?
●
Ecriture de programmes qui écrivent ou manipulent
d'autres programmes ou eux-même en se basant sur des
métadonnées.
« It is about programming at the meta level, that is
about changing your interpreter or changing how code
is compile »
- Ralph E. Johnson
7. Bénéfices
●
Lors de la conception
–
Modularité
●
–
●
Séparation des préoccupations
Evolutivité
Lors du développement
–
Gain de productivité, temps
●
En évitant le code boiler plate
–
Minimisation du LOC
–
Simplicité
8. Un process pour la Métaprogrammation
Comment exprimer les Métadonnées ?
Définition des Métadonnées
Comment extraire,
récupérer les
Métadonnées associées au
programme courant ?
Sous quel forme (implicite vs explicite,
externe vs interne) ?
Processing des Métadonnées
Comment opérer des
transformations du
programme courant en
fonction des Métadonnées
trouvées ?
Interprétation des Métadonnées
9. Un exemple d'application (i)
●
Soit une méthode permettant de retrouver les
utilisateurs ayant le même prénom …
–
Imaginons la intégrée dans une classe DAO
10. Un exemple d'application (ii)
●
On désire maintenant ajouter une méthode permettant
de retrouver les utilisateurs ayant le même nom …
–
Imaginons la intégrée dans la même classe DAO
Quelles différences ?
11. Un exemple d'application (iii)
●
●
En mettant en œuvre les principes de la Métaprogrammation, on peux imaginer ne plus avoir à écrire ces
méthodes !
On décide d'une convention permettant de faire la
correspondance …
… on implémente la transformation nécessaire pour
ajouter ces fonctions !
13. Un processus pour la Métaprogrammation
Comment exprimer les Métadonnées ?
Définition des Métadonnées
Comment extraire,
récupérer les
Métadonnées associées au
programme courant ?
Sous quel forme (implicite vs explicite,
externe vs interne) ?
Processing des Métadonnées
Comment opérer des
transformations du
programme courant en
fonction des Métadonnées
trouvées ?
Interprétation des Métadonnées
14. Conventions
●
●
●
Définition des Métadonnées
La façon la plus simple est de définir des conventions :
de nommage ou de localisation.
Attention : le bon respect de ces conventions ne peut
souvent pas être vérifié !!
Exemples :
–
Nommage du test case de Foo : FooTest
–
Nommage des méthodes liées à la persistance d'objets : save(),
update(), delete(), findAll(), findBy*(), …
–
Localisation des classes persistantes dans /grails-app/domain
pour Grails
On nomme cette approche « Convention Over Configuration »
15. Paramétrage Externe
●
●
Définition des Métadonnées
On décrit dans un fichier annexe des propriétés,
comportements ou contraintes supplémentaires
d'éléments de l'application.
Exemples :
–
ejb-jar.xml pour EJB
–
routes.rb pour RoR
16. Annotations Java
●
●
Définition des Métadonnées
Depuis Java 5, le langage offre la possibilité de définir
ses propres annotations
Une annotation se définit dans un fichier .java avec le
mot clé @interface
Cible : une classe, une
méthode, un champs
Rétention : seulement dans le
source, dans le bytecode mais
non accessible, dans le
bytecode et accessible
Attributs : éléments de
configuration des métadonnées portées par
l'annotation.
17. Annotations Java
●
Définition des Métadonnées
Une annotation s'applique en important simplement la
classe correspondante et en utilisant le marqueur
@<Mon annotation> avant l'élément visé (classe,
méthode ou champs)
18. Domain Specific Language
●
●
Définition des Métadonnées
Dans le cadre des langages dynamiques, on peut utiliser
des DSL directement embarqués dans le code mais en
dehors de la partie algorithmie.
Exemples :
–
Contraintes dans Groovy/Grails
–
Règles de routage dans RoR
19. Un processus pour la Métaprogrammation
Comment exprimer les Métadonnées ?
Définition des Métadonnées
Comment extraire,
récupérer les
Métadonnées associées au
programme courant ?
Sous quel forme (implicite vs explicite,
externe vs interne) ?
Processing des Métadonnées
Comment opérer des
transformations du
programme courant en
fonction des Métadonnées
trouvées ?
Interprétation des Métadonnées
20. Réflexivité et chargeurs de
classes
●
Processing des Métadonnées
Pour les conventions de nommage, les APIs de
réflexivité sont vos amies !
–
–
Classes groovy.lang.MetaClass et
groovy.lang.ExpandoMetaClass en Groovy
–
●
Packages java.lang.reflect en Java
Méthodes instance_variables(), instance_methods() et
respond_to ?() en Ruby
Pour les conventions de localisation, les chargeurs de classes
sont vos amis !
–
URLClassLoader sur les langages de la JVM
–
Directives require en Ruby ou Python
21. Annotations Java
●
Processing des Métadonnées
Au runtime
–
Depuis Java 5, il est possible de récupérer
programmatiquement les annotations d'un objet
22. Annotations Java
●
Processing des Métadonnées
Au build time
–
Depuis Java 6, il est possible de récupérer
programmatiquement les annotations d'un objet pendant
la compilation en créant un AnnotationProcessor.
●
Il est alors possible d'interagir avec les messages de
diagnostic du compilateur et enchaîner d'autres actions ...
23. Un processus pour la Métaprogrammation
Comment exprimer les Métadonnées ?
Définition des Métadonnées
Comment extraire,
récupérer les
Métadonnées associées au
programme courant ?
Sous quel forme (implicite vs explicite,
externe vs interne) ?
Processing des Métadonnées
Comment opérer des
transformations du
programme courant en
fonction des Métadonnées
trouvées ?
Interprétation des Métadonnées
24. Proxy dynamique Java
●
Interprétation des Métadonnées
Le principe :
–
Le proxy est un objet généré dynamiquement par la JVM afin
d'être vu comme implémentant 1 ou plusieurs interfaces,
–
Le proxy ne porte pas d'implémentation, il délègue le tout à un
InvocationHandler
–
L'InvocationHandler peut agir en tant que « décorateur » d'un
autre objet wrappé
Client
Proxy
Invocation
Handler
Vu comme un seul objet implémentant
une ou plusieurs interfaces
Cible
25. Proxy dynamique Java
●
Interprétation des Métadonnées
L'utilisation
–
On implémente l'interface java.lang.reflect.InvocationHandler
–
On appelle la méthode newProxyInstance() sur la classe
java.lang.reflect.Proxy en en fournissant les interfaces devant
être implémentées et l'invocation handler
–
C'est tout !
26. Proxy dynamique Java
Interprétation des Métadonnées
●
Une des fonctions les plus puissantes de la JVM !
●
Avec les proxies dynamiques, on peut implémenter :
–
Des politiques de sécurité pour l'accès aux objets,
–
La transparence des communications réseaux entre
objets,
–
Des applications utilisant multiples versions
incompatibles de la même librairie,
–
Toutes les transformations de comportement possibles et
imaginables pour un programme !
27. Transformations complètes
●
Interprétation des Métadonnées
Modification de la structure du code
–
–
Ajout / modification de comportements
–
●
Abstract Syntax Tree (AST) dans les langages dynamiques
Modification de hiérarchies de classes (voir gestion de
pseudo héritage multiple même en Java)
Modification directe du bytecode
–
Application du principe des proxies dynamiques aux
classes
●
Voir ASM, CGLib ou Javassist
29. Programmation Orientée Aspect
« La Programmation Orientée Aspect est un paradigme
de développement qui vise à augmenter la modularité
en permettant de traiter de façon indépendante les
cross-cutting concerns (préoccupations secondaires ou
orthogonales) »
- Gregor Kiczales, 1990s
30. Cross-cutting concerns (i)
« Lorsque qu'un client valide un panier, il faut vérifier
que le contenu est disponible et sauvegarder la
commande en base. »
Expert métier
Ventes
31. Cross-cutting concerns (ii)
« Lorsque le panier est validé, il faut vérifier que l'on
dispose bien d'un client correctement identifié / loggé »
Expert Sécurité
32. Cross-cutting concerns (iii)
« A chaque validation de panier, je veux tracer l'appel
pour connaître : les performances, les erreurs
potentielles, ... »
Exploitant
34. Cross-cutting concerns (v)
« Les 2 systèmes Vente et Approvisionnement doivent
être cohérents »
Expert SI
Quelle proportion
de ce code dédiée
à la préoccupation
principale ?
35. Cross-cutting concerns (vi)
●
Quelles autres réponses purement objet ?
–
Traiter par hiérarchie de classes ?
●
●
TxCart < StockCart < TracableCart < SecuredCart < Cart ?
Réponses de l'AOP
–
Gérer chaque concern indépendamment et fusionner le tout
dans une étape d'assemblage !
Vente (Cart)
Sources
TX
Appro (Stock)
Securité
Traces
weaving
Application
36. AspectJ
●
●
Première implémentation Java des principes de l'AOP. Provient du centre
de recherche de Xerox. Version 1.0 en 2002.
AspectJ est une extension du langage Java gérée par la fondation Eclipse
(voir http://www.eclipse.org/aspectj)
●
AspectJ implémente le weaving en faisant de la réécriture de bytecode. 4
types de weaving :
–
Compile-time : le code source de l'application et celui du langage aspect sont
fusionnés avant d'être transformé en bytecode
–
Link-time : la fusion se passe après la production de bytecode pour le langage
primaire et le langage aspect
–
Load-time : la fusion de bytecode se passe lors du chargement par le
ClassLoader
–
Run-time : la JVM est responsable de la détection et l'exécution des aspects
38. AspectJ – concepts principaux
●
Join point
–
●
Pointcut
–
●
Du code extérieur à exécuter quand un join point est atteint dans le code de
l'application
Inter-type declaration
–
●
Une expression permettant d'identifier des join points au sein d'un
programme
Advice
–
●
Un « point » d'exécution prédictible d'une application
Un mécanisme permettant d'ajouter des attributs ou des méthodes à des
classes précédemment définies
Aspect
–
Une structure analogue à une classe qui encapsule join points, pointcuts,
advices et inter-type declaration.
39. AspectJ – Join points
●
Notion la plus importante du paradigme : un « point »
clairement défini dans l'exécution d'un composant.
1
A a = new A();
a
2
a.b
5
3
4
6
7
9
10
13
8
a.a();
11
12
40. AspectJ – Pointcuts (i)
●
Exemples
Designator
Description
execution
Matche l'exécution d'une méthode ou d'un constructeur
call
Matche l'appel d'une méthode ou d'un constructeur
initialization
Matche l'exécution du premier constructeur de la classe
handler
Matche les exceptions levées
get
Matche la référence à un attribut de classe
set
Matche l'assignation d'un attribut de classe
this
Retourne l'objet associé à un join point particulier ou restreint la
portée d'un join point en spécifiant un type de classe
target
Retourne l'objet cible d'un join point ou restreint la portée d'un join
point
args
Expose les arguments d'un join point ou restreint la portée d'un join
point en spécifiant des arguments
cflow
Retourne les join points dans le flux d'exécution d'un autre join
point
cflowbelow
Retourne les join points dans le flux d'exécution d'un autre join
point sans inclure le join point courant
41. AspectJ – Pointcuts (ii)
●
Quelques exemples ...
Expression
Commentaire
pointcut allStringMethods(String str) :
call(public * HelloWorld.*(..) &&
args(str))
Matche toutes les méthodes publics de HelloWorld
ayant 1 argument de type String. L'argument est
récupéré pour utilisation dans l'advice
pointcut construct() :
initialization(new(..))
Matche les constructeurs de tous les objets dans le
système !
pointcut constructRoleList() :
initialization(RoleList.new())
Matche le constructeur de la classe RoleList et de
tous ses ascendants (ArrayList, AbstractList, List)
pointcut userFirstname(User user) :
execution(public String getFirstname())
&& this(user)
Matche la méthode getFirstname() et rend
disponible l'objet courant de l'invocation disponible
pour l'advice
poincut handle(UserNotFoundException
e) : handler(UserNotFoundException) &&
args(e)
Matche le lancement d'une exception donnée par
n'importe quelle méthode. Rend accessible
l'exception pour l'advice.
pointcut getB() : call(* * B.getB());
pointcut a() : call(public void A.a());
pointcut getBInA() : getB() &&
cflow(a()) ;
Matche l'exécution de la méthode getB() seulement
lorsque appelée depuis la méthode a()
42. AspectJ – Advices (i)
●
Référence
Advice
Description
before
S'exécute avant le join point désigné.
A utiliser pour tout ce qui concerne l'insertion de pré-conditions ou garantir
l'initialisation d'objets (ouverture de connexion DB, etc ...)
after
S'exécute après le join point désigné.
A utiliser pour tout ce qui concerne les post-conditions, les opérations de
filtrages ou garantir la libération de ressources (fermeture connexion)
after returning
Même chose que after. Permet en plus de garantir que le join point visé
s'est exécuté avec succès.
after throwing
Même chose que after. Permet en plus de garantir que le join point visé s'est
achevé en lançant une exception.
around
Remplace l'exécution du join point désigné.
A utiliser lorsque l'on veut conditionner l'exécution d'un join point ou
complètement remplacer son exécution.
43. AspectJ – Advices (ii)
●
Eléments disponibles dans les advices
–
thisJoinPoint : attribut permettant d'accéder aux détails
du join point courant (déclaration et instance)
–
thisJoinPointStaticPart : attribut permettant d'accéder
aux détails de déclaration du join point courant
–
proceed() : méthode permettant de lancer l'exécution du join
point courant
44. AspectJ – Inter-type Declarations
●
Les ITDs permettent :
–
l'ajout d'attributs ou de méthode à une classe,
–
L'ajout de comportement via la modification de la hiérarchie
d'héritage ou d'implémentation.
Après compilation et weaving, la classe User implémente l'interface Comparable. Il est
donc possible d'invoquer la méthode compareTo() sur les instances de User.
45. AspectJ – Aspect
●
●
●
Un aspect est simplement la structure permettant de regrouper les
éléments vus précédemment et présentant une certaine cohérence
Dans AspectJ, on décrit un aspect : soit dans un fichier .aj, soit dans
une classe Java annotée avec @Aspect
Cycle de vie d'un Aspect
–
Par défaut, les aspects sont des singletons. On ne peut donc
pas leur faire porter d'état !
–
Il est possible de forcer la création d'un Aspect par objet en
utilisant la directive perthis(this(obj))
–
Il est possible de forcer la création d'un Aspect par flux en
utilisant la directive percflow(<pointcut>)
46. Quelques usages de l'AOP
●
●
●
●
●
Gestion de toute la « machinerie » technique : sécurité,
transactions, connexions réseaux et base de données,
serialization...
Gestion des préoccupations liées au diagnostique : log et traces,
mesures de performances, exception handling particulier, …
Gestion de toute la « machinerie » objet : getters et setters,
toString(), equals(), …
Déploiement de patch : nouveaux advices venant remplacer les
précédents !
Assemblage de métiers différents : vente et approvisionnement,
vente et connaissance client, ...
47. Points clés
●
Les primitives de méta-programming s'organisent selon 3 phases :
–
–
Processing de méta-données,
–
●
Définition de méta-données,
Interprétation de méta-données et transformation de programme.
Assemblons ces primitives et imaginons :
–
–
L'interception par un processeur lors de la compilation qui génère lui-même des
aspects,
–
●
L'usage d'annotations Java ou le respect de conventions de nommages en phase de
développement,
Aspects qui peuvent alors être fusionnés pour enrichir les composants produits ...
… tout en conservant un code initial clair, léger et orienté vers l'objectif métier du
composant !