Articles

Quick Guide to Spring Bean Scopes

Posted on

Übersicht

In diesem kurzen Tutorial lernen wir die verschiedenen Arten von Bean Scopes im Spring Framework kennen.

Der Scope einer Bean definiert den Lebenszyklus und die Sichtbarkeit dieser Bean in den Kontexten, in denen wir sie verwenden.

Die aktuelle Version des Spring-Frameworks definiert 6 Arten von Scopes:

  • singleton
  • prototype
  • request
  • session
  • application
  • websocket

Die letzten vier genannten Scopes, request, session, application und websocket, sind nur in einer web-aware Anwendung verfügbar.

Weitere Lektüre:

Was ist eine Spring Bean?

Eine schnelle und praktische Erklärung, was eine Spring Bean ist.
Lesen Sie mehr →

Spring Bean Annotations

Lernen Sie, wie und wann Sie die Standard-Annotationen für Spring Beans verwenden – @Component, @Repository, @Service und @Controller.
Lesen Sie mehr →

Singleton Scope

Wenn wir eine Bean mit dem Singleton Scope definieren, erzeugt der Container eine einzige Instanz dieser Bean; alle Anfragen für diesen Bean-Namen geben dasselbe Objekt zurück, das zwischengespeichert wird. Alle Änderungen an dem Objekt werden in allen Verweisen auf die Bean widergespiegelt. Dieser Bereich ist der Standardwert, wenn kein anderer Bereich angegeben wird.

Lassen Sie uns eine Person-Entität erstellen, um das Konzept der Scopes zu veranschaulichen:

public class Person { private String name; // standard constructor, getters and setters}

Danach definieren wir die Bean mit dem Singleton-Scope, indem wir die @Scope-Annotation verwenden:

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

Wir können auch eine Konstante anstelle des String-Wertes auf folgende Weise verwenden:

@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)

Nun können wir fortfahren, einen Test zu schreiben, der zeigt, dass zwei Objekte, die auf dieselbe Bean verweisen, dieselben Werte haben, auch wenn nur eines von ihnen seinen Zustand ändert, da sie beide dieselbe Bean-Instanz referenzieren:

private static final String NAME = "John Smith";@Testpublic 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();}

Die scopes.xml-Datei sollte in diesem Beispiel die xml-Definitionen der verwendeten Beans enthalten:

<?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 class="org.baeldung.scopes.Person" scope="singleton"/> </beans>

Prototyp-Scope

Eine Bean mit dem Prototyp-Scope gibt jedes Mal eine andere Instanz zurück, wenn sie vom Container angefordert wird. Er wird definiert, indem man den Wert prototype auf die @Scope-Annotation in der Bean-Definition setzt:

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

Wir können auch eine Konstante verwenden, wie wir es für den Singleton-Scope getan haben:

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Wir werden nun einen ähnlichen Test wie zuvor schreiben, der zwei Objekte zeigt, die denselben Bean-Namen mit dem Prototype-Scope anfordern. Sie werden unterschiedliche Zustände haben, da sie sich nicht mehr auf dieselbe Bean-Instanz beziehen:

private static final String NAME = "John Smith";private static final String NAME_OTHER = "Anna Jones";@Testpublic 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();}

Die scopes.xml-Datei ähnelt der im vorherigen Abschnitt vorgestellten, wobei die xml-Definition für die Bean mit dem Prototyp-Scope hinzugefügt wird:

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

Web-Aware-Scopes

Wie bereits erwähnt, gibt es vier zusätzliche Scopes, die nur in einem Web-Aware-Anwendungskontext verfügbar sind. Wir verwenden diese in der Praxis seltener.

Der Request-Scope erzeugt eine Bean-Instanz für eine einzelne HTTP-Anfrage, während der Session-Scope eine Bean-Instanz für eine HTTP-Session erzeugt.

Der Application-Scope erzeugt die Bean-Instanz für den Lebenszyklus eines ServletContextes, und der Websocket-Scope erzeugt sie für eine bestimmte WebSocket-Sitzung.

Lassen Sie uns eine Klasse erstellen, die für die Instanzierung der Beans verwendet wird:

public class HelloMessageGenerator { private String message; // standard getter and setter}

4.1. Request Scope

Mit der @Scope-Annotation können wir die Bean mit dem Request Scope definieren:

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

Das proxyMode-Attribut ist notwendig, da zum Zeitpunkt der Instanziierung des Webanwendungskontextes keine aktive Anfrage vorliegt. Spring erstellt einen Proxy, der als Abhängigkeit injiziert wird, und instanziiert die Ziel-Bean, wenn sie in einer Anfrage benötigt wird.

Wir können auch eine @RequestScope zusammengesetzte Annotation verwenden, die als Abkürzung für die obige Definition dient:

@Bean@RequestScopepublic HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator();}

Nächste können wir einen Controller definieren, der eine injizierte Referenz auf die requestScopedBean hat. Wir müssen zweimal auf dieselbe Anfrage zugreifen, um die web-spezifischen Scopes zu testen.

Wenn wir die Meldung jedes Mal anzeigen, wenn die Anfrage ausgeführt wird, können wir sehen, dass der Wert auf null zurückgesetzt wird, auch wenn er später in der Methode geändert wird. Das liegt daran, dass für jede Anfrage eine andere Bean-Instanz zurückgegeben wird.

@Controllerpublic 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. Session-Scope

Wir können die Bean mit dem Session-Scope auf ähnliche Weise definieren:

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

Es gibt auch eine dedizierte zusammengesetzte Annotation, die wir verwenden können, um die Bean-Definition zu vereinfachen:

@Bean@SessionScopepublic HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator();}

Als Nächstes definieren wir einen Controller mit einer Referenz auf die SessionScopedBean. Auch hier müssen wir zwei Anfragen ausführen, um zu zeigen, dass der Wert des Nachrichtenfeldes für die Sitzung gleich ist.

In diesem Fall ist der Wert message beim ersten Request null. Sobald er jedoch geändert wird, wird dieser Wert für nachfolgende Anfragen beibehalten, da dieselbe Instanz der Bean für die gesamte Sitzung zurückgegeben wird.

@Controllerpublic 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. Application Scope

Der Application Scope legt die Bean-Instanz für den Lebenszyklus eines ServletContextes an.

Dies ist ähnlich wie der Singleton Scope, aber es gibt einen sehr wichtigen Unterschied in Bezug auf den Scope der Bean.

Wenn Beans Application Scoped sind, wird dieselbe Instanz der Bean von mehreren Servlet-basierten Anwendungen, die im selben ServletContext laufen, gemeinsam genutzt, während Singleton Scoped Beans nur auf einen einzigen Anwendungskontext skaliert sind.

Lassen Sie uns die Bean mit dem Application-Scope erstellen:

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

Analog zu den Request- und Session-Scopes können wir eine kürzere Version verwenden:

@Bean@ApplicationScopepublic HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator();}

Nun lassen Sie uns einen Controller erstellen, der diese Bean referenziert:

@Controllerpublic 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"; }}

In diesem Fall wird die Wertnachricht, sobald sie in der applicationScopedBean gesetzt wurde, für alle nachfolgenden Anfragen, Sitzungen und sogar für verschiedene Servlet-Anwendungen, die auf diese Bean zugreifen, beibehalten, vorausgesetzt, sie läuft im gleichen ServletContext.

4.4. WebSocket-Scope

Zuletzt wollen wir die Bean mit dem Websocket-Scope erstellen:

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

Beans mit WebSocket-Scope werden beim ersten Zugriff in den WebSocket-Session-Attributen gespeichert. Die gleiche Instanz der Bean wird dann immer zurückgegeben, wenn während der gesamten WebSocket-Sitzung auf diese Bean zugegriffen wird.

Man kann auch sagen, dass sie ein Singleton-Verhalten aufweist, das aber nur auf eine WebSocket-Sitzung beschränkt ist.

Abschluss

In diesem Artikel haben wir die verschiedenen Bean-Scopes, die Spring zur Verfügung stellt, besprochen und deren Verwendungszweck erläutert.

Die Implementierung dieses Artikels ist im GitHub-Projekt zu finden.

Starten Sie mit Spring 5 und Spring Boot 2, durch den Learn Spring Kurs:

>> DER KURS

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.