[Spring Boot] Container
스프링 컨테이너
스프링 컨테이너는 스프링 프레임워크, 스프링 부트의 핵심 컴포넌트이다. 스프링에서는 외부에서 자바 객체의 생명 주기를 관리하며, 생성된 자바 객체들에게 추가적인 기능을 제공하는데, 이 역할을 담당하는 '외부'가 바로 컨테이너인 것이다.
컨테이너가 담고 있는 자바 객체를 스프링에서는 빈(Bean) 객체라고 부르는데, 컨테이너는 빈의 생성,관리, 제거 등 생명주기를 관리하며, 생성된 빈에게 의존성 주입 등 추가적인 기능을 제공하는 것이다.
스프링 컨테이너의 종류
스프링 컨테이너는 내부에 있는 자바 객체를 이용하여 빈 객체를 생성하기도 하고, 관계를 설정하기도 하는 등 마치 Bean 객체를 하나의 부품으로 사용한다. 워낙 다양한 기능을 가지고 있다보니, 여러가지 이름으로 불리기도 하는데 그 중 대표적인 것이 context, factory, IoC container 등이 있다.
여러가지 이름으로 불리지만 스프링 컨테이너의 종류는 두가지로 나뉘는데, 하나는 BeanFactory이고, 나머지 하나는 BeanFactory를 상속받는 ApplicationContext이다.
BeanFactory 는 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트이고, BeanFactory 를 좀 더 확장한 것이 ApplicationContext 이다. ApplicationContext 는 IoC 방식을 따라 만들어진 일종의 BeanFactory로 둘 다 동일한 개념이라 생각하면 된다. 주로 사용되는 스프링 컨테이너는 ApplicationContext 이다.
BeanFactory
BeanFactory는 스프링 컨테이너의 최상위 인터페이스로 빈을 등록, 생성, 조회 등 빈을 관리하는 역할은 한다. 또한 getBean() 메서드를 통해 빈을 객체화할 수 있다.
빈을 등록을 하는 방법으로는 @Bean 어노테이션을 붙임으로 해당 메소드의 이름을 사용하는 빈 객체가 등록이 된다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//클래스 이름앞에 반드시 어노테이션 명시 필요
//@Configuration --> 이 클래스는 '스프링 설정'에 사용되는 클래스 입니다.
//결국 IOC 컨테이너에 생성되는 bean 들을 기술하는 클래스 --> @Bean 사용
@Configuration
public class BeanExample {
public BeanExample() {
System.out.println("BeanExample() 생성");
}
// 리턴타입의 bean 객체 생성, 기본적으로 bean 의 이름은 메소드 이름으로 설정
@Bean
public Test test() {
System.out.println("test() 호출");
return new Test();
}
}
class Test {
public Test() {};
}
ApplicationContext
ApplicationContext는 BeanFactory 인터페이스의 빈 객체를 관리하고 검색하는 기능을 상속 받아 제공하고, 그 이외의 부가 기능 또한 제공하고 있다. (ApplicationContext는 BeanFactory의 확장 버전!!)
부가 기능으로는 아래와 같은 기능들을 제공하고 있다.
기능 | 설명 |
MessageSource | 메세지 다국화를 위한 인터페이스 |
EnvironmentCapable | 개발, 운영, 환경 변수 등으로 나누어 처리하고, 애플리케이션 구동 시 필요한 정보들을 관리하기 위한 인터페이스 |
ApplicationEventPublisher | 이벤트 관련 기능을 제공하는 인터페이스 |
ResourceLoader | 파일, 클래스 패스, 외부 등 리소스를 편리하게 조회 |
위의 기능들은, 스프링 부트를 공부하면서 하나 하나 알아 볼 것이다.
스프링 컨테이너의 특징
스프링 컨테이너의 특징들에는 무엇이 있으며, 해당 특징을 통해 얻을 수 있는 이점에 대해서 알아보겠다.
- 컨테이너는 개발자가 정의한 빈을 객체로 만들어 관리하고 개발자가 필요로 할 때 제공
- 의존성 주입을 통해 유지 보수성을 높이고 손쉽게 애플리케이션의 컴포넌트를 관리
- 서로 다른 빈을 연결하여 애플리케이션 빈을 연결하는 역할
이러한 특징으로, 개발자는 모듈간에 의존 및 결합으로 인해 발생하는 문제를 최소화할 수 있고, 컨테이너가 알아서 메소드의 빈 객체를 통해 객체를 주입해주기 때문에, 객체를 생성하고 호출하는 등의 번거로운 과정에서 자유로워질 수 있다.
즉, 스프링 컨테이너를 사용함으로써, 객체 간의 의존성을 낮추고, 높은 캡슐화가 가능해진 것이다. 또한 스프링 컨테이너를 사용함으로 구현 클래스에 있는 의존성이 제거되고, 인터페이스에만 의존하도록 설계할 수 있게 되면서 유지보수성이 월등히 높아졌다.
스프링 컨테이너 생성 과정
기본적으로 스프링 컨테이너는 Configuration Metadata를 사용한다. 또한, 스프링 컨테이너는 파라미터로 넘어온 설정 클래스 정보를 사용하여 스프링 빈을 등록한다. 이 때에 설정 클래스를 지정할 수 있는 어노테이션이 위의 예제 코드에서 사용한 @Configuration이다.
스프링 컨테이너를 만드는 다양한 방법은 ApplicationContext 인터페이스의 구현체이다. AppConfig.class 등의 구성 정보를 지정하여 스프링 컨테이너를 생성할 수 있다. 애플리케이션 클래스는 구성 메타데이터와 결합되어 ApplicationContext를 생성하고 초기화된다. 이후 실행 가능한 시스템 또는 애플리케이션을 갖게 된다.
만약, 스프링 빈 조회에서 상속관계가 있을 시에는 부모 타입으로 조회하면 자식 타입도 함께 조회된다. 즉, Object 타입으로 조회하게 되면 모든 스프링 빈을 조회할 수 있다.
ApplicationContext 컨테이너 정보 확인
일반적으로, 스프링 부트에서는 BeanFactory보다 ApplicationContext를 컨테이너로 사용한다. (어차피 BeanFactory의 기능을 물려받았기 때문!!) 그렇기 때문에, 현재 컨테이너가 어떠한 빈 객체를 가지고 있는지 등 여러 정보들을 어떻게 확인하는지 알아보자.
@SpringBootApplication
public class Main implements CommandLineRunner {
// ApplicationContext 객체를 담을 변수 선언
ApplicationContext ctx;
// 자동주입 (setter injection)
// => ApplicationContext 객체를 ctx에 넣어주기
@Autowired
public void setCtx(ApplicationContext ctx) {
this.ctx = ctx;
}
public Main() {
System.out.println("Main 객체 생성");
}
public static void main(String[] args) {
System.out.println("Main 시작");
SpringApplication.run(Main.class, args);
System.out.println("Main 종료");
}
@Override
public void run(String... args) throws Exception {
// 컨테이너 안에 생성된 bean 정보 조회
System.out.println("생성된 빈의 개수: " + ctx.getBeanDefinitionCount());
// 모든 bean 에는 name(id) 이 부여된다.
for(var name : ctx.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
위의 코드에서, ApplicationContext 객체를 생성하기 위한 과정은 지금은 깊게 알 필요가 없다. 지금은 생성한 ApplicationContext 객체를 통해 생성된 bean 정보를 조회하고 현재 컨테이너에 대한 정보를 알 수 있다는 정도만 알고 넘어가자!!