스프링 컨테이너
기존에는 개발자가 AppConfig를 사용해서 직접 객체를 생성하고 DI를 했지만, 이제부터는 스프링 컨테이너를 통해서 사용합니다.
스프링 컨테이너는 스프링에서 자바 객체인 스프링 빈을 관리하는 공간입니다. 또한 스프링 빈끼의 의존 관계도 스프링 컨테이너가 런타임 시점에 관리합니다. 다시 말해 스프링 컨테이너는 내부에 존재하는 빈의 생명주기를 관리(생성, 관리, 제거 등)하며, 생성된 빈에게 추가 기능도 제공합니다.
스프링 컨테이너는 @Configuration이 붙은 AppConfig를 설정 정보로 사용합니다.
여기서 @Bean이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록합니다.
이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 합니다. 스프링 빈에 등록되는 빈 이름은 @Bean이 붙은 함수명을 사용합니다. 스프링 빈 이름이 중복되면 이전에 등록된 빈이 무시되거나 빈으로 등록되지 않을 수 있기 때문에 절대 중복되는 함수명을 사용해서 스프링 빈을 등록해서는 안됩니다.
스프링 컨테이너는 XML 기반으로 만들 수 있고, 애노테이션 기반의 자바 설정 클래스로 만들 수 있습니다.
@Configuration과 @Bean을 사용한 방식이 애노테이션 기반의 자바 설정 클래스로 스프링 컨테이너를 만든 방식입니다.
스프링 컨테이너를 코드로 생성해 봅시다.
뒤에서 살펴보겠지만 ApplicationContext는 스프링 컨테이너 인터페이스이고 AnnotationConfigApplicationContext 타입이 구현체입니다. AnnotationConfigApplicationContext 타입의 생성자에 자바 설정 클래스를 넘겨서 스프링 컨테이너를 생성하며 스프링 빈을 등록하고 스프링 빈끼리의 의존관계를 설정할 수 있습니다.
스프링 빈
스프링 빈은 스프링 컨테이너에서 관리되는 자바 객체입니다.
스프링 빈에 별도 설정을 추가하지 않으면 기본적으로 싱글톤 빈이 됩니다. 싱글톤 빈은 컨테이너에 한 번만 생성되어 여러 스레드가 싱글톤 빈에 접근하도록 합니다. 스프링 빈은 기본적으로 싱글톤이지만 스프링 환경은 멀티 스레드 환경입니다. 스프링 특성상 초당 수십에서 수백 번의 클라이언트 요청을 받기 때문에 요청을 받을 때마다 빈을 생성한다면 굉장히 많은 자원이 낭비될 것입니다.
따라서 서블릿 클래스당 하나의 스프링 빈을 만들고 여러 클라이언트 요청을 싱글톤 빈이 대응하는 구조로 설계되었습니다.
하지만 의외로 싱글톤으로 구현된 스프링 빈은 thread-safe 하지 않습니다. 동시성 문제가 존재하는 것입니다.
싱글톤 스프링 빈이 불변 객체로 특정 값을 외부에서 전달받을 경우 thread-safe 합니다. 반대로 싱글톤 스프링 빈을 내부 상태(값)를 갖는 가변 객체로 만든다면 thread-safe 하지 않습니다.
다시 말해 스프링 빈 클래스가 상태(멤버변수)를 가진다면 thread-safe 하지 않습니다. 따라서 스프링 빈을 thread-safe 하게 사용하기 위해서는 스프링 빈으로 주입되는 클래스는 모두 상태를 가지지 않는 불변 객체여야 합니다.
스프링 컨테이너의 생성 과정
new AnnotationConfigApplicationContext(AppConfig.class)로 스프링 컨테이너를 만들 때 내부적인 과정을 살펴봅시다.
1. 스프링 컨테이너 생성
가장 먼저 스프링 컨테이너가 생성되고 안에 Map<Key, Value> 타입으로 스프링 빈을 저장하는 스프링 빈 저장소를 생성합니다. 스프링 빈 저장소의 Map<Key, Value>에서 Key는 빈의 이름, Value에는 빈 객체가 저장됩니다.
빈이 Map 형태로 저장되어 컨테이너의 빈을 조회할 때 getBeansOfTypes 함수를 사용해 특정 타입의 빈을 조회할 때 Map 형태로 반환됩니다.
2. 스프링 빈 등록
스프링 컨테이너는 파라미터로 넘어온 AppConfig 클래스 설정 정보를 사용해서 스프링 빈을 등록합니다.
이때 AppConfig 클래스는 @Configuration을 지정해서 설정 정보로 스프링 컨테이너가 인식할 수 있습니다.
AppConfig 클래스에 @Bean 애노테이션이 등록된 함수들이 스프링 빈으로 등록되고 함수명이 빈 이름으로 함수 리턴 객체가 빈 객체로 등록됩니다.
물론 @Bean(name= "memberService2")와 같은 방법으로 빈 이름을 직접 지정할 수도 있습니다.
3. 스프링 빈 의존관계 설정
스프링 컨테이너에서는 스프링 빈을 생성하고 빈끼리의 의존관계도 설정합니다.
이때 AppConfig 클래스에서 설정된 의존관계를 참고하여 의존관계를 주입(DI)합니다.
위 그림처럼 orderService와 memberService 모두 memberRepository를 호출하며 의존하고 있기 때문에 스프링 컨테이너에서 빈끼리도 동일한 의존 관계를 가집니다.
위에서 설명을 위해 스프링 빈을 생성하고 의존관계를 주입하는 순서로 설명했지만, 자바 코드로 스프링 빈을 등록하면 스프링 빈 생성과 의존관계 주입이 동시에 처리됩니다.
'Spring > Spring Basic' 카테고리의 다른 글
싱글톤 컨테이너 (1) | 2024.10.15 |
---|---|
스프링 빈 조회 (1) | 2024.10.15 |
스프링 핵심 원리 이해 - 객체 지향 원리 적용 (3) | 2024.10.15 |
자동 컴포넌트 스캔 vs 수동 컴포넌트 스캔 (1) | 2024.09.28 |