Bean
Spring에서 Bean이란 Spring IoC 컨테이너에 의해 관리되는 객체를 말합니다. Spring bean은 컨테이너에 의해 인스턴스화되고, 의존성을 주입받고, 생애주기동안 관리됩니다.
스프링 컨테이너는 하나 이상의 빈을 관리합니다. 이러한 빈은 컨테이너에 제공하는 configuration metadata를 통해 생성됩니다.
Bean definition과 Configuration metadata
Configuration metadata는 개발자가 스프링 컨테이너에게 애플리케이션의 객체를 어떻게 인스턴스화, 구성, 조합하는지에 관한 관한 방법을 나타냅니다.
이러한 Configuration metadata는 xml 또는 자바 기반으로 작성할 수 있습니다.
작성한 metadata는 ApplicationContext
에 전달하면 설정 정보를 읽어들여 빈을 정의합니다.
컨테이너 내부에서 이러한 빈 정의는 BeanDefinition
객체로 표현되어 스프링 컨테이너는 이러한 설정 정보가 자바 코드인지, XML 인지 전혀 알 필요 없이 오직 BeanDefinition
만 알면 됩니다. 그리고 이러한 BeanDefinition
으로부터 스프링 빈을 생성하게 됩니다.
빈 스코프
스프링 컨테이너는 싱글톤, 프로토타입과 4개의 웹 스코프까지 총 6개의 빈 스코프를 제공합니다.
싱글톤
싱글톤 스코프는 스프링 빈의 기본 스코프입니다. 컨테이너별로 단 하나의 인스턴스만 생성되며 해당 빈에 대한 모든 요청과 참조는 캐시된 객체를 반환합니다.
GoF의 싱글톤 패턴과의 주요한 차이점은 싱글톤 패턴의 경우 클래스 로더 별로 단 하나의 인스턴스가 생성된다는 것을 보장한다면, 스프링은 컨테이너 별로 단 하나의 인스턴스가 생성된다는 것을 보장한다는 점입니다.
기본적으로 스프링 컨테이너는 초기화 프로세스의 일부로 모든 싱글톤 빈을 생성하고 구성합니다. 이를 통해서 빈의 구성에 있어서 발생할 수 있는 오류를 즉시 발견할 수 있습니다.
컨테이너에 빈의 지연 초기화를 지시할 수도 있습니다. 그러나 다른 싱글톤 빈이 해당 지연 초기화 빈에 대한 의존성을 가지고 있다면 컨테이너는 지연 초기화 빈을 애플리케이션 초기화 시점에 생성하여 주입합니다.
프로토타입
프로토타입 스코프는 특정 빈에 대한 요청이 있을 때마다 새로운 빈 인스턴스를 생성하고 획득합니다.
일반적으로 상태를 가지는 빈들은 프로토타입 스코프를 사용하고, 상태를 가지지 않는 빈들은 싱글톤 스코프를 사용해야 합니다.
프로토타입 빈은 컨테이너에 의해 전체 라이프사이클이 관리되지 않습니다.
빈 객체를 인스턴스화하고, 의존성을 주입한 뒤 클라이언트에게 전달하면 싱글톤 빈과는 달리 마치 일반적인 객체처럼 클라이언트가 라이프사이클을 관리해야 합니다.
단순히 생각해보면 자바에서 new
연산자를 컨테이너가 대체한다고 볼 수 있습니다.
싱글톤 빈과 프로토타입 빈
만일 싱글톤 빈이 프로토타입 빈에 대한 의존성을 가지고 있다면 우리가 원치 않는 방식의 동작이 수행될 것입니다.
무상태의 싱글톤 빈 A
가 상태를 가진 비싱글톤 빈 B
를 사용한다고 가정해보겠습니다.
- 개발자는
A
의 특정 메서드를 호출할 때마다 새롭게 생성된B
인스턴스에 의해서 작업이 처리되기를 원할것입니다. 그러나 싱글톤 빈은 애플리케이션 초기화 단계에 인스턴스화 되고 종속성이 주입될 것입니다. - 이때,
A
인스턴스화 되면서 프로토타입 빈B
도 인스턴스화되어 주입이 이루어질 것입니다. 우리가 원하는 것은 매 호출마다 새로운 인스턴스B
가 생성되는 것이지만,A
는 기존에 가지고 있는 인스턴스B
를 이용하여 작업을 수행할 것입니다.
만일 매번 새로운 인스턴스가 생성되길 원한다면 applicationContext.getBean()
을 통해 직접 빈을 조회해오거나 다음과 같은 메서드 주입을 이용해야 합니다.
Lookup Method Injection
스프링 프레임워크는 CGLIB
라이브러리를 이용하여 바이트코드를 조작, 동적으로 메서드를 override하는 자식 클래스를 생성하여 메서드 주입을 구현합니다.
비싱글톤 빈 B
를 사용하는 싱글톤 빈 A
에서 B
를 주입받기 위해서는 다음과 같이 @Lookup
을 이용할 수 있습니다.
public abstract class A {
public Object process(int input) {
B b = createB();
b.setInput(input);
return b.execute();
}
@Lookup
protected abstract B createB();
}
또다른 방법으로는 @Configuration
클래스에서 다음과 같이 정의될 수도 있습니다.
@Bean
@Scope("prototype")
public B b() {
return new B();
}
@Bean
public A a() {
return new A() {
protected B createB() {
return b();
}
}
}
@Bean
과 @Configuration
@Configuration
는 해당 클래스의 주요 목적이 bean definition 소스임을 나타냅니다. 또한, @Configuration
을 사용하면 동일한 클래스에서 다른 @Bean
메서드를 호출하여 빈 간의 종속성을 정의할 수 있습니다.
어떻게 싱글톤 객체가 만들어지는가?
모든 @Configuration
클래스는 애플리케이션 실행시에 바이트코드를 조작하는 라이브러리인 CGLIB를 통해 자식 클래스가 만들어집니다.
자식 클래스는 부모 클래스의 메서드를 호출하기 전에 컨테이너에 캐시된 빈이 있는지 확인합니다.
따라서 @Configuration
클래스는 final
로 선언되서는 안 됩니다. @Bean
메서드 또한 final
, private
으로 선언되서는 안 됩니다.
또한 @Configuration
이 아닌 곳에서 등록한 @Bean
은 바이트코드 조작이 이루어지지 않기 때문에 싱글톤을 보장하지 않습니다.
'Spring' 카테고리의 다른 글
분산 환경에서의 스프링 스케줄러 사용 (0) | 2023.09.10 |
---|---|
스프링 시큐리티 인증 프로세스와 데이터베이스 (0) | 2023.08.12 |
스프링 부트가 제공하는 프로덕션 준비 기능 (0) | 2023.05.29 |
스프링 부트에서 외부 설정에 접근하는 방법 (0) | 2023.05.29 |
스프링과 웹소켓 (0) | 2023.05.01 |