Introduction à Spring Framework pour Java

Cet article présente les bases de l'utilisation de Spring Framework pour Java.
D'un point de vue très, très élevé, Spring Framework déduit le comportement d'exécution d'un programme à partir d'étiquettes que le programmeur attache à des morceaux de code. Il existe de nombreux groupements d'étiquettes différents, et chaque groupement d'étiquettes fournit une interface pour la configuration d'un processus en arrière-plan.
Étant donné qu'un programme Spring Framework d'apparence simple avec seulement quelques étiquettes peut avoir beaucoup de choses à faire dans les coulisses, l'apprentissage de Spring Framework peut sembler un peu écrasant pour le débutant. L'apprentissage est rendu encore plus difficile par le fait que la plupart des ressources en ligne documentant Spring Framework parcourent au hasard différents types d'étiquettes au lieu de construire une compréhension fondamentale.
Cet article vise à combler cette lacune et à fournir une compréhension de base. Nous commencerons par Java vanille, puis, un modèle de conception de programmation à la fois, nous augmenterons notre compréhension de la façon dont ce modèle de conception est configuré dans Spring Framework avec des étiquettes.
Avant de commencer à lire sérieusement cet article…[19659006]Vous devez connaître les . Les "étiquettes" dont il a été question ci-dessus sont en réalité des annotations. J'ai mentionné que la plupart des ressources en ligne sur Spring Framework sont désorganisées et décevantes; son article est l'une des rares exceptions. Il est toujours bon d'avoir plusieurs lectures à partir desquelles apprendre un sujet, et je vous encourage donc à lire cet article en complément de celui-ci. déduit le comportement d'exécution d'un programme à partir d'annotations Java. Il s'agit d'une description précise au niveau de la surface de Spring Framework, mais ce n'est pas une bonne caractérisation à partir de laquelle développer une compréhension fondamentale.
Nous commencerons notre compréhension de Spring Framework avec une caractérisation différente. À la base, Spring Framework est un outil pour implémenter le modèle de conception appelé injection de dépendances.
Dans les instances d'objet sur class Cls
depends sont "injectés" dans une instance obj
de Cls
par un conteneur qui fait référence à obj
. Étant donné que le conteneur, plutôt que obj
contrôle le moment où les dépendances de obj
sont injectées, il est souvent appelé une inversion du conteneur de contrôle. L'injection de dépendance est aussi parfois appelée "inversion de contrôle" pour cette raison.
Quel est l'intérêt de l'injection de dépendance ? Cela nous permet d'éviter d'instancier des copies inutiles d'une dépendance.
Supposons que plusieurs classes nécessitent une référence à un objet qui représente une connexion à une base de données particulière. Étant donné que cette référence est une dépendance, nous pouvons facilement partager une seule connexion de base de données entre toutes les instances de classe en utilisant la technique d'injection de dépendance décrite ci-dessus. C'est bien mieux que de donner inutilement à chaque instance de classe sa propre connexion à la base de données.
Lorsque vous utilisez Spring Framework, nous passerons la plupart de notre temps à traiter et à penser aux « haricots Spring », qui sont les dépendances gérées par le Spring Framework. Conteneur IoC.
Pour être plus précis, un bean Spring est un objet non-nécessairement-sérialisable
qui…
est créé au moment de l'exécution par le conteneur Spring IoC (IoC signifie inversion de contrôle)
a des références à d'autres objets ou beans ("dépendances") qui lui sont injectés au moment de l'exécution par le conteneur Spring IoC
est autrement contrôlé au moment de l'exécution par le conteneur Spring IoC.
Les beans Spring peuvent être configurés via des fichiers XML ou en utilisant des annotations Java dans les classes Java. L'utilisation d'annotations est l'approche moderne. Cet article n'utilisera que des annotations – pas de code XML.
Notez qu'un bean Spring est différent d'un JavaBean. Les JavaBeans font partie du noyau du langage Java, contrairement aux Spring beans. (Plus précisément, un JavaBean est une classe Serializable
avec un constructeur public sans argument qui a tous les champs private
).
Dans ce qui précède, nous avons dit que les beans Spring sont créés au moment de l'exécution par le conteneur Spring IoC. Mais comment le conteneur Spring IoC sait-il quel type de beans créer ? Eh bien, vous, le programmeur, devez écrire un « code de configuration » qui spécifie quelles classes Java doivent être instanciées en tant que beans Spring lors de l'exécution. l'annotation ou l'annotation @Component
. (Vous pouvez également utiliser une annotation dérivée de @Component
).
Première méthode de configuration : @Bean
méthodes ed qui renvoient des instances
Pour spécifier que Cls[19659020] doit être instancié en tant que bean au moment de l'exécution, annotez une méthode qui renvoie une instance de
Cls
avec @Bean
et placez cette méthode annotée dans une classe qui est elle-même annotée avec [19659019]@Configuration :
@Configuration Configuration de la classe publique { @Haricot public Cls createABeanWrappingAClsInstance(Object args) { return new Cls(args); } }
Deuxième méthode de configuration : utilisez @Component
et @ComponentScan
Une autre façon de spécifier qu'une classe Cls
doit être instanciée comme un bean à runtime consiste à annoter la déclaration de classe de Cls
(c'est-à-dire classe publique Cls
) avec @Component
tout en annotant également une autre classe, par exemple Config
c'est-à-dire "au niveau ou au-dessus" du niveau de Cls
dans la hiérarchie des répertoires avec @Configuration
et @ComponentScan
:
@Configuration @ComponentScan /* La configuration doit être égale ou supérieure à Cls dans la hiérarchie des répertoires. */ Configuration de la classe publique {} @Composant public class Cls { ... }
(Lorsque vous créerez ultérieurement un ApplicationContext
dans la méthode main()
de votre application, vous devrez passer ApplicationContext .class
au constructeur ApplicationContext
.Mais, sisi vous utilisez Spring Boot, la classe contenant la méthode main()
est déjà secrètement annotée avec @Configuration
et @ComponentScan
vous n'avez donc rien d'autre à faire que d'annoter Cls
avec @Component
).
Plus précisément, l'utilisation de @ComponentScan
de cette manière spécifie que, lors de l'exécution, si une classe "au niveau ou en dessous" du niveau de Cls
dans la hiérarchie des répertoires est annotée par [19659019]@Component ou une annotation méta-annotée par @Component
(c'est-à-dire par @Service
@Repository
ou@ Controller
), alors cette classe sera utilisée pour construire un bean Spring.
Sidenote : annotation "héritage" dans Spring
Comme indiqué dans Spring Framework's . Ces annotations sont utilisées pour configurer les points de terminaison de l'API HTTP.
Lectures supplémentaires :
Nous savons maintenant comment configurer les beans Spring, mais nous ne savons pas encore comment injecter des dépendances Spring beans dans d'autres beans Spring. Nous décrivons comment procéder dans cette section.
Bien qu'avant de décrire comment procéder, il y a un peu plus de connaissances préalables que nous devrions couvrir.
Plus de connaissances préalables
Convention : "définition du haricot"[19659109]Pour la suite de ce document, le terme définition de bean fera référence à une méthode annotée par @Bean
qui renvoie une instance d'objet ou une classe annotée par @Component[19659026].
Portées de bean
Chaque définition de bean a une «portée» associée.
La portée par défaut (et la plus importante) est singleton
. Si un bean a une portée singleton
toutes les références à ce bean accèdent au même objet Java. la portée singleton
est utilisée pour réaliser le partage des dépendances, ce qui, si vous vous souvenez de la section "Injection de dépendances" ci-dessus, est l'un des principaux avantages de l'utilisation d'un conteneur IoC.
La deuxième portée la plus importante est [19659019] prototype . Si un bean est de portée prototype
différentes références à ce bean sont des références à différents objets Java.
Les quatre autres champs d'application, request
session
application
et websocket
ne peuvent être utilisés que dans un contexte d'application conscient » et sont moins couramment utilisés. Ne vous inquiétez pas pour ceux-ci.
Terminologie : "plain old Java objects" ("POJOs")
Une "plain old Java class" est une classe qui ne dépend pas d'un framework d'application tel que Printemps. Fondamentalement, étant donné que la plupart des fonctionnalités Spring sont gérées avec des annotations, une ancienne classe Java ordinaire est une classe sans aucune annotation Spring.
Malheureusement, les gens disent "plain old Java object" au lieu de "plain old Java class", nous parlons donc de POJO au lieu de POJC.
Les POJO sont souvent utilisés dans les applications Spring en combinaison avec des non-POJO pour représenter des objets "plus concrets" (tels qu'un Employé
etc.).
Lecture supplémentaire : http : //www.shaunabram.com/beans-vs-pojos/.
Implémentation de l'injection de dépendance quelque peu manuelle
Maintenant, nous sommes prêts à utiliser Spring Framework pour implémenter le modèle de conception d'injection de dépendance.[19659002]Supposons que nous ayons configuré un bean Spring nommé Cls1
qui fait référence à un bean Spring Cls2
:
@Component classe publique Cls1 { privé Cls2 cls2 ; public Cls2 getCls2() { return cls2; } public void setCls2(Cls2 cls2) { this.cls2 = cls2;} } @Composant public class Cls2 { ... }
Nous voulons injecter une instance de Cls2
dans notre bean Cls1
au moment de l'exécution. Pour ce faire, nous avons besoin d'une référence au conteneur Spring IoC. Étant donné que ApplicationContext
étend BeanFactory
et a donc plus de fonctionnalités, ApplicationContext
doit être utilisé dans la plupart des situations.
Nous effectuons l'injection de dépendances en utilisant ApplicationContext
comme suit :
application de classe publique { public static void main(String[] args) { /*est le package dans lequel rechercher les classes @Configuration et dans lequel effectuer @ComponentScan. Par example, pourrait être "com.perficient.techbootcamp.*" */ ApplicationContext ctx = nouvelle annotationConfigApplicationContext(" "); /* Ce qui suit suppose qu'une classe nommée Cls a été configurée en tant que bean (rappelez-vous, ceci est fait en utilisant @Component et @ComponentScan ou en utilisant @Bean). */ Cls1 cls1 = ctx.getBean(Cls1.class); /* Effectuer l'injection de dépendance : injecter une instance de cls2 dans le bean cls1. */ Cls2 cls2 = nouveau Cls2(); cls1.setCls2(cls2); } }
Le code ci-dessus est adapté de .
Implémentation de l'injection de dépendance avec @Autowired
Dans Spring Framework, on utilise généralement des annotations qui exécutent l'effet de l'injection de dépendance ci-dessus dans les coulisses. Plus précisément, on utilise l'annotation @Autowired
. Lorsque @Autowired
est présent sur le champ d'un bean, une instance du type de ce champ sera injectée dans ce champ lors de l'exécution.
Donc, si nous voulons reproduire la fonctionnalité ci-dessus, nous écrirons ce qui suit :
@Composant classe publique Cls2 { ... } @Composant classe publique Cls1 { @Autowired privé Cls2 cls2 ; public Cls2 getCls2() { return cls2; } // Remarquez, aucun setter n'est nécessaire. } Demande de classe publique { public static void main(String[] args) { ApplicationContext ctx = nouvelle annotationConfigApplicationContext(""); /* Le code ci-dessous a été commenté car il n'est pas nécessaire. L'annotation @Autowired ci-dessus indique à Spring Framework d'injecter une référence au bean Cls2 dans le bean Cls1 au moment de l'exécution. */ // Cls1 cls1 = ctx.getBean(Cls1.class); // Cls2 cls2 = new Cls2(); // cls1.setCls2(cls2); } }
Injection de champ avec @Autowired
Vous pouvez vous demander comment il est possible d'injecter une instance de Cls2
dans cls1
lorsque Cls1
n'a pas de méthode setCls2()
. Après y avoir réfléchi une seconde, vous pourriez soupçonner que l'injection est effectuée en utilisant le constructeur de Cls1
. Ce n'est en fait pas le cas. (Dans le code ci-dessus, Cls1
n'a pas de constructeur with-args !). Lorsque @Autowired
annote le champ d'un bean, alors, au moment de l'exécution, le conteneur IoC utilise cette pour modifier le champ, même s'il est privé
.[19659002]Placer @Autowired
sur un champ constitue donc une injection de champ.
Utiliser @Autowired
sur des champs est une mauvaise pratique
Selon [19]659259 cet articlel'utilisation de l'injection de champ est une mauvaise pratique car elle vous interdit de marquer les champs comme final
. (Vous voulez pouvoir marquer les champs comme final
le cas échéant, car cela vous empêche d'entrer dans une situation de dépendance circulaire).
Autres raisons pour lesquelles l'injection de champ est mauvaise : https:/ /dzone.com/articles/spring-di-patterns-the-good-the-bad-and-the-ugly.
Utilisation de @Autowired
sur les constructeurs et passeurs
@Autowired
peut également être utilisé sur des constructeurs ou des setters pour injecter un paramètre dans un constructeur ou un setter au moment de l'exécution.
L'annotation @Qualifier
Parce qu'un bean peut avoir un champ @Autowired
dont le type est une interface, et parce que plusieurs classes peuvent implémenter la même interface, il peut être nécessaire de spécifier quelle implémentation de l'interface est censée être injectée en dépendance. Cela se fait avec l'annotation @Qualifier
comme suit :
public interface Intf { ... } @Qualifier("impl1") @Composant la classe publique Impl1 étend Intf { ... } @Qualifier("impl2") @Composant la classe publique Impl2 étend Intf { ... } classe publique Cls { @Autowired @Qualifier("impl1") Cls1 privé cls1Instance ; // lors de l'exécution, cls1Instance sera défini sur une instance Cls1 @Autowired @Qualifier("impl2") Cls2 privé cls2Instance ; // lors de l'exécution, cls1Instance sera défini sur une instance Cls1 }
Voici les spécificités de la façon dont les noms de champs sont mis en correspondance avec les noms de bean :
Définissez le qualifier-name d'une définition de bean ou d'un champ comme étant : (1) l'argument du
@Qualifier
annotation attachée à ladite définition ou champ de bean, si la définition ou le champ de bean est effectivement annoté avec@Qualifier
et (2) le nom de la classe associée à la définition de bean , si la définition ou le champ du bean n'est pas annoté avec@Qualifier
.Lorsqu'aucune annotation
@Qualifier
n'est présente sur un champ, alors la classe dont le nom de qualificateur indépendant de la casse est égal au nom indépendant de la casse du champ correspond à la dépendance injectée dans le champ. ("Case agnostic" signifie "ignorer la casse").