Spring Bean Scopes ermöglichen es uns, eine genauere Kontrolle über die Erstellung von Bean-Instanzen zu haben. Manchmal möchten wir eine Bean-Instanz als Singleton erstellen, in anderen Fällen möchten wir jedoch, dass sie bei jeder Anfrage oder einmal in einer Sitzung erstellt wird.
Spring Bean Scopes
Es gibt fünf Arten von Spring Bean Scopes:
- singleton – es wird nur eine Instanz des Spring Beans für den Spring Container erstellt. Dies ist der Standard Spring Bean Scope. Bei Verwendung dieses Scopes stellen Sie sicher, dass der Bean keine gemeinsamen Instanzvariablen hat, da dies zu Dateninkonsistenzproblemen führen könnte.
- prototype – Jedes Mal, wenn der Bean vom Spring Container angefordert wird, wird eine neue Instanz erstellt.
- request – Dies entspricht dem Prototype Scope, ist jedoch für Webanwendungen gedacht. Für jede HTTP-Anfrage wird eine neue Instanz des Beans erstellt.
- session – Für jede HTTP-Sitzung wird vom Container ein neuer Bean erstellt.
- global-session – Dies wird verwendet, um globale Session Beans für Portlet-Anwendungen zu erstellen.
Spring Bean Singleton und Prototype Scope
Spring-Bean-Singleton- und Prototype-Scopes können in eigenständigen Spring-Anwendungen verwendet werden. Schauen wir uns an, wie wir diese Scopes mithilfe der @Scope
-Annotation einfach konfigurieren können. Angenommen, wir haben eine Java-Bean-Klasse.
package com.journaldev.spring;
public class MyBean {
public MyBean() {
System.out.println("MyBean instance created");
}
}
Definieren wir die Spring-Konfigurationsklasse, in der wir die Methode zur Abrufung der MyBean-Instanz aus dem Spring-Container definieren.
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();
}
}
Beachten Sie, dass singleton
der Standardbereich ist. Daher können wir die @Scope(value="singleton")
-Annotation aus der obigen Bean-Definition entfernen. Erstellen wir nun eine Hauptmethode und testen den Singleton-Scope.
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();
}
}
Wenn das obige Programm ausgeführt wird, erhalten wir eine Ausgabe wie unten dargestellt.
MyBean instance created
867988177
867988177
Beachten Sie, dass beide MyBean-Instanzen denselben Hashcode haben und der Konstruktor nur einmal aufgerufen wird. Dies bedeutet, dass der Spring-Container immer dieselbe Instanz von MyBean zurückgibt. Ändern wir nun den Scope auf prototype
.
@Bean
@Scope(value="prototype")
public MyBean myBean() {
return new MyBean();
}
Diesmal erhalten wir folgende Ausgabe, wenn die Hauptmethode ausgeführt wird.
MyBean instance created
867988177
MyBean instance created
443934570
Es ist klar, dass die MyBean-Instanz jedes Mal erstellt wird, wenn sie vom Spring-Container angefordert wird. Ändern wir nun den Scope auf request
.
@Bean
@Scope(value="request")
public MyBean myBean() {
return new MyBean();
}
In diesem Fall erhalten wir folgende Ausnahme.
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)
Es liegt daran, dass die Umfänge request
, session
und global-session
für eigenständige Anwendungen nicht verfügbar sind.
Anfrage- und Sitzungsbereich des Spring Beans
Für das Beispiel zum Anfrage- und Sitzungsbereich des Spring Beans werden wir eine Spring Boot-Webanwendung erstellen. Erstellen Sie ein Spring Boot-Starterprojekt und wählen Sie „Web“, damit wir es als Webanwendung ausführen können. Unser finales Projekt wird wie im untenstehenden Bild aussehen.
ServletInitializer
und SpringBootMvcApplication
sind automatisch generierte Spring-Boot-Klassen. Dort müssen wir keine Änderungen vornehmen. Hier ist meine pom.xml-Datei. Werfen Sie einen Blick auf die Abhängigkeiten für unsere Anwendung. Ihre pom.xml-Datei könnte je nach der von Ihnen verwendeten Eclipse-Version leicht unterschiedlich sein.
<?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>
Lassen Sie uns einige Spring-Komponenten erstellen und sie als Spring Beans im Spring-Container mit den Umfängen request
und session
konfigurieren.
Spring Bean Anfragebereich
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;
}
}
Spring Bean Session Scope
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;
}
}
Spring Komponente
Erstellen Sie jetzt eine Spring-Komponente und verwenden Sie Spring, um die oben genannten Beans automatisch zu konfigurieren.
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;
}
}
Spring Rest Controller
Zuletzt erstellen wir eine RestController-Klasse und konfigurieren einige API-Endpunkte zu Testzwecken.
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();
}
}
Spring Boot Session Timeout-Konfiguration
Zum Schluss müssen wir die Variablen für die Spring Boot Session Timeout-Konfiguration hinzufügen. Fügen Sie die folgenden Eigenschaften in die Datei src/main/resources/application.properties
ein.
server.session.cookie.max-age= 1
server.session.timeout= 1
Jetzt werden unsere Spring-Beans mit Session-Scope nach einer Minute ungültig. Führen Sie einfach die Klasse SpringBootMvcApplication
als Spring-Boot-Anwendung aus. Sie sollten die unten stehende Ausgabe für unsere konfigurierten Endpunkte sehen.
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()
Spring Bean Anfragebereich Test
Öffnen Sie einen Browser und gehen Sie zur URL https://localhost:8080/nameRS
und überprüfen Sie die Konsolenausgabe. Sie sollten sehen, dass DataRequestScope Constructor Called
bei jeder Anfrage gedruckt wird.
Spring Bean Sessionbereich Test
Gehe zu https://localhost:8080/nameSS
und du erhältst folgende Ausgabe. Gehe nun zu
https://localhost:8080/nameSSUpdated
, damit der Wert des Namens von DataSessionScope
auf Session Scope Updated
aktualisiert wird. Gehe nun erneut zu
https://localhost:8080/nameSS
und du solltest den aktualisierten Wert sehen. Zu diesem Zeitpunkt solltest du nur einmal die Meldung
DataSessionScope Constructor Called at XXX
in der Konsolenausgabe sehen. Warte nun 1 Minute, damit unser sessionsübergreifendes Bean ungültig wird. Gehe dann erneut zu https://localhost:8080/nameSS
und du solltest die ursprüngliche Ausgabe sehen. Überprüfe auch die Konsolenmeldung zur Erstellung von DataSessionScope erneut durch den Container. Das ist alles für das Tutorial zu Spring-Bean-Scope.
Du kannst das Spring Beans Scope Spring Boot-Projekt von unserem GitHub-Repository herunterladen.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-scopes