Spring Bean Scopes stellen ons in staat om meer gedetailleerde controle te hebben over het aanmaken van bean-instanties. Soms willen we bijvoorbeeld een bean-instantie als singleton creëren, maar in andere gevallen willen we dat deze bij elke aanvraag wordt gemaakt of slechts één keer in een sessie.
Spring Bean Scopes
Er zijn vijf soorten Spring bean-scopes:
- singleton – er wordt slechts één instantie van de Spring bean gemaakt voor de Spring-container. Dit is de standaard Spring bean-scope. Bij het gebruik van deze scope moet ervoor worden gezorgd dat de bean geen gedeelde instantievariabelen heeft, anders kunnen er problemen met gegevensconsistentie ontstaan.
- prototype – Er wordt elke keer een nieuwe instantie gemaakt wanneer de bean wordt opgevraagd uit de Spring-container.
- request – Dit is hetzelfde als de prototype-scope, maar het is bedoeld voor webapplicaties. Voor elke HTTP-aanvraag wordt een nieuwe instantie van de bean gemaakt.
- session – Door de container wordt voor elke HTTP-sessie een nieuwe bean gemaakt.
- global-session – Dit wordt gebruikt om wereldwijde sessiebeans te maken voor Portlet-toepassingen.
Spring Bean Singleton en Prototype Scope
Spring bean singleton- en prototypescopes kunnen worden gebruikt in zelfstandige Spring-apps. Laten we eens kijken hoe we deze scopes gemakkelijk kunnen configureren met behulp van de @Scope
-annotatie. Laten we zeggen dat we een Java bean-klasse hebben.
package com.journaldev.spring;
public class MyBean {
public MyBean() {
System.out.println("MyBean instance created");
}
}
Laten we de Spring-configuratie klasse definiëren waar we de methode zullen definiëren om een instantie van MyBean uit de Spring-container te halen.
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();
}
}
Merk op dat singleton
de standaardscope is, dus we kunnen @Scope(value="singleton")
verwijderen uit de bovenstaande beandefinitie. Laten we nu een hoofdmethoden maken en de singleton-scope testen.
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();
}
}
Wanneer het bovenstaande programma wordt uitgevoerd, krijgen we de volgende uitvoer.
MyBean instance created
867988177
867988177
Merk op dat beide instanties van MyBean hetzelfde hashcode hebben en de constructor slechts één keer wordt aangeroepen, dit betekent dat de Spring-container altijd dezelfde instantie van MyBean retourneert. Laten we nu de scope veranderen naar prototype
.
@Bean
@Scope(value="prototype")
public MyBean myBean() {
return new MyBean();
}
Deze keer krijgen we de volgende uitvoer wanneer de hoofdmethoden worden uitgevoerd.
MyBean instance created
867988177
MyBean instance created
443934570
Het is duidelijk dat een instantie van MyBean telkens wordt gemaakt wanneer deze wordt opgevraagd uit de Spring-container. Laten we nu de scope veranderen naar request
.
@Bean
@Scope(value="request")
public MyBean myBean() {
return new MyBean();
}
In dit geval krijgen we de volgende uitzondering.
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)
Het komt doordat de scopes request
, session
en global-session
niet beschikbaar zijn voor zelfstandige toepassingen.
Spring Bean Request en Session Scope
Voor het voorbeeld van de spring bean request- en sessiescope zullen we een Spring Boot-webtoepassing maken. Maak een spring boot starterproject aan en kies “web” zodat we het kunnen uitvoeren als een webtoepassing. Ons uiteindelijke project zal eruitzien als de onderstaande afbeelding.
ServletInitializer
en SpringBootMvcApplication
zijn automatisch gegenereerde spring boot-klassen. We hoeven daar geen wijzigingen aan te brengen. Hier is mijn pom.xml-bestand, bekijk de afhankelijkheden voor onze toepassing. Jouw pom.xml-bestand kan iets verschillen op basis van de Eclipse-versie die je gebruikt.
<?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>
Laten we wat spring-componenten maken en ze configureren als spring beans in de spring-container met de scope request
en session
.
Spring Bean Request Scope
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;
}
}
Lentebon Sessie 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;
}
}
Lentecomponent
Lat we een lente-component maken en lente gebruiken om bovenstaande bonnen automatisch te configureren.
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;
}
}
Lente Restcontroller
Tenslotte maken we een RestController-klasse en configureren we enkele API-eindpunten voor onze testdoeleinden.
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();
}
}
Lente Boot Sessie Time-out Configuratie
Tenslotte moeten we de variabelen voor time-out van de lentesessie van de lente-boot configureren, voeg onderstaande eigenschappen toe in src/main/resources/application.properties
.
server.session.cookie.max-age= 1
server.session.timeout= 1
Nu worden onze lente-bonen met sessiescope ongeldig gemaakt binnen één minuut. Voer gewoon de klasse SpringBootMvcApplication
uit als een lente-boottoepassing. U zou onderstaande uitvoer moeten zien voor onze geconfigureerde eindpunten.
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()
Lente Bean Aanvraag Scope Test
Open een willekeurige browser en ga naar de URL https://localhost:8080/nameRS
en controleer de console-uitvoer. Je zou DataRequestScope Constructor Called
op elke aanvraag moeten zien verschijnen.
Lente Bean Sessie Scope Test
Ga naar https://localhost:8080/nameSS
en je zou de volgende output krijgen. Ga nu naar
https://localhost:8080/nameSSUpdated
zodat de naamwaarde van DataSessionScope
wordt bijgewerkt naar Session Scope Updated
. Ga nu opnieuw naar
https://localhost:8080/nameSS
en je zou de bijgewerkte waarde moeten zien. Tegen die tijd zou je
DataSessionScope Constructor Called at XXX
slechts één keer in de console-uitvoer moeten zien. Wacht nu 1 minuut zodat onze sessie-gekopelde bean ongeldig wordt. Ga vervolgens opnieuw naar https://localhost:8080/nameSS
en je zou de oorspronkelijke output moeten zien. Controleer ook het consolebericht voor het opnieuw maken van DataSessionScope door de container. Dat is alles voor de Spring Beans Scope-tutorial.
Je kunt het Spring Beans Scope Spring Boot-project downloaden vanuit onze GitHub Repository.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-scopes