스프링 빈 스코프
Spring Bean 범위를 사용하면 Bean 인스턴스 생성을 보다 세부적으로 제어할 수 있습니다. 때때로 우리는 bean 인스턴스를 싱글톤으로 생성하기를 원하지만 다른 경우에는 요청마다 또는 세션에서 한 번 생성되기를 원할 수 있습니다.
스프링 빈 스코프
Spring Bean 범위에는 5가지 유형이 있습니다.
- singleton - 스프링 컨테이너에 대해 스프링 빈 인스턴스 하나만 생성됩니다. 이것은 기본 스프링 빈 범위입니다. 이 범위를 사용하는 동안 Bean에 공유 인스턴스 변수가 없는지 확인하십시오. 그렇지 않으면 데이터 불일치 문제가 발생할 수 있습니다.
- prototype – 스프링 컨테이너에서 빈이 요청될 때마다 새 인스턴스가 생성됩니다.
- 요청 – 프로토타입 범위와 동일하지만 웹 애플리케이션에 사용하기 위한 것입니다. 각 HTTP 요청에 대해 빈의 새 인스턴스가 생성됩니다.
- 세션 – 컨테이너에 의해 각 HTTP 세션에 대해 새 빈이 생성됩니다.
- global-session – 포틀릿 애플리케이션을 위한 전역 세션 빈을 생성하는 데 사용됩니다.
Spring Bean 싱글톤 및 프로토타입 범위
Spring bean 싱글톤 및 프로토타입 범위는 독립형 스프링 앱에서 사용할 수 있습니다. @Scope
주석을 사용하여 이러한 범위를 쉽게 구성하는 방법을 살펴보겠습니다. 자바 빈 클래스가 있다고 가정해 보겠습니다.
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
이 기본 범위이므로 위의 bean 정의에서 @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();
}
이번에는 main 메소드가 실행될 때 다음과 같은 출력을 얻을 것입니다.
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
범위는 독립 실행형 애플리케이션에서 사용할 수 없기 때문입니다.
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>
스프링 컴포넌트를 생성하고 범위를 request
및 세션
인 스프링 컨테이너에서 스프링 빈으로 구성해 봅시다.
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;
}
}
스프링 컴포넌트
이제 스프링 구성 요소를 만들고 스프링을 사용하여 위의 빈을 자동 구성해 보겠습니다.
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
이제 세션 범위가 있는 스프링 빈이 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 요청 범위 테스트
브라우저를 열고 URL https://localhost:8080/nameRS
로 이동하여 콘솔 출력을 확인합니다. 각 요청에 대해 DataRequestScope Constructor Called
가 인쇄되는 것을 볼 수 있습니다.
Spring Bean 세션 범위 테스트
GitHub 리포지토리에서 Spring Beans Scope Spring Boot 프로젝트를 다운로드할 수 있습니다.