Les scopes des beans Spring nous permettent d’avoir un contrôle plus précis sur la création des instances de beans. Parfois, nous voulons créer une instance de bean en tant que singleton, mais dans d’autres cas, nous voulons qu’elle soit créée à chaque requête ou une fois par session.
Scopes des beans Spring
Il existe cinq types de scopes de beans Spring :
- singleton – une seule instance du bean Spring sera créée pour le conteneur Spring. C’est le scope par défaut des beans Spring. Lorsque vous utilisez ce scope, assurez-vous que le bean n’a pas de variables d’instance partagées, sinon cela pourrait entraîner des problèmes d’incohérence des données.
- prototype – une nouvelle instance sera créée à chaque fois que le bean est demandé au conteneur Spring.
- request – c’est la même chose que le scope prototype, mais il est destiné à être utilisé pour les applications web. Une nouvelle instance du bean sera créée pour chaque requête HTTP.
- session – un nouveau bean sera créé pour chaque session HTTP par le conteneur.
- global-session – cela est utilisé pour créer des beans de session globaux pour les applications Portlet.
Singleton et Prototype Scope de Spring Bean
Les portées singleton et prototype des beans Spring peuvent être utilisées dans des applications Spring autonomes. Voyons comment nous pouvons facilement configurer ces portées en utilisant l’annotation @Scope
. Supposons que nous ayons une classe de bean Java.
package com.journaldev.spring;
public class MyBean {
public MyBean() {
System.out.println("MyBean instance created");
}
}
Définissons la classe de configuration Spring où nous définirons la méthode pour obtenir une instance de MyBean à partir du conteneur Spring.
package com.journaldev.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class MyConfiguration {
@Bean
@Scope(value="singleton")
public MyBean myBean() {
return new MyBean();
}
}
Remarquez que singleton
est la portée par défaut, nous pouvons donc supprimer @Scope(value="singleton")
de la définition du bean ci-dessus. Créons maintenant une méthode principale et testons la portée singleton.
package com.journaldev.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MySpringApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(MyConfiguration.class);
ctx.refresh();
MyBean mb1 = ctx.getBean(MyBean.class);
System.out.println(mb1.hashCode());
MyBean mb2 = ctx.getBean(MyBean.class);
System.out.println(mb2.hashCode());
ctx.close();
}
}
Lorsque le programme ci-dessus est exécuté, nous obtiendrons une sortie comme celle-ci.
MyBean instance created
867988177
867988177
Remarquez que les deux instances de MyBean ont le même code de hachage et le constructeur est appelé une seule fois, ce qui signifie que le conteneur Spring renvoie toujours la même instance de MyBean. Modifions maintenant la portée en prototype
.
@Bean
@Scope(value="prototype")
public MyBean myBean() {
return new MyBean();
}
Cette fois-ci, nous obtiendrons la sortie suivante lorsque la méthode principale est exécutée.
MyBean instance created
867988177
MyBean instance created
443934570
Il est clair que l’instance de MyBean est créée à chaque fois qu’elle est demandée par le conteneur Spring. Modifions maintenant la portée en request
.
@Bean
@Scope(value="request")
public MyBean myBean() {
return new MyBean();
}
Dans ce cas, nous obtiendrons l’exception suivante.
Exception in thread "main" java.lang.IllegalStateException: No Scope registered for scope name 'request'
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:347)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:224)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1015)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:339)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:334)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1107)
at com.journaldev.spring.MySpringApp.main(MySpringApp.java:12)
C’est parce que les portées request
, session
et global-session
ne sont pas disponibles pour les applications autonomes.
Portée de demande et de session du bean Spring
Pour l’exemple de portée de demande et de session du bean Spring, nous allons créer une application web Spring Boot. Créez un projet de démarrage Spring Boot et choisissez « web » afin que nous puissions l’exécuter comme une application web. Notre projet final ressemblera à l’image ci-dessous.
ServletInitializer
et SpringBootMvcApplication
sont des classes spring boot générées automatiquement. Nous n’avons pas besoin d’apporter de modifications là-bas. Voici mon fichier pom.xml, jetez un œil aux dépendances de notre application. Votre fichier pom.xml pourrait être légèrement différent en fonction de la version d’Eclipse que vous utilisez.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.spring</groupId>
<artifactId>Spring-Boot-MVC</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Spring-Boot-MVC</name>
<description>Spring Beans Scope MVC</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>10</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Créons quelques composants Spring et configurons-les en tant que beans Spring dans le conteneur Spring avec une portée request
et session
.
Portée de demande du bean Spring
package com.journaldev.spring;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DataRequestScope {
private String name = "Request Scope";
public DataRequestScope() {
System.out.println("DataRequestScope Constructor Called");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Portée de Session de Spring Bean
package com.journaldev.spring;
import org.springframework.context.annotation.Scope;
import java.time.LocalDateTime;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DataSessionScope {
private String name = "Session Scope";
public DataSessionScope() {
System.out.println("DataSessionScope Constructor Called at "+LocalDateTime.now());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Composant Spring
Maintenant, créons un composant Spring et utilisons Spring pour configurer automatiquement les beans ci-dessus.
package com.journaldev.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Customer {
@Autowired
private DataRequestScope dataRequestScope;
@Autowired
private DataSessionScope dataSessionScope;
public DataRequestScope getDataRequestScope() {
return dataRequestScope;
}
public void setDataRequestScope(DataRequestScope dataRequestScope) {
this.dataRequestScope = dataRequestScope;
}
public DataSessionScope getDataSessionScope() {
return dataSessionScope;
}
public void setDataSessionScope(DataSessionScope dataSessionScope) {
this.dataSessionScope = dataSessionScope;
}
}
Contrôleur Spring Rest
Enfin, créons une classe RestController et configurons quelques points d’API à des fins de test.
package com.journaldev.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloData {
@Autowired
private Customer customer;
@RequestMapping("/nameRS")
public String helloRS() {
return customer.getDataRequestScope().getName();
}
@RequestMapping("/nameSSUpdated")
public String helloSSUpdated() {
customer.getDataSessionScope().setName("Session Scope Updated");
return customer.getDataSessionScope().getName();
}
@RequestMapping("/nameSS")
public String helloSS() {
return customer.getDataSessionScope().getName();
}
}
Configuration du délai de session de Spring Boot
Enfin, nous devons configurer les variables de délai de session de Spring Boot, ajoutez les propriétés suivantes dans src/main/resources/application.properties
.
server.session.cookie.max-age= 1
server.session.timeout= 1
Maintenant, nos beans Spring avec une portée de session seront invalidés en une minute. Exécutez simplement la classe SpringBootMvcApplication
en tant qu’application Spring Boot. Vous devriez voir la sortie ci-dessous pour la configuration de nos points d’extrémité.
2018-05-23 17:02:25.830 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameRS]}" onto public java.lang.String com.journaldev.spring.HelloData.helloRS()
2018-05-23 17:02:25.831 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameSSUpdated]}" onto public java.lang.String com.journaldev.spring.HelloData.helloSSUpdated()
2018-05-23 17:02:25.832 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameSS]}" onto public java.lang.String com.journaldev.spring.HelloData.helloSS()
Test de la portée de la demande de Spring Bean
Ouvrez n’importe quel navigateur et allez à l’URL https://localhost:8080/nameRS
et vérifiez la sortie de la console. Vous devriez voir DataRequestScope Constructor Called
s’afficher à chaque demande.
Test de la portée de session du Spring Bean
Allez sur https://localhost:8080/nameSS
et vous obtiendrez la sortie suivante. Maintenant, allez sur
https://localhost:8080/nameSSUpdated
afin que la valeur du nom DataSessionScope
soit mise à jour en Session Scope Updated
. Maintenant, allez de nouveau sur
https://localhost:8080/nameSS
et vous devriez voir la valeur mise à jour. À ce stade, vous ne devriez voir qu’une seule fois
DataSessionScope Constructor Called at XXX
dans la sortie de la console. Attendez ensuite 1 minute pour que notre bean à portée de session soit invalidé. Ensuite, allez de nouveau sur https://localhost:8080/nameSS
et vous devriez voir la sortie originale. Vous devriez également vérifier le message de la console pour la création de DataSessionScope à nouveau par le conteneur. C’est tout pour le tutoriel sur la portée des beans Spring.
Vous pouvez télécharger le projet Spring Beans Scope Spring Boot depuis notre référentiel GitHub.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-scopes