봄 빈 범위를 사용하면 빈 인스턴스 생성을 더 세밀하게 제어할 수 있습니다. 때로는 빈 인스턴스를 싱글톤으로 생성하고 싶을 수 있지만 다른 경우에는 각 요청마다 만들거나 세션 중에 한 번만 만들기를 원할 수 있습니다.
봄 빈 범위
다섯 가지 종류의 봄 빈 범위가 있습니다:
- singleton – 봄 컨테이너에 대한 봄 빈의 인스턴스는 하나만 생성됩니다. 이것이 기본 봄 빈 범위입니다. 이 범위를 사용할 때 빈이 공유 인스턴스 변수를 가지지 않도록하십시오. 그렇지 않으면 데이터 불일치 문제가 발생할 수 있습니다.
- prototype – 빈이 봄 컨테이너에서 요청될 때마다 새 인스턴스가 생성됩니다.
- request – 이것은 프로토타입 범위와 동일하지만 웹 응용 프로그램에 사용됩니다. 빈의 새 인스턴스가 각 HTTP 요청에 대해 생성됩니다.
- session – 컨테이너에 의해 각 HTTP 세션에 대해 새로운 빈이 생성됩니다.
- global-session – 이것은 Portlet 응용 프로그램에 대한 전역 세션 빈을 생성하는 데 사용됩니다.
스프링 빈 싱글톤과 프로토타입 스코프
스프링 빈의 싱글톤과 프로토타입 스코프는 독립적인 스프링 앱에서 사용할 수 있습니다. @Scope
주석을 사용하여 이러한 스코프를 쉽게 구성하는 방법을 살펴보겠습니다. 예를 들어 Java 빈 클래스가 있다고 가정해 봅시다.
package com.journaldev.spring;
public class MyBean {
public MyBean() {
System.out.println("MyBean instance created");
}
}
스프링 컨테이너에서 MyBean 인스턴스를 가져오는 메서드를 정의하는 스프링 구성 클래스를 정의합시다.
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 인스턴스가 동일한 해시 코드를 가지고 있고 생성자가 한 번만 호출되는 것을 알 수 있습니다. 이는 스프링 컨테이너가 항상 동일한 MyBean 인스턴스를 반환한다는 것을 의미합니다. 이제 스코프를 prototype
으로 변경해 봅시다.
@Bean
@Scope(value="prototype")
public MyBean myBean() {
return new MyBean();
}
이번에는 메인 메서드가 실행될 때 다음과 같은 출력을 얻을 것입니다.
MyBean instance created
867988177
MyBean instance created
443934570
MyBean 인스턴스가 스프링 컨테이너에서 요청될 때마다 생성된다는 것을 명확히 알 수 있습니다. 이제 스코프를 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
스코프가 독립형 애플리케이션에는 사용할 수 없기 때문입니다.
스프링 빈 요청 및 세션 스코프
스프링 빈 요청 및 세션 스코프 예제를 위해 스프링 부트 웹 애플리케이션을 만들겠습니다. 스프링 부트 스타터 프로젝트를 만들고 “web”을 선택하여 웹 애플리케이션으로 실행할 수 있도록합니다. 최종 프로젝트는 아래 이미지와 같이 보일 것입니다.
ServletInitializer
및 SpringBootMvcApplication
은 자동 생성된 스프링 부트 클래스입니다. 거기에는 어떤 변경도 필요하지 않습니다. 여기에 제 pom.xml 파일이 있습니다. 애플리케이션의 종속성을 살펴보세요. 사용 중인 Eclipse 버전에 따라 pom.xml 파일이 약간 다를 수 있습니다.
<?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>
몇 가지 스프링 컴포넌트를 만들고 스프링 컨테이너에서 스코프를 request
및 session
으로 구성합니다.
스프링 빈 요청 스코프
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;
}
}
스프링 빈 세션 스코프
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;
}
}
스프링 컴포넌트
이제 스프링 컴포넌트를 생성하고 스프링을 사용하여 위의 빈을 자동으로 구성해 봅시다.
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;
}
}
스프링 레스트 컨트롤러
마지막으로, 테스트 목적으로 몇 가지 API 엔드 포인트를 구성하기 위해 RestController 클래스를 생성합시다.
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();
}
}
스프링 부트 세션 타임아웃 구성
마지막으로, 스프링 부트 세션 타임아웃 변수를 구성해야 합니다. src/main/resources/application.properties
에 아래 속성을 추가합니다.
server.session.cookie.max-age= 1
server.session.timeout= 1
이제 우리의 스프링 빈들은 한 분 내에 무효화될 것입니다. 스프링 부트 애플리케이션으로 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 Request Scope Test
어떤 브라우저든 열고 URL https://localhost:8080/nameRS
로 이동하여 콘솔 출력을 확인하십시오. 각 요청마다 DataRequestScope Constructor Called
이(가) 인쇄된 것을 확인해야 합니다.
Spring Bean Session Scope Test
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 Beans Scope Spring Boot 프로젝트는 GitHub 저장소에서 다운로드할 수 있습니다.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-scopes