Виды областей видимости Spring Bean позволяют более детально управлять созданием экземпляров бина. Иногда мы хотим создавать экземпляр бина как синглтон, но в некоторых случаях мы можем захотеть, чтобы он создавался при каждом запросе или один раз за сессию.
Виды областей видимости Spring Bean
Существует пять типов областей видимости spring bean:
- singleton – будет создан только один экземпляр spring bean для контейнера Spring. Это область видимости spring bean по умолчанию. При использовании этой области видимости убедитесь, что у бина нет общих переменных экземпляра, иначе это может привести к проблемам с несогласованностью данных.
- prototype – Новый экземпляр будет создаваться каждый раз, когда бин запрашивается из контейнера Spring.
- request – Это то же самое, что и область видимости prototype, однако ее предназначено для использования в веб-приложениях. Новый экземпляр бина будет создаваться для каждого HTTP-запроса.
- session – Для каждой HTTP-сессии контейнер создаст новый бин.
- global-session – Используется для создания глобальных сессионных бинов для портлет-приложений.
Одиночка и Прототип области Spring Bean
Одиночка и прототипные области Spring bean могут использоваться в автономных приложениях Spring. Давайте посмотрим, как легко настроить эти области, используя аннотацию @Scope
. Предположим, у нас есть класс бина на языке Java.
package com.journaldev.spring;
public class MyBean {
public MyBean() {
System.out.println("MyBean instance created");
}
}
Определим класс конфигурации Spring, где мы определим метод для получения экземпляра MyBean из контейнера Spring.
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();
}
}
Обратите внимание, что singleton
– область по умолчанию, поэтому мы можем удалить @Scope(value="singleton")
из определения бина выше. Теперь давайте создадим основной метод и протестируем область одиночки.
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();
}
}
Когда программа выше выполняется, мы получим вывод, как показано ниже.
MyBean instance created
867988177
867988177
Обратите внимание, что оба экземпляра MyBean имеют одинаковый хеш-код, и конструктор вызывается только один раз, что означает, что контейнер Spring всегда возвращает один и тот же экземпляр MyBean. Теперь давайте изменим область на prototype
.
@Bean
@Scope(value="prototype")
public MyBean myBean() {
return new MyBean();
}
На этот раз мы получим следующий вывод при выполнении основного метода.
MyBean instance created
867988177
MyBean instance created
443934570
Ясно, что экземпляр MyBean создается каждый раз, когда он запрашивается из контейнера Spring. Теперь давайте изменить область на request
.
@Bean
@Scope(value="request")
public MyBean myBean() {
return new MyBean();
}
В этом случае мы получим следующее исключение.
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)
Это потому что области request
, session
и global-session
недоступны для автономных приложений.
Диапазоны запроса и сеанса Spring Bean
Для примера диапазона запроса и сеанса Spring Bean мы создадим веб-приложение Spring Boot. Создайте стартовый проект Spring Boot и выберите “web”, чтобы мы могли запустить его как веб-приложение. Наш конечный проект будет выглядеть примерно как на изображении ниже.
ServletInitializer
и SpringBootMvcApplication
– это автоматически созданные классы Spring Boot. Нам не нужно вносить изменения там. Вот мой файл pom.xml, посмотрите на зависимости для нашего приложения. Ваш файл pom.xml может немного отличаться в зависимости от версии Eclipse, которую вы используете.
<?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>
Давайте создадим несколько компонентов Spring и настроим их как spring-бины в контейнере Spring с областью request
и session
.
Диапазон запроса Spring Bean
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 Компонент
Теперь давайте создадим компонент Spring и используем Spring для автоматической конфигурации вышеуказанных бинов.
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
Наконец, давайте создадим класс RestController и настроим некоторые конечные точки API для наших тестовых целей.
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
Наконец, нам нужно настроить переменные таймаута сеанса Spring Boot, добавив следующие свойства в src/main/resources/application.properties
.
server.session.cookie.max-age= 1
server.session.timeout= 1
Теперь наши весенние бобы с областью сеанса будут аннулированы через одну минуту. Просто запустите класс SpringBootMvcApplication
как приложение Spring Boot. Вы должны увидеть нижеуказанный вывод для наших настроенных конечных точек.
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
Откройте любой браузер и перейдите по адресу https://localhost:8080/nameRS
, затем проверьте вывод в консоли. Вы должны увидеть, что DataRequestScope Constructor Called
печатается при каждом запросе.
Тест области сессии Spring Bean
Перейдите по ссылке https://localhost:8080/nameSS
, и вы получите следующий вывод. Теперь перейдите по ссылке
https://localhost:8080/nameSSUpdated
, чтобы значение имени DataSessionScope
обновилось на Session Scope Updated
. Теперь снова перейдите по ссылке
https://localhost:8080/nameSS
, и вы должны увидеть обновленное значение. К этому моменту в консоли должно быть видно только одно сообщение
DataSessionScope Constructor Called at XXX
. Теперь подождите 1 минуту, чтобы наш бин с областью видимости сеанса был аннулирован. Затем снова перейдите по ссылке https://localhost:8080/nameSS
, и вы должны увидеть первоначальный вывод. Также следует проверить консольное сообщение о создании DataSessionScope снова контейнером. Вот и все для учебного пособия по областям видимости бинов Spring.
Вы можете скачать проект Spring Boot Spring Beans Scope из нашего репозитория GitHub.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-scopes