[스프링] 스프링 빈 스코프

빈 스코프

빈 스코프(bean scope)란 컨테이너에 등록한 빈 인스턴스가 스프링 컨테이너 내에서 생성되는 방법, 빈 인스턴스가 특정 범위에서 공유되는 방법을 의미한다. 빈의 스코프를 여러 형태로 지정함으로써 해당 빈 인스턴스를 컨테이너 내에서 하나만 생성하여 공유하도록 할지, 인스턴스를 생성할 때마다 새로 생성할지 등을 정할 수 있다.

@Scope는 빈 스코프를 지정하는 어노테이션이다. 어노테이션의 속성으로 다음과 같은 값들을 설정하여 해당 빈의 스코프를 지정할 수 있다.

  • singleton: 컨테이너 당 하나의 빈 인스턴스를 생성한다.
  • prototype: 객체 생성을 요청할 때마다 빈 인스턴스를 새로 생성한다.
  • request: HTTP 요청 당 하나의 빈 인스턴스를 생성한다. 웹 애플리케이션 컨텍스트에만 해당된다.
  • session: HTTP 세션 당 하나의 빈 인스턴스를 생성한다. 웹 애플리케이션 컨텍스트에만 해당된다.
  • globalSession: 전역 HTTP 세션 당 하나의 빈 인스턴스를 생성한다. 포털 애플리케이션 컨텍스트에만 해당된다.


기본적으로 스프링은 컨테이너에 정의한 빈마다 정확히 하나를 인스턴스를 생성하고 생성된 인스턴스는 전체 컨테이너 스코프에 공유된다. 따라서 getBean() 메서드를 호출하거나 컨테이너에 등록된 빈을 참조하면 해당 인스턴스가 반환된다. 즉, @Scope 어노테이션의 기본 속성은 singleton이다.

빈 스코프를 prototype으로 설정하면 getBean() 메서드를 호출할 때마다 빈 인스턴스를 새로 생성한다.


싱글톤과 동시성, 스레드 안전

스프링에서 컴포넌트의 기본 스코프는 싱글톤이므로 동시적 요청에 따른 동시성 이슈가 발생할 수 있다. 싱글톤으로 생성된 빈 인스턴스는 힙 메모리에 저장되므로 해당 빈에 대한 참조만 있다면 빈의 멤버(프로퍼티 및 메서드)에 접근 가능하다. 힙 메모리에 대한 객체 잠금(lock)을 설정하지 않는다면 각 스레드는 힙 메모리에 존재하는 빈 인스턴스의 동일한 참조를 가리킬 수 있으며 따라서 서로 스레드 모두 동일한 빈 인스턴스의 프로퍼티에 동시에 접근하거나 메서드를 동시에 실행할 수 있다. 이 경우 하나의 스레드가 빈 인스턴스의 프로퍼티를 변경시킬 때 다른 스레드는 변경된 프로퍼티를 갖는 빈 인스턴스에 접근하게 될 수 있으며 이로 인해 의도치 않은 결과가 발생할 수 있다. 요청 당 스레드를 사용하는 서블릿 컨테이너의 경우 동시적으로 요청을 처리할 때 서로 다른 요청을 처리하는 서로 다른 스레드가 동일한 싱글톤 빈 인스턴스의 멤버에 접근할 수 있다. 스레드 간 공유되어 문제가 발생 가능한 객체, 또는 그러한 코드 구현을 스레드 안전(thread-safe)하지 않다고 표현한다.

객체 잠금이란 공유 리소스에 대한 여러 스레드의 접근을 제어하는 동기화(syncronization) 기법 중 하나이다. 일반적으로 한 번에 하나의 스레드만 잠금을 획득할 수 있으며 스레드는 먼저 잠금을 획득해야 공유 리소스에 대한 접근이 가능하다.

스레드가 싱글톤 인스턴스의 메서드 내 정의한 로컬 변수에 접근하는 경우, 스레드마다 스택 메모리가 할당 되기 때문에 스레드 간 변수에 대한 동시적 접근 문제는 발생하지 않는다. 스택 메모리는 스레드 실행 중에 메서드 내부에서 사용되는 로컬 변수의 상태를 저장하는 역할을 한다. 스택 메모리를 사용하면 동시적으로 실행되는 스레드가 서로의 변수에 접근하지 않도록 할 수 있다. 따라서 메서드에 정의한 로컬 변수의 경우 싱글톤 빈 인스턴스에 대한 서로 다른 스레드의 접근은 동시성 이슈를 발생시키지 않는다.

클래스에 인스턴스 프로퍼티가 정의되어 있으면 해당 인스턴스는 스테이트풀(stateful) 인스턴스이며, 그렇지 않은 경우 스테이트리스(stateless) 인스턴스이다. 싱글톤인 스테이트리스 인스턴스를 사용하면 객체 잠금을 사용하지 않고도 동시성 이슈 발생을 막을 수 있다. 인스턴스 생성 이후 프로퍼티를 변경 불가능하게 하면 스테이트풀 인스턴스를 불변(immutable)으로 만들 수 있다. 불변 인스턴스는 스테이트리스 인스턴스와 동일하게 인스턴스의 내부 상태가 시간이 지나도 일정하게 유지되므로 여러 스레드 간에 안전하게 공유될 수 있다.


참고

Comments