스프링 컨테이너
스프링은 스프링 컨테이너에 있는 객체를 관리한다. 스프링 컨테이너에 속한 객체를 빈(Bean) 객체라 하는데 스프링은 빈으로 등록된 모든 객체를 스프링 컨테이너에 생성하고 의존성을 주입한다.
스프링 컨테이너 생성 과정
1. 스프링 컨테이너 생성
스프링 컨테이너(ApplicationContext) 객체를 생성해 스프링 컨테이너를 생성한다. 이때 생성자로 스프링 설정 정보를 전달한다. 어노테이션 기반의 자바 설정 클래스 또는 XML 등 다양한 형식의 설정 정보를 넘겨줄 수 있다.
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
2. 스프링 빈 등록, 의존 주입
전달받은 설정 정보를 기반으로 스프링 컨테이너에 스프링 빈을 생성한다. 기본적으로 스프링은 빈 이름을 Key로 하여 싱글톤으로 빈 객체를 생성, 관리하기 때문에 빈 이름이 중복되서는 안된다. (빈 이름으로 빈 객체를 가져온다.)
3. 스프링 빈 의존 주입
전달받은 설정 정보를 기반으로 스프링 빈에 의존을 주입(DI)한다. 만약 빈 객체가 생성자 주입 방식을 사용하고 있다면 빈 생성과 동시에 의존을 주입한다.
스프링 컨테이너 상속도
BeanFactory 인터페이스는 스프링 컨테이너의 최상위 인터페이스이다. 스프링 빈을 조회하고 관리하는 역할을 한다. ApplicationContext 인터페이스는 BeanFactory를 포함한 여러 인터페이스들을 상속받은 인터페이스로 스프링 컨테이너라 하면 일반적으로 ApplicationContext을 의미한다. 아래의 인터페이스를 다중 상속받고 있다.
- MessageSource : 메시지 소스를 활용한 국제화 기능
- EnvironmentCapable : 로컬, 개발, 운영 등을 구분해서 처리할 수 있는 기능
- ApplicationEventPublisher : 이벤트를 발행하고 구독하는 모델을 편리하게 지원하는 기능
- ResourceLoader : 외부로부터 리소스를 읽어오는 기능
빈 설정 메타 정보(BeanDefinition)
다양한 형식의 설정 정보(JAVA, XML 등)을 기반으로 스프링 컨테이너를 만들 수 있는 이유는 어떠한 형식의 설정 정보를 사용하더라도 BeanDefinition 메타 정보를 생성하고, BeanDefinition을 기반으로 스프링 컨테이너를 만들기 때문이다.
예를 들어 AnnotationConfigApplicationContext는 AnnotatedBeanDefinitionReader를 사용해서 어노테이션 기반의 자바 설정 클래스인 AppConfig.class를 읽고 BeanDefinition을 생성한다. 그 후 BeanDefinition을 기반으로 스프링 컨테이너를 만든다. BeanDefinition을 생성할 수만 있다면 어떤 형식의 설정 정보든지 상관이 없다.
빈 생명 주기와 콜백
스프링 컨테이너가 생성되면 아래의 일이 일어난다.
스프링 컨테이너 생성 → 빈 객체 생성 → 의존성 주입 →
초기화 콜백 메소드 호출 → 사용 → 소멸 전 콜백 메소드 호출 → 스프링 컨테이너 소멸
스프링 컨테이너 생성 과정에서 빈 객체 생성과 의존 주입이 발생한다. 그 후 빈 객체를 완전히 사용할 수 있게 되면 초기화 콜백 메서드가 호출되고 스프링 컨테이너 소멸 전 콜백 메서드가 호출된다. @PostConstruct, @PreDestroy 어노테이션으로 빈 객체의 콜백 메서드를 만들 수 있다.
빈 등록과 컴포넌트 스캔
객체를 빈으로 등록하는 방법에는 크게 두 가지가 있다. 첫 번째 방법은 수동 빈 등록 방법이다. 즉 스프링 설정 파일에서 빈을 등록하고 의존성을 주입하는 방법이다. 주로 외부 라이브러리 객체를 빈으로 등록할 때 사용한다. 아래는 어노테이션 기반 자바 설정 클래스에서 3개의 빈을 등록한 코드이다.
@Configuration
public class AppConfig {
@Bean
@Qualifier("KoreaTire")
public Tire getKoreaTire() {
return new KoreaTire();
}
@Bean
@Qualifier("AmericaTire")
public Tire getAmericaTire() {
return new AmericaTire();
}
@Bean
public SunRoof getSunRoof() { return new SunRoof(); }
}
두 번째 방법은 자동 빈 등록 방법이다. 즉 컴포넌트 스캔을 사용한다. 컴포넌트 스캔이란 @Component, @Service, @Controller, @Configuration 등의 어노테이션이 붙은 객체를 찾아 빈으로 등록하는 기능을 말한다. 보통 등록되는 빈의 개수는 몇백 개에서 몇천 개가 되기에 일일이 수동으로 빈을 등록하면 번거롭다. 따라서 직접 만든 클래스는 간편하게 컴포넌트 스캔을 이용해 빈으로 등록한다. 아래는 어노테이션 기반 자바 설정 클래스에서 컴포넌트 스캔을 사용해 자동으로 빈을 등록하는 코드이다.
@Configuration
@ComponentScan(basePackages = "TEST") //TEST 패키지 내 컴포넌트를 스캔
public class AppConfig { ···
package TEST;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class Car {
private final Tire tire;
private final SunRoof sunRoof;
@Autowired
public Car(@Qualifier("KoreaTire") Tire tire, @Autowired(required = false) SunRoof sunRoof) {
this.tire = tire;
this.sunRoof = sunRoof;
}
public String getTireBrand() {
return tire.getBrand();
}
public String getSunRoofBrand() {
if(sunRoof == null)
return "sunRoof not exist";
return sunRoof.getBrand();
}
}
자동 빈 등록, 수동 빈 등록, 두개의 방식으로 빈을 등록하면 빈 이름이 중복되어 오류가 발생할 수 있으므로 빈 이름에 대한 주의가 필요하다.
'Spring > Spring Basic' 카테고리의 다른 글
스프링 IoC / DI (0) | 2023.02.01 |
---|