Guide rapide sur les champs de haricots printaniers

1. Vue d’ensemble

Dans ce didacticiel rapide, vous découvrirez les différents types d’étendues de beans du framework Spring.

La portée d’un haricot définit le cycle de vie et la visibilité de ce haricot dans les contextes dans lesquels il est utilisé.

La dernière version du framework Spring définit 6 types d’étendues:

  • singleton

  • prototype

  • demande

  • session

  • application

  • websocket

Les quatre dernières portées mentionnées, demande, session, application et ____websocket, sont uniquement disponibles dans une application compatible Web.

2. Singleton Scope

La définition d’un bean avec singleton scope signifie que le conteneur crée une instance unique de ce bean et que toutes les demandes pour ce nom de bean renverront le même objet, qui est mis en cache. Toute modification de l’objet sera reflétée dans toutes les références au bean. Cette étendue est la valeur par défaut si aucune autre étendue n’est spécifiée.

Créons une entité Person pour illustrer le concept de portées:

public class Person {
    private String name;

   //standard constructor, getters and setters
}

Ensuite, nous définissons le bean avec singleton scope en utilisant l’annotation @ Scope :

@Bean
@Scope("singleton")
public Person personSingleton() {
    return new Person();
}

Nous pouvons également utiliser une constante au lieu de la valeur String de la manière suivante:

@Scope(value = ConfigurableBeanFactory.SCOPE__SINGLETON)

Nous allons maintenant écrire un test qui montre que deux objets faisant référence au même bean auront les mêmes valeurs, même si un seul d’entre eux change d’état, car ils font tous deux référence à la même instance de bean:

private static final String NAME = "John Smith";

@Test
public void givenSingletonScope__whenSetName__thenEqualNames() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scopes.xml");

    Person personSingletonA = (Person) applicationContext.getBean("personSingleton");
    Person personSingletonB = (Person) applicationContext.getBean("personSingleton");

    personSingletonA.setName(NAME);
    Assert.assertEquals(NAME, personSingletonB.getName());

    ((AbstractApplicationContext) applicationContext).close();
}

Le fichier scopes.xml dans cet exemple doit contenir les définitions XML des beans utilisés:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="personSingleton" class="org.baeldung.scopes.Person" scope="singleton"/>
</beans>

3. Portée du prototype

Un bean avec la portée de prototype renverra une instance différente chaque fois qu’il est demandé à partir du conteneur. Pour le définir, définissez la valeur prototype sur l’annotation @ Scope dans la définition du bean:

@Bean
@Scope("prototype")
public Person personPrototype() {
    return new Person();
}

Nous pourrions également utiliser une constante, comme nous l’avons fait pour le singleton scope:

@Scope(value = ConfigurableBeanFactory.SCOPE__PROTOTYPE)

Nous allons maintenant écrire un test similaire, montrant que deux objets demandant le même nom de bean avec le prototype de scope auront des états différents, car ils ne font plus référence à la même instance de bean:

private static final String NAME = "John Smith";
private static final String NAME__OTHER = "Anna Jones";

@Test
public void givenPrototypeScope__whenSetNames__thenDifferentNames() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scopes.xml");

    Person personPrototypeA = (Person) applicationContext.getBean("personPrototype");
    Person personPrototypeB = (Person) applicationContext.getBean("personPrototype");

    personPrototypeA.setName(NAME);
    personPrototypeB.setName(NAME__OTHER);

    Assert.assertEquals(NAME, personPrototypeA.getName());
    Assert.assertEquals(NAME__OTHER, personPrototypeB.getName());

    ((AbstractApplicationContext) applicationContext).close();
}

Le fichier scopes.xml est similaire à celui présenté dans la section précédente tout en ajoutant la définition xml du bean avec la portée de prototype :

<bean id="personPrototype" class="org.baeldung.scopes.Person" scope="prototype"/>

4. Portées conscientes du Web

Comme indiqué précédemment, quatre portées supplémentaires ne sont disponibles que dans un contexte d’application Web. Ceux-ci sont moins souvent utilisés dans la pratique.

La portée request crée une instance de bean pour une seule requête HTTP tandis que la portée s _ession _ est créée pour une session HTTP.

__Application scope crée l’instance de bean du cycle de vie a ServletContext et websocket scope le crée pour une WebSocket __session particulière.

Créons une classe à utiliser pour instancier les beans:

public class HelloMessageGenerator {
    private String message;

   //standard getter and setter
}

4.1. Portée de la demande

Nous pouvons définir le bean avec request scope en utilisant l’annotation @ Scope :

@Bean
@Scope(value = WebApplicationContext.SCOPE__REQUEST, proxyMode = ScopedProxyMode.TARGET__CLASS)
public HelloMessageGenerator requestScopedBean() {
    return new HelloMessageGenerator();
}

L’attribut proxyMode est nécessaire car, au moment de l’instanciation du contexte de l’application Web, il n’y a pas de requête active. Spring créera un proxy à injecter en tant que dépendance et instanciera le bean cible lorsqu’il en aura besoin dans une demande.

Ensuite, nous pouvons définir un contrôleur qui a une référence injectée à requestScopedBean . Nous devons accéder à la même demande deux fois afin de tester les portées spécifiques au Web.

Si nous affichons le message à chaque fois que la demande est exécutée, nous pouvons voir que la valeur est réinitialisée à null , même si elle est modifiée ultérieurement dans la méthode. Cela est dû au fait qu’une instance de bean différente est renvoyée pour chaque demande.

@Controller
public class ScopesController {
    @Resource(name = "requestScopedBean")
    HelloMessageGenerator requestScopedBean;

    @RequestMapping("/scopes/request")
    public String getRequestScopeMessage(final Model model) {
        model.addAttribute("previousMessage", requestScopedBean.getMessage());
        requestScopedBean.setMessage("Good morning!");
        model.addAttribute("currentMessage", requestScopedBean.getMessage());
        return "scopesExample";
    }
}

4.2. Portée de la session

Nous pouvons définir le bean avec session de la même manière:

@Bean
@Scope(value = WebApplicationContext.SCOPE__SESSION, proxyMode = ScopedProxyMode.TARGET__CLASS)
public HelloMessageGenerator sessionScopedBean() {
    return new HelloMessageGenerator();
}

Ensuite, nous définissons un contrôleur avec une référence au sessionScopedBean . De nouveau, nous devons exécuter deux demandes afin de montrer que la valeur du champ message est la même pour la session.

Dans ce cas, lorsque la demande est faite pour la première fois, la valeur message est _null. Mais une fois, il est modifié, puis cette valeur _ est conservée pour les demandes suivantes, car la même instance du bean est renvoyée pour toute la session.

@Controller
public class ScopesController {
    @Resource(name = "sessionScopedBean")
    HelloMessageGenerator sessionScopedBean;

    @RequestMapping("/scopes/session")
    public String getSessionScopeMessage(final Model model) {
        model.addAttribute("previousMessage", sessionScopedBean.getMessage());
        sessionScopedBean.setMessage("Good afternoon!");
        model.addAttribute("currentMessage", sessionScopedBean.getMessage());
        return "scopesExample";
    }
}

4.3. Champ d’application

_Application scope crée l’instance de bean pour le cycle de vie d’un ServletContext._

Ceci est similaire à la portée singleton, mais il existe une différence très importante en ce qui concerne la portée du haricot.

Lorsque les beans sont application étendus, la même instance est partagée entre plusieurs applications basées sur des servlets s’exécutant dans le même ServletContext , tandis que les beans singleton-étendus sont étendus à un seul contexte d’application.

Créons le bean avec application scope:

@Bean
@Scope(value = WebApplicationContext.SCOPE__APPLICATION, proxyMode = ScopedProxyMode.TARGET__CLASS)
public HelloMessageGenerator applicationScopedBean() {
    return new HelloMessageGenerator();
}

Et le contrôleur qui référence ce haricot:

@Controller
public class ScopesController {
    @Resource(name = "applicationScopedBean")
    HelloMessageGenerator applicationScopedBean;

    @RequestMapping("/scopes/application")
    public String getApplicationScopeMessage(final Model model) {
        model.addAttribute("previousMessage", applicationScopedBean.getMessage());
        applicationScopedBean.setMessage("Good afternoon!");
        model.addAttribute("currentMessage", applicationScopedBean.getMessage());
        return "scopesExample";
    }
}

Dans ce cas, la valeur message une fois définie dans _applicationScopedBean sera conservée pour toutes les demandes et sessions ultérieures et même pour une application de servlet différente qui accédera à ce bean, à condition qu’il soit exécuté dans le même ServletContext._

4.4. Portée WebSocket

Enfin, créons le haricot avec __websocket __scope:

@Bean
@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET__CLASS)
public HelloMessageGenerator websocketScopedBean() {
    return new HelloMessageGenerator();
}

Les beans à la portée de WebSocket lors du premier accès sont stockés dans les attributs de session WebSocket . La même instance du bean est ensuite renvoyée à chaque accès à ce bean pendant toute la session WebSocket .

Nous pouvons également dire qu’il présente un comportement singleton, mais limité à une session WebSocket uniquement.

5. Conclusion

Nous avons présenté différents champs de haricots fournis par Spring et leurs utilisations prévues.

La mise en œuvre de ce didacticiel est disponible à l’adresse the github project . Il s’agit d’un projet basé sur Eclipse. Il devrait donc être facile à importer et à exécuter tel quel.