Dans l' article précédent , j'ai brièvement expliqué ce qu'est le printemps et ce que sont les haricots et le contexte. Il est maintenant temps de l'essayer. Je vais le faire en utilisant IntelliJ IDEA Enterprise Edition. Mais tous mes exemples devraient également fonctionner dans l'édition communautaire gratuite d'IntelliJ IDEA. Dans les captures d'écran, si vous voyez que j'ai une fenêtre que vous n'avez pas, ne vous inquiétez pas — ce n'est pas important pour ce projet :) Tout d'abord, créez un projet Maven vide. J'ai montré comment faire cela dans l'article sur ce lien . Lisez jusqu'aux mots " Il est temps pour nous de convertir notre projet Maven en un projet Web. " — Après cela, l'article montre comment créer un projet Web, mais nous n'en avons pas besoin pour le moment. Dans le src/main/javadossier, créez un package (dans mon cas, je l'ai appelé "
en.codegym.info.fatfaggy.animals
. Vous pouvez l'appeler comme vous voulez. N'oubliez pas de remplacer le nom de mon package par le nom de votre package aux bons endroits. Maintenant, créez la Main
classe et ajoutez un méthode
public static void main(String[] args) {
...
}
Après cela, ouvrez le fichier pom.xml et ajoutez la dependencies
section. Allez maintenant dans le référentiel Maven et recherchez le contexte Spring pour la dernière version stable. Mettez ce que nous trouvons dans la dependencies
section. J'ai décrit ce processus plus en détail dans cet autre article de CodeGym (voir la section intitulée " Connecter les dépendances dans Maven "). Ensuite, Maven lui-même trouvera et téléchargera les dépendances nécessaires. Au final, vous devriez obtenir quelque chose comme ceci : Dans la fenêtre de gauche, vous pouvez voir la structure du projet avec le package et la Main
classe. La fenêtre du milieu montre à quoi ressemble pom.xml pour moi. J'ai aussi ajouté une propriétésection à elle. Cette section indique à Maven quelle version de Java j'utilise dans mes fichiers source et quelle version compiler. C'est juste pour qu'IDEA ne m'avertit pas que j'utilise une ancienne version de Java. Ceci est facultatif :) La fenêtre de droite indique clairement que même si nous n'avons connecté que le module spring-context, il a automatiquement inséré les modules spring-core, spring-beans, spring-aop et spring-expression. Nous aurions pu connecter chaque module séparément, en écrivant une dépendance pour chaque module avec la version explicite dans le fichier pom.xml, mais pour l'instant nous sommes satisfaits des choses telles qu'elles sont. Créez maintenant le entities
package et créez-y 3 classes : Cat
, Dog
, Parrot
. Donnons un nom à chaque animal (private String name
- vous pouvez y coder en dur certaines valeurs). Les getters/setters sont publics. Passons maintenant à la Main
classe et à la main()
méthode, et nous écrivons quelque chose comme ceci :
public static void main(String[] args) {
// Create an empty Spring context that will look for its own beans based on the annotations in the specified package
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.entities");
Cat cat = context.getBean(Cat.class);
Dog dog = (Dog) context.getBean("dog");
Parrot parrot = context.getBean("parrot-polly", Parrot.class);
System.out.println(cat.getName());
System.out.println(dog.getName());
System.out.println(parrot.getName());
}
Tout d'abord, nous créons un objet de contexte, indiquant au constructeur dans quel package rechercher les beans. En d'autres termes, Spring parcourra ce package et essaiera de trouver des classes marquées d'annotations spéciales indiquant qu'il s'agit de beans. Ensuite, il crée les objets de ces classes et les place dans le contexte. Après cela, nous obtenons un chat de ce contexte. Nous appelons l'objet de contexte pour nous donner un bean (objet), indiquant la classe de l'objet que nous voulons (d'ailleurs, nous pouvons également spécifier des interfaces, pas seulement des classes). Après cela, Spring renvoie un objet de la classe demandée, que nous sauvegardons ensuite dans une variable. Ensuite, nous demandons à Spring de nous procurer un haricot appelé "chien". Quand le printemps crée unDog
object, il donne à l'objet un nom standard (sauf si un nom a été explicitement attribué au bean créé), qui est le nom de la classe mais avec une lettre initiale en minuscule. Dans ce cas, notre classe s'appelle Dog
, donc le nom du bean est be "dog". Si nous avions besoin d'un BufferedReader
objet là-bas, alors Spring le nommerait "bufferedReader". Et comme Java ne peut pas être sûr à 100 % de la classe que nous voulons, il renvoie un Object
objet, que nous transtypons ensuite manuellement dans le type souhaité, c'est-à-direDog
. L'option où la classe est indiquée explicitement est plus pratique. La troisième option consiste à obtenir un bean par nom de classe et par nom de bean. Il est possible que le contexte ait plusieurs beans d'une même classe. Afin d'indiquer le haricot particulier dont nous avons besoin, nous indiquons son nom. Comme nous indiquons également explicitement la classe ici, nous n'avons plus à effectuer de cast. IMPORTANT!Si Spring trouve plusieurs beans qui correspondent à nos besoins, il ne peut pas déterminer quel bean nous donner, il lèvera donc une exception. Par conséquent, pour éviter cette situation, vous devriez essayer d'être aussi précis que possible en indiquant à Spring de quel bean vous avez besoin. Si Spring recherche son contexte et ne parvient pas à trouver un seul bean correspondant à nos exigences, il lèvera également une exception. Enfin, nous affichons simplement les noms de nos animaux pour vérifier que nous avons bien récupéré les objets dont nous avons besoin. Mais si nous exécutons le programme maintenant, nous verrons que Spring est mécontent - il ne peut pas trouver les animaux dont nous avons besoin dans son contexte. C'est parce qu'il n'a pas créé ces beans. Comme je l'ai dit précédemment, lorsque Spring analyse les classes, il recherche ses propres annotations Spring. Et si Spring ne trouve pas ces annotations, alors il ne le fait pas Ne pensez pas que ces classes correspondent à des beans qu'il doit créer. Pour résoudre ce problème, il suffit d'ajouter le@Component
annotation devant chacune de nos classes d'animaux.
@Component
public class Cat {
private String name = "Oscar";
...
}
Mais il y a plus. Si nous devons indiquer explicitement à Spring que le bean de cette classe doit avoir un nom spécifique, nous indiquons le nom entre parenthèses après l'annotation. Par exemple, pour dire à Spring de donner le nom " parrot-polly
" au haricot perroquet, qui est le nom que nous utiliserons pour obtenir ce perroquet dans la main
méthode, nous devrions faire quelque chose comme ceci :
@Component("parrot-polly")
public class Parrot {
private String name = "Polly";
...
}
C'est tout l'intérêt de la configuration automatique . Vous écrivez vos classes, marquez-les avec les annotations nécessaires et indiquez à Spring le package contenant vos classes. Il s'agit du package que le framework exécutera pour trouver des annotations et créer des objets de ces classes. Soit dit en passant, Spring ne recherche pas seulement @Component
les annotations, mais également toutes les autres annotations qui héritent de celle-ci. Par exemple, @Controller
, @RestController
, @Service
, @Repository
, et plus, que nous présenterons dans de futurs articles. Nous allons maintenant essayer de faire la même chose en utilisant une configuration basée sur Java . Pour commencer, supprimez le@Component
annotations de nos cours. Pour rendre les choses plus difficiles, imaginez que nous n'avons pas écrit ces classes, donc nous ne pouvons pas les modifier facilement, ce qui signifie que nous ne pouvons pas ajouter d'annotations. C'est comme si ces classes étaient regroupées dans une bibliothèque. Dans ce cas, nous n'avons aucun moyen de modifier ces classes afin qu'elles soient reconnues par Spring. Mais nous avons besoin d'objets de ces classes ! Ici, nous avons besoin d'une configuration basée sur Java pour créer les objets. Pour commencer, créez un package avec un nom comme configs
. Dans ce package, créez une classe Java ordinaire, quelque chose comme MyConfig
, et marquez-la avec l' @Configuration
annotation.
@Configuration
public class MyConfig {
}
Maintenant, nous devons ajuster la main()
méthode, en changeant la façon dont nous créons le contexte. Nous pouvons soit indiquer explicitement quelle classe a notre configuration :
ApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
Si nous avons plusieurs classes différentes qui créent des beans et que nous voulons en connecter plusieurs simultanément, nous les indiquons simplement toutes ici, séparées par des virgules :
ApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class, MyAnotherConfig.class);
Et si nous en avons trop et que nous voulons tous les connecter simultanément, nous indiquons simplement le nom du package dans lequel ils sont contenus :
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.configs");
Dans ce cas, Spring parcourra le package et trouvera toutes les classes marquées avec l' @Configuration
annotation. Eh bien, et si nous avons un très gros programme où les configurations sont divisées en différents packages, nous indiquons simplement une liste délimitée par des virgules des noms des packages contenant les configurations :
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.database.configs",
"en.codegym.info.fatfaggy.animals.root.configs",
"en.codegym.info.fatfaggy.animals.web.configs");
Ou le nom d'un package qui leur est commun :
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals");
Vous pouvez le faire comme bon vous semble, mais il me semble que la toute première option, qui indique simplement une classe avec les configurations, conviendra le mieux à notre programme. Lors de la création d'un contexte, Spring recherche les classes marquées avec l' @Configuration
annotation et crée ses propres objets de ces classes. Il tente d'appeler des méthodes marquées avec l' @Bean
annotation, ce qui signifie que ces méthodes renvoient des beans (objets) que Spring ajoutera au contexte. Et maintenant, nous allons créer des beans pour un chat, un chien et un perroquet dans notre classe avec une configuration basée sur Java. C'est assez simple à faire :
@Bean
public Cat getCat() {
return new Cat();
}
Ici, nous créons manuellement notre chat et le remettons à Spring, qui maintient ensuite notre objet dans son contexte. Puisque nous n'avons pas explicitement donné de nom à notre bean, Spring lui donnera le même nom que le nom de la méthode. Dans notre cas, le haricot chat s'appellera " getCat
". Mais parce que nous utilisons la classe, pas le nom, pour obtenir le haricot chat dans la main
méthode, le nom du haricot n'est pas important pour nous. De même, créez un dog bean, en gardant à l'esprit que Spring donnera le nom de la méthode au bean. Pour nommer explicitement notre haricot perroquet, nous indiquons simplement son nom entre parenthèses après l' @Bean
annotation :
@Bean("parrot-polly")
public Object weNeedMoreParrots() {
return new Parrot();
}
Comme vous pouvez le voir, j'ai indiqué ici un Object
type de retour et donné à la méthode un nom arbitraire. Cela n'affecte pas le nom du bean, car nous avons explicitement spécifié le nom ici. Néanmoins, il est préférable d'indiquer une valeur de retour et un nom de méthode plus ou moins significatifs. Faites-le si ce n'est que pour vous rendre service lorsque vous rouvrirez le projet dans un an. :) Considérons maintenant la situation où nous avons besoin d'un bean pour en créer un autre . Par exemple, supposons que nous voulions que le nom du chat dans le haricot chat soit le nom du perroquet plus la chaîne "-killer". Aucun problème!
@Bean
public Cat getCat(Parrot parrot) {
Cat cat = new Cat();
cat.setName(parrot.getName() + "-killer");
return cat;
}
Ici, Spring verra que pour créer ce bean, le framework doit passer dans le bean perroquet créé précédemment. En conséquence, il organisera la chaîne nécessaire d'appels de méthode : d'abord, la méthode de création de perroquet est appelée, puis le framework passe le nouveau perroquet à la méthode de création de chat. C'est là que l'injection de dépendances entre en jeu : Spring lui-même transmet le bean perroquet requis à notre méthode. Si IDEA est contrarié par la parrot
variable, n'oubliez pas de changer le type de retour de la méthode de création de perroquet de Object
à Parrot
. De plus, la configuration basée sur Java vous permet d'exécuter absolument n'importe quel code Javadans vos méthodes de création de haricots. Vous pouvez vraiment tout faire : créer d'autres objets auxiliaires, appeler d'autres méthodes, même celles qui ne sont pas marquées d'annotations Spring, créer des boucles, des conditions booléennes, tout ce qui vous vient à l'esprit ! Tout cela n'est pas possible avec la configuration automatique, et encore moins avec la configuration XML. Considérons maintenant un problème un peu plus amusant. Polymorphisme et interfaces :) Nous allons créer une WeekDay
interface et créer 7 classes qui implémentent cette interface : Monday
, Tuesday
, Wednesday
, Thursday
, Friday
, Saturday
, Sunday
. Nous allons donner à l'interface une String getWeekDayName()
méthode, qui renverra le nom du jour de la semaine pour la classe correspondante. Autrement dit, la Monday
classe reviendra"Monday
", etc. Au démarrage de l'application, supposons que notre tâche consiste à placer un bean correspondant au jour de la semaine en cours dans le contexte. Pas des beans pour toutes les classes qui implémentent l'interface - un seul bean dont nous avons besoin. Vous WeekDay
pouvez fais ça à peu près comme ça :
@Bean
public WeekDay getDay() {
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
switch (dayOfWeek) {
case MONDAY: return new Monday();
case TUESDAY: return new Tuesday();
case WEDNESDAY: return new Wednesday();
case THURSDAY: return new Thursday();
case FRIDAY: return new Friday();
case SATURDAY: return new Saturday();
default: return new Sunday();
}
}
Ici, le type de retour est notre interface. La méthode renvoie un objet authentique de l'une des classes qui implémentent l'interface, en fonction du jour de la semaine en cours. Maintenant, nous pouvons faire ce qui suit dans la main()
méthode :
WeekDay weekDay = context.getBean(WeekDay.class);
System.out.println("Today is " + weekDay.getWeekDayName() + "!");
Pour moi, le programme me dit que c'est dimanche :) Je suis convaincu que si je lance le programme demain, le contexte contiendra un objet complètement différent. Notez que nous obtenons le bean simplement en utilisant l'interface : context.getBean(WeekDay.class)
. Spring recherchera dans son contexte le bean qui implémente l'interface et le renverra. Ensuite, il s'avère que notre WeekDay
variable se retrouve avec un objet dimanche, et le concept familier de polymorphisme s'applique lorsque nous travaillons avec cette variable. :) Maintenant, quelques mots sur l' approche combinée , dans laquelle certains beans sont créés automatiquement par Spring, certains en analysant les packages pour les classes avec l' @Component
annotation, et d'autres par une configuration basée sur Java. En considérant cela, nous reviendrons à la version originale, où les Cat
, Dog
etParrot
les classes ont été marquées avec l' @Component
annotation. Supposons que nous voulions créer des haricots pour nos animaux en demandant à Spring de scanner automatiquement le entities
paquet, mais nous voulons également créer un haricot avec le jour de la semaine, comme nous venons de le faire. Tout ce que vous avez à faire est d'ajouter l' @ComponentScan
annotation au niveau de la MyConfig
classe, que nous indiquons lors de la création du contexte dans main()
, et d'indiquer entre parenthèses le package qui doit être scanné et de créer automatiquement les beans des classes nécessaires :
@Configuration
@ComponentScan("en.codegym.info.fatfaggy.animals.entities")
public class MyConfig {
@Bean
public WeekDay getDay() {
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
switch (dayOfWeek) {
case MONDAY: return new Monday();
case TUESDAY: return new Tuesday();
case WEDNESDAY: return new Wednesday();
case THURSDAY: return new Thursday();
case FRIDAY: return new Friday();
case SATURDAY: return new Saturday();
default: return new Sunday();
}
}
}
Lors de la création du contexte, Spring voit qu'il doit traiter la MyConfig
classe. Il entre dans la classe et voit qu'il doit analyser le en.codegym.info.fatfaggy.animals.entities
package " " et créer des beans de ces classes, après quoi il exécute la méthode MyConfig
de la classe getDay()
et ajoute un WeekDay
bean au contexte. Dans la main()
méthode, nous avons maintenant accès à tous les beans dont nous avons besoin : à la fois des objets animaux et un bean avec le jour de la semaine. Si jamais vous avez besoin que Spring récupère également des fichiers de configuration XML, vous pouvez faire votre propre recherche sur le Web pour trouver une explication :) Résumé :
- Essayez d'utiliser la configuration automatique
- Lors de la configuration automatique, indiquez le nom du package qui contient les classes dont les beans doivent être créés
- Ces classes sont marquées de l'
@Component
annotation - Spring parcourt toutes ces classes, crée des objets et les place dans le contexte ;
- Si la configuration automatique ne nous convient pas pour une raison quelconque, nous utilisons la configuration basée sur Java
- Dans ce cas, nous créons une classe Java ordinaire dont les méthodes renvoient les objets dont nous avons besoin. Nous marquons cette classe avec l'
@Configuration
annotation si nous allons scanner l'ensemble du package plutôt que d'indiquer une classe spécifique avec la configuration lors de la création du contexte - Les méthodes de cette classe qui renvoient des beans sont marquées avec l'
@Bean
annotation
@ComponentScan
annotation.
GO TO FULL VERSION