On a jamais parler de Spring-boot [9], ça fait même assez longtemps que l’on a pas parler Java! Du coup comme nous sommes lancé dans les articles sur RabbitMQ et que ce message broker est assez courant dans ces écosystèmes, on va en profiter!
On va donc finir cette liste d’articles [2, 3] avec une mise en situation en réalisant une application client server implémentée avec Spring-boot (sur lequel nous reviendrons de façon plus général dans un autre article).
Affichage des articles dont le libellé est Spring. Afficher tous les articles
Affichage des articles dont le libellé est Spring. Afficher tous les articles
dimanche 23 février 2020
vendredi 14 juin 2019
Changement de taff!!
Et bien cela va faire presque deux mois que je n’ai rien écrit! non je n’ai pas mis de coté le blog. Mais j’avoue que ces deux derniers mois ont été très chargé. D'une part, j’ai déménagé! Encore! Ce qui en terme d’organisation pour écrire, perturbe un peu les habitudes! Et aussi j’ai changé de boulot! aussi et oui ça faisait un an que j'étais chez Capemini mais je m’ennuyais.
Du coup je suis rentré chez Norsys, je me suis laisser séduire par le dynamisme, le côté écolo, et une volonté de bien faire… et j’ai débuté le premier avril dernier… (sans blague)
Alors je disais dans un précédent article que d’ailleurs, souvent on choisi une boite surtout pour les gens qui y travaillent et le fait que l’on veuille bosser avec eux… et bien là clairement, j'étais en plein dedans! et je regrette pas! Alors initialement, c’est surtout parce que Christophe venait lui aussi d’y rentrer (bon j’ai du coup délaissé David … ^^) mais au final je l’ai assez peu croisé et j’ai dû me confronter aux effectifs en présence….
Honnêtement, ça a quand même été un choc. J’ai vu sur ces dernières années pas mal de choses, j’ai bien sûr eut des haut et des bas, j’imagine comme tout le monde… mais je ne mesurais pas à ce point comment j’avais surtout capitalisé sur des technologies qui avaient fait leur temps. D’un coup il m’a donc fallu intégrer dans mes outils du quotidien plein de nouveaux concepts et de manière de travailler….
Alors non bien sur je ne parle pas des processus de développement, de l’agilité… mais plutôt le fait que typiquement passer de Java 8 (et encore) à Java 11 implique quelques mise à jours qui même si elles ont été réalisé à titre personnelle, cela reste qu’un débroussaillage par rapport aux besoins que l’on peut en avoir dans une utilisation quotidienne!
Alors quand même! heureusement que j’ai bien débroussaillé ces dernières années! Je ne regrette pas ces heures de rafraîchissement sur Postgres et la normalisation des SGBD-R, ni cette monté en compétence sur docker qui m'a permis d'être OP des le GO sur les projets. Cependant, changer de projet, et partir sur de nouvelles techno c’est quand même la loterie et forcément on tombe toujours sur des numéros que l’on avait pas choisi!
Du coup même en ayant fait du String sur ces 10 dernières années, j’avoue que j’ai mesuré à quel point je n'étais qu’un utilisateur de cette technologie. Celle ci est vraiment trop magique et devenir vraiment compétent sur cette technologie nécessite de passer la montagne de la stupidité (cf Dunning-Kruger)
Alors oui bien sur j’ai d’autres compétences (du moins je l'espère ^^) mais quand celles ci ne sont pas à utiliser dans le cadre d’un projet, le sentiment qui en ressort est un gros syndrome de l’imposteur!
Enfin donc tout ça pour dire que ces deux derniers mois ont été très riches, que je n’ai pas eut le temps de consolider quoique ce soit mais, que je reviens doucement mais surement avec plein de sujet en tête… (sans oublier ceux qui étaient en cours….)
Du coup en aperçu, on parlera dans les prochains mois, de
Peut être aussi quelques sujet sur la sécurité (Spring Security, OAuth2, JWT, etc…)
Sans oublié je le disais les sujets sur l’IA:
Et bien d’autres choses comme l'héritage de table dans Postgres, SpringData, peut être un peu de bayésianisme si enfin j’arrive à me poser sur ce sujet et meme un peu de réseau!
Donc à très vite pour le prochain article!
Du coup je suis rentré chez Norsys, je me suis laisser séduire par le dynamisme, le côté écolo, et une volonté de bien faire… et j’ai débuté le premier avril dernier… (sans blague)
Alors je disais dans un précédent article que d’ailleurs, souvent on choisi une boite surtout pour les gens qui y travaillent et le fait que l’on veuille bosser avec eux… et bien là clairement, j'étais en plein dedans! et je regrette pas! Alors initialement, c’est surtout parce que Christophe venait lui aussi d’y rentrer (bon j’ai du coup délaissé David … ^^) mais au final je l’ai assez peu croisé et j’ai dû me confronter aux effectifs en présence….
Honnêtement, ça a quand même été un choc. J’ai vu sur ces dernières années pas mal de choses, j’ai bien sûr eut des haut et des bas, j’imagine comme tout le monde… mais je ne mesurais pas à ce point comment j’avais surtout capitalisé sur des technologies qui avaient fait leur temps. D’un coup il m’a donc fallu intégrer dans mes outils du quotidien plein de nouveaux concepts et de manière de travailler….
Alors non bien sur je ne parle pas des processus de développement, de l’agilité… mais plutôt le fait que typiquement passer de Java 8 (et encore) à Java 11 implique quelques mise à jours qui même si elles ont été réalisé à titre personnelle, cela reste qu’un débroussaillage par rapport aux besoins que l’on peut en avoir dans une utilisation quotidienne!
Alors quand même! heureusement que j’ai bien débroussaillé ces dernières années! Je ne regrette pas ces heures de rafraîchissement sur Postgres et la normalisation des SGBD-R, ni cette monté en compétence sur docker qui m'a permis d'être OP des le GO sur les projets. Cependant, changer de projet, et partir sur de nouvelles techno c’est quand même la loterie et forcément on tombe toujours sur des numéros que l’on avait pas choisi!
Du coup même en ayant fait du String sur ces 10 dernières années, j’avoue que j’ai mesuré à quel point je n'étais qu’un utilisateur de cette technologie. Celle ci est vraiment trop magique et devenir vraiment compétent sur cette technologie nécessite de passer la montagne de la stupidité (cf Dunning-Kruger)
Alors oui bien sur j’ai d’autres compétences (du moins je l'espère ^^) mais quand celles ci ne sont pas à utiliser dans le cadre d’un projet, le sentiment qui en ressort est un gros syndrome de l’imposteur!
Enfin donc tout ça pour dire que ces deux derniers mois ont été très riches, que je n’ai pas eut le temps de consolider quoique ce soit mais, que je reviens doucement mais surement avec plein de sujet en tête… (sans oublier ceux qui étaient en cours….)
Du coup en aperçu, on parlera dans les prochains mois, de
- ReST
- Spring Boot
- JavaScript
- Reactor
Peut être aussi quelques sujet sur la sécurité (Spring Security, OAuth2, JWT, etc…)
Sans oublié je le disais les sujets sur l’IA:
- Mnist
- Les arbres de décision
- Les forêts aléatoire
Et bien d’autres choses comme l'héritage de table dans Postgres, SpringData, peut être un peu de bayésianisme si enfin j’arrive à me poser sur ce sujet et meme un peu de réseau!
Donc à très vite pour le prochain article!
Libellés :
bayesianisme,
Capgemini,
Dunning-Kruger,
IA,
Java,
Norsys,
PostGres,
reactif,
REST,
Spring
vendredi 28 décembre 2018
OSGI : Concepts généraux
Aujourd’hui nous allons parler d’un framework qui me tient à coeur: OSGI. Cela faisait un moment que je voulais écrire un article sur celui ci mais beaucoup d’autre sujet, se sont trouvé être plus intéressant sur le moment.
À vrai dire, OSGI est clairement très intéressant mais l’utilisant depuis déjà près de 8 ans, malgré l'intérêt qu’il a intrinsèquement, il ne porte plus pour moi ce goût de nouveau que peuvent avoir les sujets comme l’IA... , c’est évident.
Pourtant voilà, je me suis dit, il est temps de faire le point sur le sujet et de le mettre un peu plus sous les projecteurs, car mine de rien, quasiment tout le monde l’a un jour utiliser, mais très peu de gens le savent!
Mais repartons du début!
Historique
OSGI [OSGI], [wikipedia] ou Open Service Gateway Initiative est un framework de gestion de composants et de services. Initialement prévu pour le monde de l’embarqué (si si ^^), il a été utilisé par des constructeur comme Volvo, BMW ou encore Cisco et s’emploie dans les produits logiciels à destination du monde de la domotique et des systèmes de sécuritésNous le verrons plus en détail mais pourquoi OSGI? parce que ce framework est léger (en dehors de la JVM) et permet le chargement et le déchargement de ses librairies à chaud tout en facilitant la manipulation de ces dernières dans des versions différentes simultanément.
Aujourd’hui la spécification 7 de OSGI forme un pavé de plus de 1000 pages de documentation [OSGI-V7-CORE] et il nous est impossible d’en faire un résumé exhaustif ici cependant dans cet article nous tâcherons de passer en revu les concepts pivots de OSGI pour en comprendre les mécanismes clefs puis nous nous intéresserons aux différentes implémentations ainsi que leur intégrations dans les frameworks plus classique (mais qu’on sait pas que OSGI est dedans…. O_o)
OSGI concepts généraux
OSGI est donc un framework Java. Conçu il y a près de 18 ans, celui ci est un peu partout sans que personne ne le sache vraiment. On le trouve dans la plupart de nos serveurs d’applications JEE, jusque dans nos IDE comme eclipse qui à fondé sa architecture dessus… si si…Bien sûr ce n’est pas parce que quelque chose est utilisé partout que forcément c’est bien…. c’est vrai on le voit tous les jours avec le framework Spring! non blague à part (ou pas), OSGI est partout mais pour de bonnes raisons et c’est justement sa discrétion qui en fait sa force et sa pertinence.
En effet, OSGI est un framework amenant des moyen technique de mise en oeuvre logicielle mais aussi des moyens conceptuelles, apportant avec lui quelques paradigme de modélisation simple mais efficace laissant libre le développeur de faire ce qu’il veut tout en lui garantissant plus de souplesse à son application.
Il fournit pour cela une implémentation à deux paradigmes importants en développement logiciel:
- la modularité qui permet la gestion et l’articulation logique des éléments de l’application
- les services qui fournissent les moyens de donner une opérationnalitée à ces éléments en leur donnant du sens.
La modularité dans OSGI
Sans aller trop loin dans le débat de la modularité [serv-side], on rappellera quelques principe de façon à poser ce à quoi nous attendre dans OSGILa modularité est une approche de conception permettant la séparation des préoccupations, que celles-ci soient fonctionnelle, ou technique.
Bien sur il existe différents degrés de modularité et celle-ci s’appliquant sur la solution logicielle à des niveaux très variés.
Il est ainsi possible de concevoir de façon modulaire tant au niveau de l’architecture système que de l'architecture logicielle. Quel que soit l’objectif métier, le but est de permettre de scinder les problématiques et amener du découplage facilitant ainsi: la réalisation, le test, la compréhension, la maintenance, la mise à jour, etc...
Pour mettre en oeuvre de la modularité dans une architecture, il existe divers solutions et implémentations: certains design pattern comme l’observateur [observ], l’association du pattern proxy et factory [invochand] ou encore l’utilisation de contrat de services avec des interfaces et des API comme illustré ci dessus.
OSGI propose une autre alternative en se situant à la croisé des chemins de ces approches. Nous en avions parlé dans l’article [whriteboard]. En résumé, la modularité consiste en la construction de composants interdépendants via des interfaces (API). Des lors, l’utilisation d’un module par un autre passe par une api et masque donc l’identité du composant réellement utilisé, c’est le principe du masquage de l'implémentation permettant ainsi découplage, réutilisation, capacité à être testé etc…
Cependant, on constate une limite a l’utilisation des contrats de service et des API : il faut etre capable de re-associer les composants interdépendants [serv-side]. Il faut pour cela utiliser des patterns comme la Factory et au final déplacer le couplage vers la factory [whiteboard].
Alors bien sur il existe d’autres approches pour répondre à cette problématique avec les framework d’IoC comme Spring (implémentant le pattern Hollywood [hollywood]) utilisant des xml ou des annotations. Pourtant ces solutions sont assez peu satisfaisantes car elles confient les détails d'implémentations à un acteur tiers qui aura la charge de faire communiquer les composants entre eux sur la base d’une description donné statiquement. Finalement cela re-créera un couplage car si en effet dans Spring (par exemple) on spécifie la dépendance d’un composant via une API et des interfaces, il faudra lui spécifier un à moment l'implémentation qui y répondra et cela de façon statique par configuration.
Le pattern Broker
OSGI fournit une alternative en utilisant le pattern Broker [broker] permettant, au travers d’un registre de bundle (nom des composants ou modules OSGI) et de service, de déporter le couplage en dehors de la préoccupation des composants mis en interaction.Il s’agit avant tout d’un pattern d’architecture (non d'implémentation) qui classiquement s’utilise dans le cadre de service de messagerie comme JMS mais qui ici se focalise sur la mise en collaboration des bundles entre eux, à la demande, et ce géré par le registre.
Dans sa mise en oeuvre, un ensemble de composants (les bundles) vont s’enregistrer auprès du registre comme fournisseur de services liés à différents contrats d’interfaces. Ensuite, par l'intermédiaire du registre, ils seront appelé par d’autres bundle ayant besoin des services remplissant les contrats d’interfaces souhaités.
Dans ce principe, aucun bundle ne se connait directement, tout passe par le registre qui aura à gérer l’ensemble des services offerts et les clients qu’il faudra rediriger vers les bonnes implémentations.
Les services
OSGI est une implémentation du pattern Broker. Il s’agit donc en réalité d’un framework permettant la conception logicielle de façon modulaire mais et surtout de construire des applications selon une logique orienté service.Ainsi comme nous venons de le voir la modularité va nous amener à séparer les préoccupations, les services vont en plus nous amener à réfléchir nos bundles selon le rôle qu’ils ont à mener dans notre application de façon à en exposer juste et seulement leur valeur ajouté.
L’idée est la encore et toujours la modularité, mais en ayant une forte décomposition, on maximise alors le re-use car un service implémentant un contrat de service pourra être mis à disposition par le registre à tous les bundles en ayant le besoin.
Vous allez dire mais ça Spring le fait déjà au final le registre, c’est le fichier xml de Spring.
Effectivement mais OSGI va plus loin:
- D’une part, l’utilisation du registre va autoriser un bundle à consommer un service à la demande et de s’en défaire, le couplage entre les bundles est dynamique et non permanent.
- La conséquence du point précédent que nous détaillerons plus loin dans l’article est que cela permet la mise en ligne des bundles sans que ces derniers n’aient eu le besoin de résoudre à leur chargement toute leur dépendance et service.
- Ceci nous amènera alors à la possibilité de gérer des versions concurrente d’un même service pour faciliter leur mise à jour à chaud.
Avec OSGI, la gestion des services par le registre des bundles nous laisse entrevoir quelques fonctionnalités dont nous n’avons pas vraiment l’habitude. Dans un projet classique, généralement nous pensons notre application comme un tout unique dont les éléments, librairies et services, devront évoluer conjointement. Mais avec OSGI, cette vision limitative devient caduc et il faudra penser l’application comme un système en perpétuel mutation dont les éléments nouveau côtoient des éléments anciens et dont les services seront utilisés selon le besoin.
Gestion des dépendances
Pour permettre des mécanismes de gestion à chaud des bundles et de la consommations dynamiques des services, OSGI va donc fournir un certain nombre de concepts focalisé sur une gestion un peu particulière des dépendances.Cette gestion des dépendances va s’exprimer selon deux types [10min]:
- les dépendances statiques
- les dépendances dynamiques
Statiquement
La gestion statique des dépendances dans OSGI est assez similaire à la gestion des dépendances Java réalisé avec le classpath à ceci prêt que celui ci est plus stricte car embarque avec les lui le detail des versions impliqué dans la dépendance.Cela paraît dans un premier temps très contraignant cependant cela permet en fait de faire vivre au sein du framework deux versions distinctes d’une même dépendance, laissant alors au composant qui en ont besoin d’utiliser celle avec laquelle il sera le plus en phase.
Ce type de dépendance se déclarent au sein du manifest java décoré et adapté au formalisme OSGI (nous verrons cela plus en détails dans le chapitre sur les bundles).
Ainsi les bundles lorsqu’ils seront chargé par le framework OSGI (on verra plus tard mais il s’agit de la phase d’installation) devront trouver dans leur classpath ou dans les classloader du framework les dépendances apportées par les autres bundles dans les versions attendues
Dynamiquement
Une fois installé, un bundle apporte un ensemble de dépendance statique, cependant, pour créer un minimum de couplage, il est préférable de s’appuyer autant que faire se peut sur le pattern broker.Ainsi pour cela le framework OSGI nous amene à construire des bundles définissant des contrats des services ne contenant que des informations spécifiques aux contrats de services.
Ainsi ces contrats seront ensuite implémentés par un autre bundle qui aura la charge de l'implémentation du contrat de service. Ainsi, ce bundle s'enregistrer auprès du registre et lorsqu’un autre bundle demandera au registre OSGI un service conforme au contrat de service défini dans le bundle contrat alors le registre fournit l'implémentation.
L'intérêt de cette approche est qu’il est possible alors de proposer divers implémentation d’un même contrat de service.
Conclusion
Nous avons dans les grandes lignes les principes généraux de OSGI, son paradigme de modélisation modulaire et orienté service.L'intérêt de son utilisation est double, d’une part de fournir une solution réelle de couplage des modules mais aussi la capacité de faire évoluer une application dynamiquement tout au long de son cycle de vie et ce, “à chaud”.
Il peut être intéressant maintenant d’en parcourir l’utilisation dans le concret (dans le prochain article sur OSGI) afin de mieux appréhender ses contraintes techniques.
Références
- [OSGI] https://www.osgi.org/
- [wikipedia] https://fr.wikipedia.org/wiki/OSGi
- [JSR8] https://www.jcp.org/en/jsr/detail?id=8
- [OSGI-V7] https://osgi.org/specification/osgi.core/7.0.0/index.html
- [equinox] http://www.eclipse.org/equinox
- [knopflerfish] https://www.knopflerfish.org/
- [concierge] http://concierge.sourceforge.net/
- [felix] http://felix.apache.org/
- [OSGI-V7-CORE] https://osgi.org/specification/osgi.core/7.0.0/framework.lifecycle.html#framework.lifecycle-frameworks
- [serv-side] http://www.theserverside.com/feature/OSGi-is-the-framework-for-all-modular-applications
- [whiteboard] https://un-est-tout-et-tout-est-un.blogspot.com/2018/11/whiteboard-pattern.html
- [broker] https://towardsdatascience.com/10-common-software-architectural-patterns-in-a-nutshell-a0b47a1e9013
- [observ] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-observateur.html
- [invocHand] https://un-est-tout-et-tout-est-un.blogspot.com/2018/04/invocationhandler.html
- [hollywood] https://dzone.com/articles/the-hollywood-principle
- [10min] http://davidmasclet.gisgraphy.com/post/2009/11/26/10-minutes-pour...comprendre-OSGI
Libellés :
broker,
bundles,
composants,
dependances,
equinox,
felix,
framework,
invocation handler,
Ioc,
Java,
OSGI,
services,
Spring
samedi 18 août 2018
JEE : CDI partie 2 : les patterns
Nous revoilà avec CDI, nous avions vu les principes de base de l'injection, son utilisation dans une factory et ensuite comment l'utiliser avec des servlets et des JSP. Aujourd'hui nous terminerons le sujet en traitant des implémentations de CDI des patterns observateur, décorateur (ou façade) et InvocationHandler.
Dans le framework CDI, ce pattern se traduit par l'utilisation de la classe générique Event<T> prenant en type l'information à transmettre (le vrai événement en fait) et l'annotation @Observes se chargeant de taguer le paramètre de type T d'une méthode devant recevoir l’événement, l'appel à celle ci se faisant alors automatiquement. Voyons cela dans le concret avec un exemple.
Considérons simplement que deux beans doivent communiquer entre eux (on va s'appuyer sur l'article précédent sur CDI [14]). On va considérer que CDIModelShortScope veuille prévenir CDIModel qu'une requête utilisateur vient d’être faite (en gros que l'objet vient de se creer et d'etre detruit)
On va donc modifier ce premier de façon a qu'il permette l’émission de messages en utilisant Event et en utilisant la méthode fire
On ajoute dans la classe CDIModel, le moyen d’écouter les messages:
Le comportement resultant produisant bien dans les log :
Plutôt efficace et pas cher... non? Passons au décorateur.
Regardons cela par un exemple:
Considérons un bean MyObject contenant une donnée membre "nom" de type String caracterisant l'identité de l'objet. Ce nom est recuperable via une méthode getName().
Nous souhaiterions alors une fonctionnalité nous permettant de façon transparente renvoyer le hash du nom a la place. (Par soucis de simplicité pour l'exemple on utilisera les annotations @Named et @ApplicationContext).
On va dans un premier temps proposer une solution avec un décorateur. Pour cela, il nous faut construire une classe ayant le même profil de classe que le bean décoré. On va définir une API commune sous la forme d'une interface.
Enfin définissons le décorateur: Celui se défini sous la forme d'une classe abstraite implémentant la même interface que le bean en utilisant les annotations @Delegate et @Decorator (et @Priority qui permet de donner un ordre de prise en charge des beans si plusieurs décorateurs sont a l'oeuvre sinon cela est équivalent a définir le décorateur dans le beans.xml).
Ainsi le bean prendra la forme suivante:
et le décorateur:
La JSP affichant et utilisant la méthode getName nous renverra alors:
Ce pattern est un pattern permettant de positionner un objet entre le client faisant l'appel d'une méthode et la méthode elle même. En comparaison avec le décorateur, on pourrait se dire, "ok bon ca fait la meme chose" mais l'invocation handler a plutôt pour idée de modifier le comportement de la méthode d'une classe pour toutes ses instances. Un décorateur n'a pas cet objectif : il ne va pas se charger de toutes les instances de la classe, il peut éventuellement ne décorer que des objets spécifiques selon le besoin.
On pourrait donc en quelques sortes considérer que le décorateur a pour rôle de modifier le comportement en y ajoutant des éléments fonctionnel selon le cas alors que l'invocation handler aura un impact plus global et avec une orientation plus technique.
Voyons maintenant comment utiliser l'invocation Handler avec CDI.
Pour l'exemple imaginons que nous souhaitons compter le nombre de caractère du nom de l’objet que nous utilisions précédemment et l'affecter a une donnée membre dédiée.
Pour cela il nous faut tout d'abord construire une annotation maison permettant de tisser un lien entre le bean et notre futur intercepteur. Cette annotation devra alors porter l'annotation @InterceptorBinding. Appelons la CompteurAnnot:
On place ensuite cette annotation sur la méthode du bean qu'il s'agit d'intercepter
On va enfin créer notre intercepteur avec une nouvelle classe Compteur avec les Annotations @Interceptor et @AroundInvoke. Le premier est placé sur la classe et le deuxième sur la méthode qui devra être appeler a la place de celle du bean et réaliser le comptage. On y ajoutera notre annotation, afin que CDI sache faire le lien entre l'intercepteur et le bean associé.
Du coup en ajoutant l'appel a cette méthode getSizeName dans la JSP, celle ci nous donne maintenant la taille de la chaîne, l’intercepteur a donc bien fait son travail.
[2] https://docs.oracle.com/javaee/6/tutorial/doc/giwhl.html
[3] http://openwebbeans.apache.org/
[4] http://weld.cdi-spec.org/
[5] https://javaee.github.io/glassfish/
[6] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-decorateur.html
[7] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-observateur.html
[8] https://un-est-tout-et-tout-est-un.blogspot.com/2018/04/invocationhandler.html
[9] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-factory.html
[10] http://www.cdi-spec.org/news/2017/05/15/CDI_2_is_released/
[11] https://readlearncode.com/java-ee/introduction-to-cdi-producer-methods/
[12] https://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html
[13] https://rmannibucau.developpez.com/tutoriels/cdi/introduction-cdi
[14] http://un-est-tout-et-tout-est-un.blogspot.com/2018/08/jee-cdi-partie-1-linjection.html
Les patterns
Nous avons jusque la vu les fonctionnalités les plus importantes du framework CDI mais ca serait passer a coté d'autres fonctions bien pratiques que de s’arrêter ici. En effet comme énoncé plus haut, CDI fourni des versions adaptées à l'injection de certains designs patterns.Observateur
Voyons par exemple le pattern observateur [7] Je ne reviens pas sur ce pattern déjà décrit dans le blog mais il s'agit d'un modèle de communication réactif entre deux composants se notifiant par le biais d'un système d’événements.Dans le framework CDI, ce pattern se traduit par l'utilisation de la classe générique Event<T> prenant en type l'information à transmettre (le vrai événement en fait) et l'annotation @Observes se chargeant de taguer le paramètre de type T d'une méthode devant recevoir l’événement, l'appel à celle ci se faisant alors automatiquement. Voyons cela dans le concret avec un exemple.
Considérons simplement que deux beans doivent communiquer entre eux (on va s'appuyer sur l'article précédent sur CDI [14]). On va considérer que CDIModelShortScope veuille prévenir CDIModel qu'une requête utilisateur vient d’être faite (en gros que l'objet vient de se creer et d'etre detruit)
On va donc modifier ce premier de façon a qu'il permette l’émission de messages en utilisant Event et en utilisant la méthode fire
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @Named @RequestScoped public class CDIModelShortScope { @Inject private Event<String> event; @PostConstruct public void build() { System.out.println("Construction de CDIModelShortScope"); event.fire("Une nouvelle conexion"); } @PreDestroy public void destroy() { System.out.println("Destruction de CDIModelShortScope"); event.fire("Fin de vie CDIModelShortScope"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Named("model") @ApplicationScoped public class CDIModel { private String dataModel="les données du model"; public void updateOnConnect(@Observes String event) { System.out.println(event); } .... } |
Le comportement resultant produisant bien dans les log :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [#|2018-08-14T21:27:18.493+0200|INFO|glassfish 5.0||_ThreadID=27;_ThreadName=Thread-8;_TimeMillis=1534238838493;_LevelValue=800;| Construction de CDIModel|#] [#|2018-08-14T21:27:18.502+0200|INFO|glassfish 5.0||_ThreadID=27;_ThreadName=Thread-8;_TimeMillis=1534238838502;_LevelValue=800;| Construction de CDIModelShortScope|#] [#|2018-08-14T21:27:18.503+0200|INFO|glassfish 5.0||_ThreadID=27;_ThreadName=Thread-8;_TimeMillis=1534238838503;_LevelValue=800;| Une nouvelle conexion|#] [#|2018-08-14T21:27:18.505+0200|INFO|glassfish 5.0||_ThreadID=27;_ThreadName=Thread-8;_TimeMillis=1534238838505;_LevelValue=800;| Destruction de CDIModelShortScope|#] [#|2018-08-14T21:27:18.505+0200|INFO|glassfish 5.0||_ThreadID=27;_ThreadName=Thread-8;_TimeMillis=1534238838505;_LevelValue=800;| Fin de vie CDIModelShortScope|#] [#|2018-08-14T21:27:47.287+0200|INFO|glassfish 5.0||_ThreadID=132;_ThreadName=Thread-8;_TimeMillis=1534238867287;_LevelValue=800;| Destruction de CDIModel|#] |
Plutôt efficace et pas cher... non? Passons au décorateur.
Le Decorateur [6]
Je vous avais parler de pattern avec un "s" et bien CDI fourni aussi des implémentations pour quelques autres patterns, entre autre les patterns façade (ou décorateur) et invocationhandler. Ces deux patterns sont assez proche en fonctionnement, la différence étant dans la nature de la dépendance que l'on crée entre le bean décoré et l'objet décorateur ou objet intercepteur.Regardons cela par un exemple:
Considérons un bean MyObject contenant une donnée membre "nom" de type String caracterisant l'identité de l'objet. Ce nom est recuperable via une méthode getName().
Nous souhaiterions alors une fonctionnalité nous permettant de façon transparente renvoyer le hash du nom a la place. (Par soucis de simplicité pour l'exemple on utilisera les annotations @Named et @ApplicationContext).
On va dans un premier temps proposer une solution avec un décorateur. Pour cela, il nous faut construire une classe ayant le même profil de classe que le bean décoré. On va définir une API commune sous la forme d'une interface.
Enfin définissons le décorateur: Celui se défini sous la forme d'une classe abstraite implémentant la même interface que le bean en utilisant les annotations @Delegate et @Decorator (et @Priority qui permet de donner un ordre de prise en charge des beans si plusieurs décorateurs sont a l'oeuvre sinon cela est équivalent a définir le décorateur dans le beans.xml).
Ainsi le bean prendra la forme suivante:
1 2 3 4 5 6 7 8 9 10 | @Named("myObject") @ApplicationScoped public class MyObject implements INamed { private String name="defaultName"; public String getName() { return name; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Priority(value = 0) @Decorator public abstract class MyObjectDecorator implements INamed{ @Inject @Delegate private INamed myObject; @Override public String getName() { return String.valueOf(myObject.getName().hashCode()); } } |
La JSP affichant et utilisant la méthode getName nous renverra alors:
1 | myObject Name: -437142420 |
L'InvocationHandler [8]
Apres avoir traité la question du décorateur regardons la solution alternative qu'est l'utilisation de l'intercepteur (basé sur le pattern invocation handler que nous avons traité dans [8]).Ce pattern est un pattern permettant de positionner un objet entre le client faisant l'appel d'une méthode et la méthode elle même. En comparaison avec le décorateur, on pourrait se dire, "ok bon ca fait la meme chose" mais l'invocation handler a plutôt pour idée de modifier le comportement de la méthode d'une classe pour toutes ses instances. Un décorateur n'a pas cet objectif : il ne va pas se charger de toutes les instances de la classe, il peut éventuellement ne décorer que des objets spécifiques selon le besoin.
On pourrait donc en quelques sortes considérer que le décorateur a pour rôle de modifier le comportement en y ajoutant des éléments fonctionnel selon le cas alors que l'invocation handler aura un impact plus global et avec une orientation plus technique.
Voyons maintenant comment utiliser l'invocation Handler avec CDI.
Pour l'exemple imaginons que nous souhaitons compter le nombre de caractère du nom de l’objet que nous utilisions précédemment et l'affecter a une donnée membre dédiée.
Pour cela il nous faut tout d'abord construire une annotation maison permettant de tisser un lien entre le bean et notre futur intercepteur. Cette annotation devra alors porter l'annotation @InterceptorBinding. Appelons la CompteurAnnot:
1 2 3 4 | @InterceptorBinding @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface CompteurAnnot {} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Named("myObject") @ApplicationScoped public class MyObject implements INamed { private String name="defaultName"; private String nameSize; @CompteurAnnot public String getName() { return name; } public String getNameSize() { return nameSize; } public void setNameSize(String nameSize) { this.nameSize = nameSize; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Priority(value = 0) @CompteurAnnot @Interceptor public class Compteur { @Inject private MyObject myObject; @AroundInvoke public Object invoq(InvocationContext context) throws Exception { String result = (String) context.proceed(); System.out.println("YES: on a invoqué "+result); myObject.setNameSize(String.valueOf((result.length()))); return result; } } |
1 | myObject Name: -437142420, 10 |
Conclusion
Nous voila au bout de cet article sur CDI. Nous n'avons pas absolument tout traité mais globalement on en a fait un tour d'horizon plutôt large techniquement nous permettant de suffisamment comprendre l’intérêt de profiter des fonctionnalités de CDI et surtout de voir son role pivot dans les applications à base JEE. Ainsi a l'image de Spring, CDI est maintenant un incontournable.Références
[1] https://un-est-tout-et-tout-est-un.blogspot.com/2018/05/iod-linjection-de-dependances.html[2] https://docs.oracle.com/javaee/6/tutorial/doc/giwhl.html
[3] http://openwebbeans.apache.org/
[4] http://weld.cdi-spec.org/
[5] https://javaee.github.io/glassfish/
[6] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-decorateur.html
[7] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-observateur.html
[8] https://un-est-tout-et-tout-est-un.blogspot.com/2018/04/invocationhandler.html
[9] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-factory.html
[10] http://www.cdi-spec.org/news/2017/05/15/CDI_2_is_released/
[11] https://readlearncode.com/java-ee/introduction-to-cdi-producer-methods/
[12] https://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html
[13] https://rmannibucau.developpez.com/tutoriels/cdi/introduction-cdi
[14] http://un-est-tout-et-tout-est-un.blogspot.com/2018/08/jee-cdi-partie-1-linjection.html
Libellés :
annotations,
architecture,
CDI,
Conception,
decorateur,
EJB,
facade,
Factory,
glassfish,
invocation handler,
Iod,
JEE,
JSP,
observateur,
observer,
pattern,
Spring,
Weld
mercredi 15 août 2018
JEE : CDI partie 1 : l'injection
Apres un mois de juillet avec de l'IA, nous revoilà avec du Java, en particulier JEE. Cet article intervient dans la ligné de l'article [1] ou nous avions présenté les principes de l'injection de dépendance mais étions passé au dessus des implémentations possibles. Nous traiterons donc en toute logique d'une des implémentations majeur existante : CDI (l'autre étant Spring mais nous le traiterons dans un article dédié).
en utilisant la version 3 de Weld [4] et en le declarant dans le pom du projet:
Pourtant cette utilisation en standalone n'est pas forcement préconisé et est seulement à utiliser que lorsque l'on souhaite construire son propre conteneur d'application Java (nous reviendrons sur cette possibilité quand nous aborderons OSGI, bientot)
Ainsi l'application que l'on produira (sous la forme d'un War ou d'un EAR si l'on utilise des EJB) s'appuiera sur les implémentations java fournies par le container d'applications.
Pour traiter de CDI, n'importe quel container supportant une version 1.2 de CDI fera l'affaire (idéalement appuyez vous sur Glassfish [5] qui reste l’implémentation JEE de référence)
On ne peut pas plus simple pour un framework! ou du moins pour son utilisation en première approche. Cependant ce n'est pas tout, car CDI fourni de nombreuses autres fonctionnalités techniques pour la conception logicielle en s'appuyant sur différents Design Pattern tel que:
Nous nous limiterons aux patterns, les annotations cités ci-dessus étant assez immédiates a utiliser.
L'interet de l'approche est double, d'une part elle permet d'associer la création d'un bean a injecter avec un comportement et d'autre part de permettre a des objets qui ne sont pas des beans [12] a être quand même injectable :
Alors est ce que ça marche? Et bien non!! Arf pourquoi ?? C’était si simple, ça aurait du! Et bien simplement que le moteur CDI va avoir un conflit d'injection, il se base sur les signature de type pour trouver quelque chose a injecter et la, il en trouve deux : la classe CDIModel elle même et la méthode createCDIModel qui retourne un CDIModel.
Pour s'en sortir, il nous faut un moyen de discrimination. Pour cela, on va utiliser l'annotation @Qualifier qui va nous permettre de construire des annotations utilisables a la fois sur les éléments sources et sur les éléments cibles, la ou l'on souhaite réaliser l'injection.
On va donc créer une annotation maison pour discriminer les beans construis avec la factory.
On l'ajoute sur la signature de la méthode et sur l’élément ou l'injecter et la oh miracle ça fonctionne (ici par exemple on injecte avec les deux approches dans une jsp)
Sans oublier d'ajouter l'annotation @Default sur notre classe CDIModel de façon a ce que par défaut le moteur d'injection n'utilise pas la factory. Il ne le fera que si on lui dit explicitement.
Enfin, on utilise les deux modes d'injection:
donnant
Ainsi par exemple, on va pourvoir ajouter deux @Named, l'un sur la classe:
et l'autre sur la factory
les deux permettant de ne pas avoir a écrire le code suivant dans notre servlet ni même de spécifier qu'il faut les injecter:
Ainsi selon le scope choisi, un bean ne vivra pas le même temps... cela signifie aussi que celui ci sera recréé autant de fois que nécessaire, selon les demandes. Donc resumons:
Pour tester ces scopes nous allons aller un peu plus loin dans la gestion du cycle de vie du bean. En effet, on a bien compris qu'un bean a une durée de vie plus ou moins longue mais on a pas parler de ce qui se passe lors de sa naissance ou de sa mort. Bien sur nous avons évoqué l'utilisation de l'annotation @Produces permettant de réaliser une pseudo initialisation du bean mais en fait l'utilisation de @Produces a surtout pour vocation d’initialiser un contexte de construction du bean et non le bean lui-même.
Ainsi donc, pour ce qui concerne la naissance et la mort d'un bean, il existe deux annotations spécifiques @PostConstruct et @PreDestroy permettant de preciser deux méthodes à exécuter à l'instar d'un constructeur et d'un destructeur.
Pouvoir exécuter un destructeur, on comprend vite que ça permet d’éventuellement désallouer des ressources. Par contre, on peut discuter de la pertinence d'un pseudo constructeur par rapport au vrai constructeur de la classe. En effet celui ci a pour objet justement d'initialiser l'objet! Alors pourquoi un @PostConstrut? Simplement pour permettre au bean d'exister complètement avant de réaliser une initialisation quelconque ceci permettant donc la prise en compte de la présence d’éventuelles autres injections de bean dans le bean que nous sommes en train de construire! Sans cela, point de salut!
Donc voila un petit exemple pour illustrer tout cela et voir comment fonctionne nos @ResquestScope et @ApplicationScope (on ne traitera pas @SessionScope, celui ci étant plus long a mettre en oeuvre et n'amenant qu'assez peu d'infos supplémentaires sur le fonctionnement de CDI)
Dans un premier temps, on va tout d'abord créer deux nouveaux bean pour lesquels nous définirons les méthodes @PostConstruct et @PreDestroy.
A ces deux classes on va ajouter a l'un @ApplicationScope et a la seconde @RequestScope comme suit:
et
On réalise finalement les actions suivantes
Effectivement la on se rend compte que le bean ayant le scope de applicatif n'est créer qu'au premier chargement de la page tandis que le scope associé a la requête et créer et détruit quasiment a la volé lors de chaque rafraîchissement.
[2] https://docs.oracle.com/javaee/6/tutorial/doc/giwhl.html
[3] http://openwebbeans.apache.org/
[4] http://weld.cdi-spec.org/
[5] https://javaee.github.io/glassfish/
[6] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-decorateur.html
[7] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-observateur.html
[8] https://un-est-tout-et-tout-est-un.blogspot.com/2018/04/invocationhandler.html
[9] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-factory.html
[10] http://www.cdi-spec.org/news/2017/05/15/CDI_2_is_released/
[11] https://readlearncode.com/java-ee/introduction-to-cdi-producer-methods/
[12] https://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html
[13] https://rmannibucau.developpez.com/tutoriels/cdi/introduction-cdi
Introduction
CDI, ou Context and Dependency Injection, est donc un framework d'injection de dépendances évoluant dans l'environnement JEE. Comme d'habitude avec les technologie JEE, CDI a été élaboré après que le framework concurrent ici Spring qui en a posé les bases. Ainsi il s'appuie sur la formalisation apportée par les JSR 299 et 346 [2], [3] a partir des années 2009 et fut décliné selon differentes versions telles que:- v1 en 2009 avec JEE 6
- v1.1 en 2013
- v1.2 en 2014
- v2 en 2017 dans JEE 8 [10]
Standalone mode
La plupart des concepts de base de CDI ont été élaboré dans ses premières versions et en fait sa dernière évolution n'apporte surtout que la possibilité de faire évoluer le framework en dehors d'un conteneur d'application en permettant d'instancier le framework directement dans un classique main comme suit:1 2 3 | public static void main(String[] args) { CDI<Object> container = new Weld().initialize(); } |
en utilisant la version 3 de Weld [4] et en le declarant dans le pom du projet:
1 2 3 4 5 | <dependency> <groupId>org.jboss.weld.se</groupId> <artifactId>weld-se-core</artifactId> <version>3.0.5.Final</version> </dependency> |
Pourtant cette utilisation en standalone n'est pas forcement préconisé et est seulement à utiliser que lorsque l'on souhaite construire son propre conteneur d'application Java (nous reviendrons sur cette possibilité quand nous aborderons OSGI, bientot)
Standard mode
En l’état, le mieux que l'on puisse faire, c'est se référer à la spécification JEE en utilisant la dépendance suivante:1 2 3 4 5 6 | <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency> |
Ainsi l'application que l'on produira (sous la forme d'un War ou d'un EAR si l'on utilise des EJB) s'appuiera sur les implémentations java fournies par le container d'applications.
Pour traiter de CDI, n'importe quel container supportant une version 1.2 de CDI fera l'affaire (idéalement appuyez vous sur Glassfish [5] qui reste l’implémentation JEE de référence)
L'injection
Retournons a CDI et ce que le framework permet de faire. CDI comme son nom l’évoque traite de l'injection de dépendances. Globalement ceci passe par l'utilisation de beans et de l'annotation @Inject.1 2 | @Inject private ServletConf servletConf; |
On ne peut pas plus simple pour un framework! ou du moins pour son utilisation en première approche. Cependant ce n'est pas tout, car CDI fourni de nombreuses autres fonctionnalités techniques pour la conception logicielle en s'appuyant sur différents Design Pattern tel que:
- Le Decorateur [6]
- L'Observateur [7]
- L'InvocationHandler [8]
- La Factory [9]
Nous nous limiterons aux patterns, les annotations cités ci-dessus étant assez immédiates a utiliser.
La factory [11]
Apres cet exemple simple, considérons par exemple que nous ne souhaitons plus simplement injecter un bean quelconque mais injecter un bean de notre propre composition. Comment faire? Et bien la va intervenir une annotation @Produces que l'on va associer a une ou plusieurs méthodes qui seront désignées comme les constructeurs des beans à injecter.L'interet de l'approche est double, d'une part elle permet d'associer la création d'un bean a injecter avec un comportement et d'autre part de permettre a des objets qui ne sont pas des beans [12] a être quand même injectable :
1 2 3 4 5 | @Produces public CDIModel createCDIModel() { return new CDIModel(); } |
Alors est ce que ça marche? Et bien non!! Arf pourquoi ?? C’était si simple, ça aurait du! Et bien simplement que le moteur CDI va avoir un conflit d'injection, il se base sur les signature de type pour trouver quelque chose a injecter et la, il en trouve deux : la classe CDIModel elle même et la méthode createCDIModel qui retourne un CDIModel.
Pour s'en sortir, il nous faut un moyen de discrimination. Pour cela, on va utiliser l'annotation @Qualifier qui va nous permettre de construire des annotations utilisables a la fois sur les éléments sources et sur les éléments cibles, la ou l'on souhaite réaliser l'injection.
On va donc créer une annotation maison pour discriminer les beans construis avec la factory.
1 2 3 4 5 6 | @Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) public @interface FromFactory { } |
On l'ajoute sur la signature de la méthode et sur l’élément ou l'injecter et la oh miracle ça fonctionne (ici par exemple on injecte avec les deux approches dans une jsp)
1 2 | @Produces @FromFactory public CDIModel createCDIModel() |
Sans oublier d'ajouter l'annotation @Default sur notre classe CDIModel de façon a ce que par défaut le moteur d'injection n'utilise pas la factory. Il ne le fera que si on lui dit explicitement.
Enfin, on utilise les deux modes d'injection:
1 2 3 4 5 6 | @Inject private CDIModel model; @Inject @FromFactory private CDIModel model2; |
donnant
1 2 3 | Model CDI: les données du model Model 2 CDI: les données du model From Factory |
Named
Pour faire plus simple, et ne pas forcement avoir a construire des annotations, CDI fourni une annotation dédiée à la discrimination des beans: @Named qui permet de donner un nom a l’élément producteur du bean et de faciliter le binding sur l’élément où l'injecter. L’intérêt de cette annotation est qu elle permet de construire des beans spécifiques utilisables directement dans les JSP (même si l'on a pas encore parler de cette techno, cela va permettre d'utiliser des beans dans des éléments de pseudo code déclarés dans le code html, nous y reviendrons)Ainsi par exemple, on va pourvoir ajouter deux @Named, l'un sur la classe:
1 2 3 4 | @Named("model") public class CDIModel { ... } |
et l'autre sur la factory
1 2 3 4 5 6 7 | @Produces @Named("model2") public CDIModel createCDIModel() { CDIModel m=new CDIModel(); m.setDataModel(m.getDataModel()+" From Factory"); return m; } |
les deux permettant de ne pas avoir a écrire le code suivant dans notre servlet ni même de spécifier qu'il faut les injecter:
1 2 3 4 5 6 7 8 | if(model!=null) request.setAttribute("model", model); if(model2!=null) { model2.setDataModel(model2.getDataModel()+" From Factory"); request.setAttribute("model2", model2); }) |
Les scopes
Puisque nous parlons de JSP, profitons en pour nous intéresser au cycle de vie des beans. C'est quoi le lien? Bien entendu il s'agit de la durée de vie du bean selon le type de requête qu'un utilisateur est amener a faire sur les differentes pages de notre application (on parle d'application JEE quoi...). Entre autre on doit se demander si notre bean doit vivre juste le temps d'une requete (on utilisera alors l'annotation @RequestScoped), le temps de plusieurs requêtes (@SessionScoped) ou carrément le temps de toute la durée de vie de notre application (@ApplicationScoped).Ainsi selon le scope choisi, un bean ne vivra pas le même temps... cela signifie aussi que celui ci sera recréé autant de fois que nécessaire, selon les demandes. Donc resumons:
- Avec un scope application, le bean ne sera donc créer qu'une fois et ne sera a priori pas détruit (sauf lors de l’arrêt de l'applicatif).
- Le scope requete provoque a l'inverse la création du bean a chaque requête http et détruite a l'issu de la réponse.
- Le scope session est un intermédiaire où le bean est créé a la connexion de l'utilisateur et a l'aide d'information de session permet de maintenir le bean tant que cet utilisateur utilise l'application.
Pour tester ces scopes nous allons aller un peu plus loin dans la gestion du cycle de vie du bean. En effet, on a bien compris qu'un bean a une durée de vie plus ou moins longue mais on a pas parler de ce qui se passe lors de sa naissance ou de sa mort. Bien sur nous avons évoqué l'utilisation de l'annotation @Produces permettant de réaliser une pseudo initialisation du bean mais en fait l'utilisation de @Produces a surtout pour vocation d’initialiser un contexte de construction du bean et non le bean lui-même.
Ainsi donc, pour ce qui concerne la naissance et la mort d'un bean, il existe deux annotations spécifiques @PostConstruct et @PreDestroy permettant de preciser deux méthodes à exécuter à l'instar d'un constructeur et d'un destructeur.
Pouvoir exécuter un destructeur, on comprend vite que ça permet d’éventuellement désallouer des ressources. Par contre, on peut discuter de la pertinence d'un pseudo constructeur par rapport au vrai constructeur de la classe. En effet celui ci a pour objet justement d'initialiser l'objet! Alors pourquoi un @PostConstrut? Simplement pour permettre au bean d'exister complètement avant de réaliser une initialisation quelconque ceci permettant donc la prise en compte de la présence d’éventuelles autres injections de bean dans le bean que nous sommes en train de construire! Sans cela, point de salut!
Donc voila un petit exemple pour illustrer tout cela et voir comment fonctionne nos @ResquestScope et @ApplicationScope (on ne traitera pas @SessionScope, celui ci étant plus long a mettre en oeuvre et n'amenant qu'assez peu d'infos supplémentaires sur le fonctionnement de CDI)
Dans un premier temps, on va tout d'abord créer deux nouveaux bean pour lesquels nous définirons les méthodes @PostConstruct et @PreDestroy.
A ces deux classes on va ajouter a l'un @ApplicationScope et a la seconde @RequestScope comme suit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @ApplicationScoped public class CDIModel { private String dataModel="les données du model"; @PostConstruct public void build() { System.out.println("Construction de CDIModel"); } @PreDestroy public void destroy() { System.out.println("Destruction de CDIModel"); } ... } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Named @RequestScoped public class CDIModelShortScope { @PostConstruct public void build() { System.out.println("Construction de CDIModelShortScope"); } @PreDestroy public void destroy() { System.out.println("Destruction de CDIModelShortScope"); } } |
On réalise finalement les actions suivantes
- On build et on construit notre EAR.
- On charge celui ci dans notre container d'application preferé
- On charge la page et on fait plusieurs raffraichissement.
- On fini par decharger l'EAR
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | [2018-08-13T23:42:14.969+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=31 _ThreadName=Thread-8] [timeMillis: 1534196534969] [levelValue: 800] [[ Construction de CDIModel]] [2018-08-13T23:42:14.986+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=31 _ThreadName=Thread-8] [timeMillis: 1534196534986] [levelValue: 800] [[ Construction de CDIModelShortScope]] [2018-08-13T23:42:15.002+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=31 _ThreadName=Thread-8] [timeMillis: 1534196535002] [levelValue: 800] [[ Destruction de CDIModelShortScope]] [2018-08-13T23:43:31.005+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=32 _ThreadName=Thread-8] [timeMillis: 1534196611005] [levelValue: 800] [[ org.tc.jee.essai.prod.web.cdi.bean.ServletConf@3dc05bc]] [2018-08-13T23:43:31.013+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=32 _ThreadName=Thread-8] [timeMillis: 1534196611013] [levelValue: 800] [[ Construction de CDIModelShortScope]] [2018-08-13T23:43:31.014+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=32 _ThreadName=Thread-8] [timeMillis: 1534196611014] [levelValue: 800] [[ Destruction de CDIModelShortScope]] [2018-08-13T23:43:32.079+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=34 _ThreadName=Thread-8] [timeMillis: 1534196612079] [levelValue: 800] [[ org.tc.jee.essai.prod.web.cdi.bean.ServletConf@3dc05bc]] [2018-08-13T23:43:32.081+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=34 _ThreadName=Thread-8] [timeMillis: 1534196612081] [levelValue: 800] [[ Construction de CDIModelShortScope]] [2018-08-13T23:43:32.081+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=34 _ThreadName=Thread-8] [timeMillis: 1534196612081] [levelValue: 800] [[ Destruction de CDIModelShortScope]] [2018-08-13T23:43:33.420+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=30 _ThreadName=Thread-8] [timeMillis: 1534196613420] [levelValue: 800] [[ org.tc.jee.essai.prod.web.cdi.bean.ServletConf@3dc05bc]] [2018-08-13T23:43:33.422+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=30 _ThreadName=Thread-8] [timeMillis: 1534196613422] [levelValue: 800] [[ Construction de CDIModelShortScope]] [2018-08-13T23:43:33.423+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=30 _ThreadName=Thread-8] [timeMillis: 1534196613423] [levelValue: 800] [[ Destruction de CDIModelShortScope]] [2018-08-13T23:43:42.998+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=48 _ThreadName=Thread-8] [timeMillis: 1534196622998] [levelValue: 800] [[ Destruction de CDIModel]] |
Conclusion
Bien sur nous n'avons pas tout vu il reste les patterns avec CDI! mais cet article est deja assez long et vous propose de revenir d'ici quelques jours pour terminer ce sujet.Références
[1] https://un-est-tout-et-tout-est-un.blogspot.com/2018/05/iod-linjection-de-dependances.html[2] https://docs.oracle.com/javaee/6/tutorial/doc/giwhl.html
[3] http://openwebbeans.apache.org/
[4] http://weld.cdi-spec.org/
[5] https://javaee.github.io/glassfish/
[6] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-decorateur.html
[7] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-observateur.html
[8] https://un-est-tout-et-tout-est-un.blogspot.com/2018/04/invocationhandler.html
[9] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-factory.html
[10] http://www.cdi-spec.org/news/2017/05/15/CDI_2_is_released/
[11] https://readlearncode.com/java-ee/introduction-to-cdi-producer-methods/
[12] https://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html
[13] https://rmannibucau.developpez.com/tutoriels/cdi/introduction-cdi
Libellés :
annotations,
architecture,
CDI,
Conception,
EJB,
Factory,
glassfish,
http,
Iod,
JEE,
JSP,
pattern,
Request,
Scope,
Session,
Spring,
Weld
mardi 8 mai 2018
Iod, l'injection de dépendances
Nous avons vu dans l’article précédent, le principe de l’inversion de contrôle. Ce dernier se base sur les principe de SOLID et est indispensable aux frameworks modernes tels que Spring qui l’utilise, on peut presque le dire, à outrance dans leur système d’injection de dépendances.
Dans cet article, justement, je vous propose de regarder comment fonctionne un mécanisme d’injection de dépendance et vous invite donc si vous ne l’avez pas vu à regarder l’article [1] afin d’en acquérir les pré-requis.
J'aurais peut être dû faire un article commun pour traité les sujets de IOC et de IOD car les deux sont souvent intimement lié et même traité ensemble. Cependant, d’une part, le temps dont je dispose ces derniers temps c’est gravement amoindri donc c'était plus simple d’en faire deux et d’autre part, je trouve que si l’IOD dépendant de l’IOC, l’inverse n’est pas vrai et heureusement (ca serait presque paradoxale…. ^^)
Donc du coup voilà, qu’est que IOD où injection de dépendance [2]. Le principe est simple, il s’agit de donner la responsabilité de la construction de la dépendance entre deux objets a un troisième. A ce stade on peut se dire que ce que nous avions vu sur l’inversion de contrôle est suffisant, on crée une factory pour produire des objets et op on l'appelle lorsqu'il faut construire des objets qui en ont besoin. Si l’idée de base est bien celle-ci, il nous manque un partie importante, l’orchestration.
En effet, dans le principe, l’injection de dépendances repose globalement sur les même mécanismes que l’inversion de contrôle car, l’inversion de contrôle permet de déplacer la problématique de la gestion du cycle de vie d’un objet dont un objet en avait la responsabilité, auprès d’un autre. Du coup forcément, pour permettre la mise en relation, l’objet initial doit toujours pouvoir obtenir l’objet dont il a besoin.
Ainsi dans l’article précédent [1] on avait forcément en plus de l’IOC une forme implicite d’injection de dépendance.
Mais bon on s’accordera que ça mériterait, un petit peu plus d’outillage.
Ainsi, ce qui manque c’est d’un moyen permettant de faire cette mise en relation de la voiture avec le moteur implicitement. Le premier réflexe, qui n’est pas forcément une mauvaise idée, est de faire appelle à une autre factory. La combinaison des factory est souvent dans un périmètre limité une bonne solution.
Bien ça marche, cool mais en fait on se rend vite compte qu’on a déplacer le problème! ba oui car la on passe en paramètre la factory des moteurs à celle des voitures. On a donc aussi un problème de gestion de dépendance! Mais ne peut on pas faire une factory pour gérer cela? ba si allons jusqu'au bout de l’idée
Bien on voit que ça marche, mais ça fait beaucoup de factory! Et bien oui et c’est pour cela que l’on utilise des framework comme Spring [3] ou CDI [4].
Voilà, j'espère qu’avec ces deux articles et cet exemple très simple, on a un peu démystifier l’IOD et IOC et permit de mieux aborder les mécanismes sous jacent a des framework comme SPring et CDI qui se basent eux aussi (aux registres et annotations près) sur ce bon vieux pattern factory. A très vite donc sur Spring et CDI!
[2] http://igm.univ-mlv.fr/~dr/XPOSE2010/guicespring/di_presentation.html
[3] https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
[4] https://rmannibucau.developpez.com/tutoriels/cdi/introduction-cdi/
Dans cet article, justement, je vous propose de regarder comment fonctionne un mécanisme d’injection de dépendance et vous invite donc si vous ne l’avez pas vu à regarder l’article [1] afin d’en acquérir les pré-requis.
J'aurais peut être dû faire un article commun pour traité les sujets de IOC et de IOD car les deux sont souvent intimement lié et même traité ensemble. Cependant, d’une part, le temps dont je dispose ces derniers temps c’est gravement amoindri donc c'était plus simple d’en faire deux et d’autre part, je trouve que si l’IOD dépendant de l’IOC, l’inverse n’est pas vrai et heureusement (ca serait presque paradoxale…. ^^)
Donc du coup voilà, qu’est que IOD où injection de dépendance [2]. Le principe est simple, il s’agit de donner la responsabilité de la construction de la dépendance entre deux objets a un troisième. A ce stade on peut se dire que ce que nous avions vu sur l’inversion de contrôle est suffisant, on crée une factory pour produire des objets et op on l'appelle lorsqu'il faut construire des objets qui en ont besoin. Si l’idée de base est bien celle-ci, il nous manque un partie importante, l’orchestration.
En effet, dans le principe, l’injection de dépendances repose globalement sur les même mécanismes que l’inversion de contrôle car, l’inversion de contrôle permet de déplacer la problématique de la gestion du cycle de vie d’un objet dont un objet en avait la responsabilité, auprès d’un autre. Du coup forcément, pour permettre la mise en relation, l’objet initial doit toujours pouvoir obtenir l’objet dont il a besoin.
Ainsi dans l’article précédent [1] on avait forcément en plus de l’IOC une forme implicite d’injection de dépendance.
def mFactory=new MoteurFactory() new Voiture(mFactory.buildMoteurX()).drive() new Voiture(mFactory.buildMoteurX()).drive()
Mais bon on s’accordera que ça mériterait, un petit peu plus d’outillage.
Ainsi, ce qui manque c’est d’un moyen permettant de faire cette mise en relation de la voiture avec le moteur implicitement. Le premier réflexe, qui n’est pas forcément une mauvaise idée, est de faire appelle à une autre factory. La combinaison des factory est souvent dans un périmètre limité une bonne solution.
class MoteurFactory { def buildMoteurX() { return new MoteurX() } def buildMoteurY() { return new MoteurY() } } class VoitureFactory { def buildWithMoteurX(MoteurFactory f) { return new Voiture(f.buildMoteurX()) } def buildWithMoteurY(MoteurFactory f) { return new Voiture(f.buildMoteurY()) } } def mFactory=new MoteurFactory() def vFactory=new VoitureFactory() def v1=vFactory.buildWithMoteurX(mFactory) def v2=vFactory.buildWithMoteurY(mFactory) v1.drive() v2.drive()
Bien ça marche, cool mais en fait on se rend vite compte qu’on a déplacer le problème! ba oui car la on passe en paramètre la factory des moteurs à celle des voitures. On a donc aussi un problème de gestion de dépendance! Mais ne peut on pas faire une factory pour gérer cela? ba si allons jusqu'au bout de l’idée
class MoteurFactory { def buildMoteurX() { return new MoteurX() } def buildMoteurY() { return new MoteurY() } } class VoitureFactory { MoteurFactory facto def VoitureFactory(MoteurFactory facto) { this.facto=facto } ca def buildWithMoteurX() { return new Voiture(this.facto.buildMoteurX()) } def buildWithMoteurY() { return new Voiture(this.facto.buildMoteurY()) } } class MetaFactory { def buildFactoryForCar() { return new VoitureFactory(new MoteurFactory()) } } def mf=new MetaFactory() mf.buildFactoryForCar().buildWithMoteurX().drive() mf.buildFactoryForCar().buildWithMoteurY().drive()
Bien on voit que ça marche, mais ça fait beaucoup de factory! Et bien oui et c’est pour cela que l’on utilise des framework comme Spring [3] ou CDI [4].
Voilà, j'espère qu’avec ces deux articles et cet exemple très simple, on a un peu démystifier l’IOD et IOC et permit de mieux aborder les mécanismes sous jacent a des framework comme SPring et CDI qui se basent eux aussi (aux registres et annotations près) sur ce bon vieux pattern factory. A très vite donc sur Spring et CDI!
Références
[1] https://un-est-tout-et-tout-est-un.blogspot.fr/2018/05/ioc-inversion-de-controle.html[2] http://igm.univ-mlv.fr/~dr/XPOSE2010/guicespring/di_presentation.html
[3] https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
[4] https://rmannibucau.developpez.com/tutoriels/cdi/introduction-cdi/
Libellés :
CDI,
composants,
Conception,
Factory,
Groovy,
Ingénierie,
Inversion de controle,
Ioc,
Iod,
Spring
Inscription à :
Articles (Atom)