0% found this document useful (0 votes)
85 views

Spring 3

spring

Uploaded by

hlemorvan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
0% found this document useful (0 votes)
85 views

Spring 3

spring

Uploaded by

hlemorvan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
You are on page 1/ 13
D'autres formations sont sur https://gayerie.dev ft » Le contexte d’application © Précédent Suivant © Le contexte d’application Nous avons vu au chapitre précédent que le Spring framework fournit un conteneur loC. Ce conteneur est formé a partir de un ou plusieurs contextes d'application. Un contexte d'application contient la définition des objets que le conteneur doit créer ainsi que leurs interdépendances. LAPI du Spring Framework définit linterface ApplicationContext. II existe plusieurs implémentations de cette interface et donc plusieurs fagons de définir un contexte d’application. A lorigine, un contexte d'application devait étre décrit a l'aide d'un fichier XML. Avec I'évolution des technologies, le Spring Framework s‘est enrichi de nouvelles classes implémentant l'interface ApplicationContext afin d’offrir des méthodes alternatives a la création d'un contexte d’application. La méthode la plus souvent recommandée consiste a ajouter des annotations sur les classes de notre application pour indiquer au Spring Framework comment le conteneur loC doit étre initialisé. Pour cela, nous devons utiliser une instance de la classe AnnotationConfigApplicationContext. Mise en place du projet Pour ce chapitre, vous pouvez générer votre projet a partir du site https://start.spring.io/ sans ajouter de dépendance particuliére. Vous pouvez également télécharger le modéle de projet Maven & firstapp.zip. Aprés avoir décompresser I'archive sur votre disque, vous pouvez importer votre projet sous Eclipse en choisissant dans le menu ( File > Import... . Puis choisissez comme type d'import : Select EOL NE ) Select an importsource: type filter text a) >@pvate + Maven a check out Maven Projects from SCM Install or deploy an artifact to a Maven repository ‘Wil Materialize Maven Projects from SCM. > Oomph > © Plug-in Development > Remote Systems ® S21) Fenétre d'import de projet dans Eclipse Une nouvelle fenétre s’ouvre pour vous demander d'indiquer le répertoire dans lequel se trouve le fichier pon.xm1 de votre projet. Cliquez sur le bouton ( Browse pour sélectionner ce répertoire. Eclipse doit remplirle reste de la fenétre avec les informations sur votre projet. Ine vous reste plus qu’a cliquer sur le bouton (Finish). Import Maven Projects > 8 Maven Projects Select Maven projects ‘Root Directory: | shome/david/workspace/spring-booufirstapp | ¥ | Browse... | Projects: Deselect Tree | ® | fade | Next> | Cancel Fenétre d'import du projet Maven dans Eclipse Une premiére application avec Spring Pour illustrer la création d'un contexte d'application, nous allons commencer par un exemple trés simple. Imaginons que nous souhaitions afficher l'heure courante sur la sortie standard. Bien évidemment, une application Java aussi simple n’a pas besoin d'un quelconque framework, Mais imaginons que nous souhaitions tout de méme ut rr le Spring Framework. II nous faut créer un contexte d'application et demander a Spring d'ajouter une objet de type LocalTime dans ce contexte. Puis nous pourrons extraire cet objet du conteneur pour Vafficher a Fécran. Voici a quoi ressemble le programme correspondant : package dev.gayerie.firstapp; import java. tine.LocalTine; import org. springfranework. context .annetation.Beans 1 2 3 4 5S import org.springfranework. context annotation .AnnotationContigapplicationContexts 6 7 8 public class TineApplication { 9 10 @Bean 11 public LocalTine maintenant () { 2 return LocalTime.now(); Bo} 4 1S public static void main(stringl] args) < 16 try(AnnotationConFigApplicationContext appCtx v = new AnnotationConfigApplicationContext(TimeApplication.class)) { 18 LocalTime time = appCtx.getBean("naintenant", LocalTime.class); 19 System.out.printIn(tine); 20 x a} 2) On crée une méthode sain la ligne 15 comme point d’entrée de notre application. Aux lignes 16-17, on crée une instance de AnnotationConfigApplicationContext qui est le contexte Spring de notre application. Notez que la création attend en paramétre la classe utiliser pour définir le contexte. Dans notre exemple, il s'agit directement de la classe TineApplication . Notez également que l'on crée Vinstance du contexte d'application dans un structure de type try-with-resources. Cela nous garantit que la méthode ciose() du contexte d'application sera appelée a la fin du bloc et donc avant la fin de l'application. Nous reviendrons sur le fait qu'un contexte d'application doit étre correctement fermé afin de lui laisser la possibilité de gérer correctement le cycle de vie des objets qu'il contient. La création du contexte d'application implique que Spring va créer une instance de la classe Tinespplication passée en paramétre du constructeur. Il va ensuite chercher des annotations particuligres sur cet objet. Par exemple, il va chercher toutes les méthodes déclarées avec lannotation @Bean. Ces méthodes sont traitées comme des méthodes de fabrique (factory methods). Pour Spring, elles servent a créer des objets qui doivent étre gérés par le contexte d’application. Le framework va donc appeler ces méthodes une a une et récupérer les objets qu'elles retournent en leur donnant un nom correspondant au nom de la méthode. @ Note La terme de bean renvoie aux JavaBeans qui désignent les composants élémentaires en Java. Bean est a la fois une métaphore et un trait d’humour. En effet, le langage Java tient son nom de la référence au café (en provenance de I’ile de Java) que les concepteurs du langage ont bu en grande quantité durant sa création. Pour faire du bon café, il faut du bon grain. Donc, les composants principaux des programmes Java doivent naturellement tre des grains (beans en anglais) de café. Si le standard JavaBeans imposait de respecter certaines convention de codage, le terme de bean s'est peu a peu généralisé pour devenir synonyme d’objet. C'est dans ce sens trés général qu'il est utilisé avec le Spring Framework : un bean est un objet. Dans ce chapitre et les suivants, nous utiliserons le terme bean pour désigner un objet Java qui est ajouté dans le contexte d'application et qui est done géré par le conteneur loC du Spring Framework. Dans notre exemple, la méthode naintenant() posséde l'annotation @Bean (lignes 10 4 13). Elle sera donc appelée par Spring et l'objet de type LocalTime quelle retourne sera placé dans le contexte d'application avec le nom maintenant . Ala ligne 18, nous utilisons la méthode getBean du contexte d’application pour récupérer un objet de type LocalTime et qui s‘appelle maintenant pour pouvoir lafficher. @ Note Sill n'existe qu'un seul objet dans le contexte d'application qui est compatible avec le type demandé, alors il n’est pas nécessaire de spécifier le nom de lobjet : LocalTine time = appCtx.getean(LocalTime.class); Systen.out.printIn(tine) Si vous ne souhaitez pas utiliser le nom de la méthode comme nom pour l'objet, vous pouvez déclarer le nom de l'objet avec lattribut nane de l'annotation @Bean @Bean(nane = “maintenant") public LocalTime getLocalTime() { return LocalTine.now()5 > Vous pouvez méme donner plusieurs noms a un objet en passant un tableau de valeurs Vattribut nase |: GBean(nane = {"naintenant™, “now", “ahora”, “jetzt"}) public LocalTine getLocalTime() { return LocalTine.now()5 y Notion de portée (scope) Les beans ajoutés dans un contexte d’application ont une portée (scope). Par défaut, Spring définit deux portées singleton Cette portée évoque le modéle de conception singleton. Cela signifie qu'une seule instance de ce bean existe dans le conteneur IoC. Autrement dit, si un programme appelle une méthode getsean pour récupérer ce bean, chaque appel retourne le méme objet. prototype Cette portée est I'inverse de la portée singleton. A chaque fois qu'un programme appelle une méthode getaean pour récupérer ce bean, chaque appel retourne une nouvelle instance du bean. Le type de la portée peut étre indiquée grace a annotation @Scope. La portée par défaut dans le Spring Framework est singleton. Si nous reprenons notre programme précédent, nous pourrions le faire évoluer pour afficher le temps toutes les secondes. 1 package dev. gayerie.firstapps 2 3. inport java.tine.LocalTine; 4 5 import org.springfranework. context .annotation.AnnotationConfigapplicationContext 6 import org.springfranework. context annotation. Beans 7 B public class TimeApplication { ° 19 @Bean 11 public LocalTine maintenant() { 2 return LocalTime.now(); Boo} a 15 public static void main(String[] args) throws Interruptedéxception { 16 ‘try(AnnotationConfigdoplicationContext appCtx v new AnnotatLonConFigAppLicationContext (Timeapplication.class)) ( 18 while (true) ( Fey Thread. sleep(1000); 20 LocalTime tine = appCtx.getBean(“naintenant", LocalTime.class); a Systen.out.printin(time) ; 2 y 23 y 2m} 23} Le programme précédent ne s'arréte jamais de lui-méme puisqu’il contient une boucle infinie. A chaque itération, on attend 1000 millisecondes pour ensuite demander le bean au contexte d'application pour l’afficher. Si vous lancez ce programme, vous constaterez qu'il affiche & Vinfini la méme heure correspondant au lancement du programme. En effet, la portée d’un bean par défaut est singleton . Cela signifie que l'objet est instancié a la création du contexte d'application et quill n'existera qu'une fois. Nous pouvons utiliser annotation @Scope pour modifier ce comportement et spécifier une portée prototype + 1 package dev. gayerie. firstapps 2 3 Anport java. tine.LocalTine; 4 5 import org.springfranework. context annotation. AnnotationConfigapplicationContext 6 import org. springfranework. context .annotation.8ean; 7 import org.springfranework.context .annotation.Scope; 8 9 public class TineApplication { 10 1 @Bean 312 @Scope("prototype") 13 public LocalTime maintenant() { 4 return LocalTime.now(); 3} 16 17 public static void main(Stringl] args) throws Interruptedxception { 18 ‘try(AnnotationConfighoplicationContext appctx 19 new AnnotationConFigappLicationContext (Timeapplication.class)) { 20 while (true) ( a Thread. sleep(1000); 2 LocalTime tine = appCtx.getBean(“naintenant", LocalTime.class); 23 systen.out.println(time) ; 24 y 25 y 2%} 7 Le programme se comporte bien comme attendu et le temps s’écoule lorsqu’on affiche l'heure sur la console. Prototype signifie que l'objet créé n'est pas l'objet définitif et qu'il doit étre recréé a chaque fois qu’il est demandé au contexte d’application. Dans notre exemple, Spring rappelle continuellement notre méthode maintenant() a chaque fois que le programme récupére une instance de l'objet en appelant getsean() « Il peut sembler étonnant que la portée par défaut soit singleton . En fait, nous verrons que le Spring Framework est quasi exclusivement utilisé pour construire lossature d’une application. Il s'agit alors de créer des objets qui n’ont besoin d’étre présents qu'une seule fois en mémoire. @ Prudence Il faut garder a esprit que, par défaut, la portée des objets créés par le Spring Framework est. singleton . Donc limplémentation de ces objets doit étre thread safe. En effet, si notre application s'exécute dans un environnement concurrent (comme une application Web), le méme objet est susceptible d’étre appelé simultanément par plusieurs flux de traitement (threads). @ Note Dans certains contextes d’exécution, il existe d'autres portées disponibles. Par exemple, pour une application Web, vous pouvez définir des beans avec une portée request pour indiquer que la durée de vie de ces objets ne doit pas aller au dela du traitement de la requéte courante. Dans le méme contexte, vous pouvez définir des beans avec une portée session pour indiquer qu'ils devront étre uniques par session utilisateur sur le serveur. Linjection de bean En plus de pouvoir enregistrer les beans que nous créons dans un contexte d’application, nous pouvons utiliser le Spring Framework pour nous aider 8 initialiser les dépendances entre les objets, Pour illustrer ce fonctionnement, nous allons prendre un exemple d'application un peu plus proche de ce que nous pourrions trouver dans la réalité tout en gardant volontairement un code simple. Imaginons que nous souhaitions créer une application pour réaliser des traitements sur des chaines de caractéres. Notre application sera constituée de deux catégories d’objets : d'une part un service qui doit réaliser le traitement demandé et d'autres part un fournisseur de données chargé de produire la chaine de caractéres utilisée par le service. Afin de garantir un découplage maximum, nous allons matérialiser les réles par des interfaces. Nous pourrions créer nos propres interfaces mais I'API standard Java nous fournit déja celles dont nous avons besoin. Le service de traitement implémentera interface Runnable et le fournisseur de données implémentera I'interface Supplier. Comme exemple de service de traitement, nous utiliserons une classe qui se contente diafficher la donnée produite par le fournisseur : Une implémentation de service de traitement package dev. gayerie. appstrings Import java.util. function. suppliers public class WriterService impLenents Runnable { private Supplier suppliers public WriterService(Supplier supplier) { ‘this.supplier = supplier; y foverride public void run() { system. out .print1n(supplier.get())s y t Comme exemple de fournisseur de données, nous utiliserons une classe qui produit une chaine de caractéres en dur : Une implémentation de fournisseur de données package dev. gayerie. appstrings import java.util. function. suppliers public class HardcodedSupplier implements Supplier { override public String get() { return "Hello world"; y x Pour réaliser notre application avec le Spring Framework, il faut ajouter une instance de ces. objets dans le contexte d'application. Mais pour créer une instance de writerservice , nous avons besoin d'un objet de type supplier . Nous pouvons demander au Spring Framework de nous en fournir un disponible dans le contexte d'application en l'ajoutant comme paramétre de la méthode de fabrique : application construite avec Spring 1 package dev. gayerie. appstring; 2 3 import java. util. function.Supplier; 4 5 import org.springfranework. context annotation .AnnotationConfighpplicationContext 6 import ong. springfranework. context annotation. Beans 7 8 public class TaskApplication { 9 10 @sean 11 public Supplier dataSupplier() { 2 return new Hardcodedsupplier(); Bo} 4 15 @eean 16 public Runnable task(Supplier datasupplier) { v7 return new WriterService(dataSupplier) ; wo} 9 20 public static void main(String[] args) throws Interruptedéxception { a ‘try (AnnotationConfigapplicationContext appCtx 2 ew AnnotationConfigapplicationContext (TaskApplication.class)) { 23 appCtx. getean(Runnable.class).run(); 24 x 2} 2%) La méthode ain de 'application crée un contexte d'application a partir de la classe courante et recherche le bean du contexte qui est compatible avec interface Runnable pour pouvoir lancer sa méthode run La partie la plus importante de notre exemple est I'implémentation de la méthode tas« . Elle est annotée avec @Bean pour indiquer a Spring qu'il s'agit d'une méthode qui fabrique un bean. Mais surtout, elle attend en paramétre un objet de type supplier . Pour réaliser cet appel, le Spring Framework va devoir trouver dans le contexte d'application un (et un seul) bean qui est compatible avec ce type. II va trouver celui fournit par la méthode catasupplier et le passer en paramétre. Méme si notre exemple reste trivial, nous pouvons utiliser un mécanisme d’injection de dépendance tout en garantissant un niveau d'abstraction important entre les différents objets de notre application. © Note Le Spring Framework est capable de déduire ordre d'appel des différentes méthodes de fabrique selon les dépendances requises. Dans notre exemple, Spring doit nécessairement appeler la méthode | Lordre de déclara importance. Spring est capable de déterminer "ordre correct des appels en analysant le graphe des dépendances. Attention cependant a ne pas créer une dépendance cyclique (a dépend de b et b dépend de a). On comprend bien que ce type de situation ne peut pas tre résolu par le Spring Framework et aboutira a une erreur a la création du conteneur loc. Lexécution du programme précédent affiche (entre quelques lignes de log du Spring Framework) : Hello world Les méthodes d'initialisation et de destruction Le conteneur IoC du Spring Framework permet de gérer le cycle de vie des beans. II permet notamment d'invoquer des méthodes d’initialisation et de destruction de ces beans. Par exemple, un objet peut initialiser une connexion a une base de données au lancement de application et libérer cette connexion a la fin de l'application. La gestion des ressources est un cas courant pour lequel nous pouvons avoir besoin de réaliser une phase dnitialisation et/ou de destruction. ll est possible de préciser sur l'annotation @Bean le nom de la méthode d'initialisation avec Vattribut initmethod et le nom de la méthode de destruction avec l'attribut cestroymethod . Nous pouvons compléter notre exemple avec une nouvelle implémentation d'un fournisseur de données : Fournisseur de données d partir d'un fichier package dev. gayerie. appstrings Amport java.io. 10Exception; Anport java.nio. file.Filess import java.nio.file.Path; import java.util.function.suppliers public class FilebataSupplier implements Supplier private String data; public void readdata() throws 10Exception { data = Files.readstring(Path.of(“data.txt"))$ y override public String get() { return data; y Cette implémentation nécessite l'appel a la méthode readvata_ pour lire la chaine de caractéres a partir du fichier cata.txt . Dans notre classe représentant notre application, nous pouvons remplacer la méthode de fabrique application construite avec Spring Beowvausunn 2 B 4 as 16 v7 18 1» 20 a 2 23 24 2s 26 package dev. gayerie.appstring; Import java.util. function.Suppliens import org. springfranework. context .annotation .AnnotationConfigapplicationContext import ong. springfranework. context .annetation.Beans public class TaskApplication { G@Bean(initMethod = “readbata") public Supplier dataSupplier() { return new FileDatasupplier() y @Bean public Runnable task(Supplier dataSupplier) { return new WriterService(dataSupplier) ; y public static void main(String[] args) throws InterruptedException { ‘try (AnnotationConfighpplicationContext appctx lonConfigApplicationcontext (TaskApplication.class)) { appCtx. getSean(Runnable.class).run(); x x = new Annota } Ala ligne 10, nous indiquons que le bean posséde une méthode diinitialisation qui sera automatiquement appelée par le framework. @ Note Pourquoi ne pas appeler directement la méthode readoata dans la méthode de fabrique ? Gaean public Supplier dataSupplier() { y FileDatasupplier supplier = new FileDataSupplier()3 supplier readData() return supplier; Pour notre exemple, cette implémentation est équivalente. Cependant, nous verrons au chapitre suivant qu'il est possible de réaliser automatiquement des injections de dépendances sur le bean retourné par la méthode de fabrique. Dans ce cas, l'appel a la méthode diinitialisation est effectué aprés la phase d’injection afin de permettre a l'objet de initialiser avec toutes ses dépendances disponibles. Pour illustrer l'utilisation d'une méthode de destruction, nous allons complexifier un peu notre application, LAPI Java permet 'exécution de taches concurrentes en utilisant un Executor, La classe Executors est une classe outil qui nous permet de créer un exécuteur multi-thread. Cependant, il est nécessaire d’appeler la méthode shutdown de cet exécuteur pour attendre la fin des traitements concurrents et libérer correctement les ressources. La méthode shutdown correspond bien a une méthode de destruction a appeler sur l'exécuteur. Voici la nouvelle implémentation : application construite avec Spring package dev. gayerie.appstring; import java.util ArrayList; import java.util.List import java.util.concurrent. executors import java.util.concurrent executors; import java.util. function.Supplier; import org. springfranework.context . annotation, AnnotationConfigApplicationContext 1@ import org. springfranework. context .annotation.Bean; Ft 12 public class Taskapplication { B 14 @@ean 15 public Supplier dataSupplier() { 16 return new HardcodedSupplier(); wo} 18 19 @Bean(initMethod = “readbata") 20 public Suppliercstring> #ilebatasupplier() { a return new FileDataSupplien()5 2} 23 24 @aean 25 public List tasks(List> dataSuppliers) { 26 List tasks = new ArrayListo>(); 27 for (Supplier supplier : dataSuppliers) ( 28 tasks.add(new WriterService(supplier)); 29 y 30 return tasks; 3} 32 33 @Bean(destroyMethod = "shutdow! 34 public Executor executor(List tasks) ( 35 Executor executor = Executors.newCachedThreadPool() 5 36 for (Runnable task : tasks) { 37 executor.execute(task)3 38 x 39 return executors 4} a1 42 public static void main(String[] args) throws Interruptedexception { 43 try (AnnotationConfighpplicationContext appCtx = 4a, new AnnotationConfigApplicationContext (TaskApplication.class)) { 45 } 4%} 47 Aux lignes 33 a 40, nous déclarons une méthode de fabrique qui retourne un Executor. On déclare grace a 'annotation @Bean quill existe une méthode de destruction appelée shutdown. La méthode de fabrique crée l'exécuteur et ajoute la liste des taches recues en paramétre. Cette liste de taches est maintenant produite parla méthode tasks . Notez que cette méthode accepte en parametre une liste de suopliercstring> . Spring comprend quil s'agit de la liste de tous les objets compatibles avec l'interface supplier présents dans le contexte d'application, c'est-3 dire Vobjet retourné par l'appel 2. datasuppiien et objet retourné par 'appel a FileDatasupplier « Laméthode sain se contente de créer un contexte d'application puis de le fermer a la fin du bloc try . La fermeture du contexte d’application déclenche l'appel ala méthode shutdown sur l'exécuteur. Ainsi la fermeture du contexte d’application est suspendue le temps que toutes les taches concurrentes s'achévent. @ Note Pour plus d'information, reportez-vous a la documentation officielle.

You might also like