Поиск по сайту:

Спринг Бин Сферы


Spring Bean Scopes позволяет нам более детально контролировать создание экземпляров bean-компонентов. Иногда мы хотим создать экземпляр компонента как синглтон, но в некоторых других случаях мы можем захотеть, чтобы он создавался при каждом запросе или один раз в сеансе.

Спринг Бин Сферы

Существует пять типов прицелов Spring Bean:

  1. singleton — для контейнера Spring будет создан только один экземпляр Spring Bean. Это область действия Spring bean по умолчанию. При использовании этой области убедитесь, что у bean-компонента нет общих переменных экземпляра, иначе это может привести к проблемам с несогласованностью данных.
  2. prototype — новый экземпляр будет создаваться каждый раз, когда bean-компонент запрашивается из контейнера Spring.
  3. запрос — это то же самое, что и область действия прототипа, однако она предназначена для использования в веб-приложениях. Для каждого HTTP-запроса будет создаваться новый экземпляр компонента.
  4. session — новый компонент будет создаваться контейнером для каждого HTTP-сеанса.
  5. global-session — используется для создания глобальных сеансовых компонентов для приложений с портлетами.

Spring Bean Singleton и Prototype Scope

Одноэлементные и прототипные области Spring bean можно использовать в автономных приложениях Spring. Давайте посмотрим, как мы можем легко настроить эти области с помощью аннотации @Scope. Допустим, у нас есть класс java bean.

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) из приведенного выше определения bean-компонента. Теперь давайте создадим основной метод и проверим область действия синглтона.

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 и область сеанса

<?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

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 для автоматической настройки вышеупомянутых bean-компонентов.

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


}

Контроллер пружинного отдыха

Наконец, давайте создадим класс 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

Наконец, нам нужно настроить переменные времени ожидания сеанса весенней загрузки, добавить свойства ниже в src/main/resources/application.properties.

server.session.cookie.max-age= 1
server.session.timeout= 1

Теперь наши spring bean-компоненты с областью действия сеанса будут аннулированы через одну минуту. Просто запустите класс SpringBootMvcApplication в качестве приложения весенней загрузки. Ниже вы должны увидеть выходные данные для наших конечных точек, которые настраиваются.

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

Откройте любой браузер и перейдите по URL-адресу https://localhost:8080/nameRS и проверьте вывод консоли. Вы должны увидеть, что DataRequestScope Constructor Called печатается при каждом запросе.

Тест области действия сеанса Spring Bean

Вы можете загрузить проект Spring Boot Spring Beans Scope из нашего репозитория GitHub.