Kurzanleitung zu Spring Bean Scopes

Kurzanleitung für Spring Bean Scopes

1. Überblick

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

Der Umfang einer Bean definiert den Lebenszyklus und die Sichtbarkeit dieser Bean in den Kontexten, in denen sie verwendet wird.

Die neueste Version von Spring Framework definiert 6 Arten von Bereichen:

  • Singleton

  • Prototyp

  • Anfrage

  • Session

  • Anwendung

  • Websocket

Die letzten vier genannten Bereicherequest, session, application undwebsocket ind nur in einer webfähigen Anwendung verfügbar.

Weitere Lektüre:

Was ist eine Frühlingsbohne?

Eine schnelle und praktische Erklärung, was ein Spring Bean ist.

Read more

Spring Bean-Anmerkungen

Erfahren Sie, wie und wann Sie die Standardanmerkungen für Spring Beans verwenden müssen - @Component, @Repository, @Service und @Controller.

Read more

2. Singleton Scope

Das Definieren einer Bean mit dem Gültigkeitsbereich vonsingletonbedeutet, dass der Container eine einzelne Instanz dieser Bean erstellt und alle Anforderungen für diesen Bean-Namen dasselbe Objekt zurückgeben, das zwischengespeichert wird. Änderungen am Objekt werden in allen Verweisen auf die Bean berücksichtigt. Dieser Bereich ist der Standardwert, wenn kein anderer Bereich angegeben ist.

Erstellen wir einePerson-Entität, um das Konzept der Bereiche zu veranschaulichen:

public class Person {
    private String name;

    // standard constructor, getters and setters
}

Anschließend definieren wir die Bean mit dem Gültigkeitsbereich vonsingletonmithilfe der Annotation@Scope:

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

Wir können auch eine Konstante anstelle desString-Wertes folgendermaßen verwenden:

@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)

Nun schreiben wir einen Test, der zeigt, dass zwei Objekte, die auf dasselbe Bean verweisen, dieselben Werte haben, auch wenn nur eines von ihnen seinen Status ändert, da beide auf dieselbe Bean-Instanz verweisen:

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();
}

Diescopes.xml-Datei in diesem Beispiel sollte die XML-Definitionen der verwendeten Beans enthalten:




    

3. Prototyp-Umfang

Eine Bean mit dem Gültigkeitsbereich vonprototypegibt jedes Mal eine andere Instanz zurück, wenn sie vom Container angefordert wird. Es wird definiert, indem der Wertprototype auf die Annotation@Scope in der Bean-Definition gesetzt wird:

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

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

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Wir werden jetzt einen ähnlichen Test wie zuvor schreiben, der zeigt, dass zwei Objekte, die denselben Bean-Namen mit Gültigkeitsbereichsprototyp anfordern, unterschiedliche Zustände haben, da sie nicht mehr auf dieselbe Bean-Instanz verweisen:

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();
}

Die Dateiscopes.xmlähnelt der im vorherigen Abschnitt beschriebenen Datei, während die XML-Definition für die Bean mit dem Gültigkeitsbereich vonprototypehinzugefügt wird:

4. Web Aware Scopes

Wie bereits erwähnt, gibt es vier zusätzliche Bereiche, die nur in einem webfähigen Anwendungskontext verfügbar sind. Diese werden in der Praxis seltener eingesetzt.

Der Bereichrequesterstellt eine Bean-Instanz für eine einzelne HTTP-Anforderung, während der Bereich sessionfür eine HTTP-Sitzung erstellt wird.

Dasapplication scope erstellt die Bean-Instanz für den Lebenszyklus aServletContext und daswebsocket scope erstellt sie für eine bestimmteWebSocket -Session.

Erstellen wir eine Klasse zum Instanziieren der Beans:

public class HelloMessageGenerator {
    private String message;

    // standard getter and setter
}

4.1. Umfang anfordern

Wir können die Bean mit dem Gültigkeitsbereich vonrequestmithilfe der Annotation@Scopedefinieren:

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

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

Als nächstes können wir einen Controller definieren, der eine injizierte Referenz aufrequestScopedBean hat. Wir müssen zweimal auf dieselbe Anfrage zugreifen, um die webspezifischen Bereiche zu testen.

Wenn wir diemessage jedes Mal anzeigen, wenn die Anforderung ausgeführt wird, können wir sehen, dass der Wert aufnull zurückgesetzt wird, obwohl er später in der Methode geändert wird. Dies liegt daran, dass für jede Anforderung eine andere Bean-Instanz zurückgegeben wird.

@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. Sitzungsumfang

Wir können die Bean mit dem Gültigkeitsbereich vonsessionauf ähnliche Weise definieren:

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

Als nächstes definieren wir einen Controller mit einem Verweis aufsessionScopedBean. Auch hier müssen zwei Anforderungen ausgeführt werden, um zu zeigen, dass der Wert des Feldsmessagefür die Sitzung gleich ist.

In diesem Fall, wenn die Anforderung zum ersten Mal gestellt wird, ist der Wertmessagenull. . Wenn er jedoch einmal geändert wird, wird dieser Wert  für nachfolgende Anforderungen als dieselbe Instanz von beibehalten Die Bean wird für die gesamte Sitzung zurückgegeben.

@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. Anwendungsbereich

Dasapplication scope erstellt die Bean-Instanz für den Lebenszyklus vonServletContext.

Dies ähnelt dem Singleton-Gültigkeitsbereich, es gibt jedoch einen sehr wichtigen Unterschied im Hinblick auf den Gültigkeitsbereich der Bean.

Wenn Beans einen Gültigkeitsbereich vonapplicationhaben, wird dieselbe Instanz des Beans von mehreren servletbasierten Anwendungen gemeinsam genutzt, die in denselbenServletContext ausgeführt werden, während Beans mit Singleton-Gültigkeitsbereich nur für einen einzelnen Anwendungskontext gelten.

Erstellen wir die Bean mit dem Gültigkeitsbereich vonapplication:

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

Und der Controller, der auf diese Bean verweist:

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

In diesem Fall wird der Wertmessage, der einmal inapplicationScopedBean festgelegt wurde, für alle nachfolgenden Anforderungen, Sitzungen und sogar für eine andere Servlet-Anwendung beibehalten, die auf diese Bean zugreift, vorausgesetzt, sie wird in demselbenServletContext.ausgeführt ) s

4.4. WebSocket-Bereich

Zuletzt erstellen wir die Bean mitwebsocket scope:

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

Beans mit WebSocket-Gültigkeitsbereich werden beim ersten Zugriff in den Sitzungsattributen vonWebSocketgespeichert. Dieselbe Instanz der Bean wird dann zurückgegeben, wenn während der gesamten Sitzung vonWebSocketauf diese Bean zugegriffen wird.

Wir können auch sagen, dass es Singleton-Verhalten zeigt, aber nur auf eineWebSocket-Sitzung beschränkt ist.

5. Fazit

Wir haben verschiedene von Spring bereitgestellte Bohnenbereiche und deren Verwendungszweck demonstriert.

Die Implementierung dieses Tutorials finden Sie inthe GitHub project - dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.