WEB

[WEB] 스프링 컨테이너 (Spring Container)

아, 그래요? 2022. 4. 19. 12:22

이전 포스팅에서 관심사 분리를 설명하면서, 스프링 컨테이너에 대한 언급을 한적이 있다. 오늘 포스팅에서는 스프링 컨테이너에 대해 조금 더 자세히 이야기 하고자 한다.

 

https://ohreallystore.tistory.com/40

 

220417 [번외] : 객체 생성을 자동화해야하는 이유 (feat. 관심사분리)

기존의 프런트 컨트롤러에서 생성하던 페이지 컨트롤러와 DAO 객체의 생성을 ApplicationContext로 위임하여 프로그램을 실행시키면 객체가 자동으로 생성되게끔 프로그래밍하였다. 해당 과정에 대

ohreallystore.tistory.com

관련 내용은 해당 피드 참고

 

참고: 앞으로 계속 언급될 빈(Bean, 또는 스프링 빈)이란, 스프링이 관리하는 자바 객체를 뜻한다.


스프링 컨테이너 (Spring Container)

스프링 컨테이너는 스프링 빈을 저장하고 있어, 프로그램 실행시 스프링 빈(Spring Bean) 저장소의 빈(Bean) 객체들을 생성하고 의존성을 주입한다. 또한 생성된 빈을 관리및 조회하고, 구현체에 따라 추가 기능을 제공한다.

 


스프링 컨테이너 생성

ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

스프링 컨테이너는 ApplicationContext 타입으로 선언한다. AnnotationConfigApplicationContext는 ApplicationContext 인터페이스의 구현체로, 애노테이션 기반의 자바 설정 정보를 통해 스프링 컨테이너를 생성한다. 매개변수로 설정 정보(Configuration)를 넣어주어 해당 클래스를 통해 스프링 빈을 등록한다.


 

스프링 빈 저장소 등록

스프링 컨테이너는 내부에 스프링 빈 저장소를 가지고 있어, 스프링 컨테이너 생성시 구성 정보를 읽어와, 스프링 빈 저장소에 빈(Bean)을 등록한다.

스프링 컨테이너 생성과정

애노테이션 방식에서는 @Bean 애노테이션을 통해 스프링 빈을 등록할 수 있다.

 

AppConfig.class

@Bean
public BeanExample beanExample() {
	return new BeanExample();
}

 AppConfig.class 파일에 @Bean 애노테이션을 붙이고, 스프링 빈 저장소에 등록할 빈(Bean)을 리턴하면 된다. 기본 적으로 메서드명을 빈 이름으로 등록하고, 리턴하는 객체를 빈 객체로 등록한다. 위 코드의 경우, 빈 이름은 beanExample이고, BeanExample.class 객체가 빈 객체로 등록된다.

 

만약 메서드명이 아닌 다른 이름으로 빈 이름을 등록하고 싶다면 애노테이션에 직접 부여해주면 된다.

@Bean(name="newName")

'newName'이 빈 이름으로 등록된다.


 

스프링 빈 의존관계 설정

빈 객체를 생성한 후, 빈 객체의 의존관계를 주입해주어야 한다(Dependency Injection: DI) 의존 관계를 주입해주는 방법은 다양하지만 보통 생성자 호출을 통해 주입하는 것이 일반적이다(애노테이션 기반의 자바 설정 정보 기준)

 

AppConfig.class

@Bean
public BeanA beanA() {
	return new BeanA(beanB(), beanC());
}

@Bean
public BeanB beanB() {
	return new BeanB(beanD());
}

@Bean
public BeanC beanC() {
	return new BeanC();
}

@Bean
public BeanD beanD() {
	return new BeanD();
}

스프링 컨테이너는 AppConfig.class 파일을 읽어 스프링 빈 객체를 생성한후, 생성자의 매개 변수를 통해 빈 객체간의 의존관계를 설정한다.

 

스프링 컨테이너가 AppCofig.class 통해 위와 같이 의존관계를 설정한다.

 

스프링 빈의 생성과 의존관계 주입을 나눠서 설명했지만, 실제로는 생성자를 호출할때 동시의 의존관계가 주입되기 때문에, 빈의 생성과 의존관계 주입이 동시에 일어난다고 할 수 있다.

 


스프링 컨테이너 클래스 계층

위에서 스프링 컨테이너를 설명하면서, 스프링 컨테이너의 타입을 ApplicationContext라고 했지만, 실제로는 ApplicationContextBeanFactory의 하위타입이다. 또한 ApplicationContext를 생성하는 과정에서도 설정 정보의 형식에 따라 다양한 구현체(e.g. AnnotationConfigApplicationContext)를 통해 스프링 컨테이너를 생성한다.

스프링 컨테이너 클래스 계층 구조

 


빈 팩토리 (BeanFactroy)

BeanFactory는 스프링 컨테이너의 최상위 인터페이스이다. 스프링 빈의 생성/관리 및 조회하는 역할을 한다. getBean()메서드를 통해 빈을 조회 할 수 있다. 하지만 BeanFactory는 거의 사용하지 않는다. 왜냐하면 BeanFactory는 위에 언급한 빈 관리/조회외에는 기능이 없기 때문이다. 실제 실무에서는 빈 관리/조회외에도 수많은 부가기능들이 필요하고, 그러기 위해 ApplicationContext 타입으로 스프링 컨테이너를 생성한다.

 

ApplicationContext 객체를 설명한 문서의 일부분이다. 맨 아래를 보면 BeanFactory외에도 다양한 인터페이스를 상속받은 것을 볼 수 있다. 각각의 인터페이스들을 환경변수, 이벤트 관리, 국제화, 리소스 조회등의 기능 등을 제공한다.

 


 

빈 설정 메타 정보 (BeanDefinition)

스프링 컨테이너는 설정 정보(구성 정보)를 읽고, 빈을 생성하여 저장소에 등록한다. 문제는 설정 정보의 작성 방식이 다양하다는 것이다. 자바코드로 작성할 수도 있고, xml 파일로 작성할 수도 있고 또는 그 외의 형식으로 작성할 수도 있다. 실제로 스프링은 설정 정보 작성 방식에 따른 ApplicationContext 구현체를 제공한다. 

 

설정 정보 파일 형식에 따라 구현체를 지정해준다.

그렇다면 ApplicationContext는 서로 다른 형식의 설정 정보를 어떻게 읽을까? 이것을 해결해주는 것이 BeanDefinition, 빈 메타정보이다. 각각의 ApplicationContext 구현체를 Reader 클래스를 가지고 있는데 Reader Class는 설정 정보를 읽어 BeanDefinition을 생성한다. 그러면 ApplicationContext는 BeanDefinition을 읽고 빈 객체를 생성하고 저장소에 등록한다. 즉, 설정 정보가 어떤 형식으로 작성되었든 관계없이, ApplicationContext는 BeanDefinition만 있으면 스프링 컨테이너를 생성할 수 있다.


마무리

스프링 컨테이너는 구성영역을 빈에서 따로 분리함으로써, 객체지향의 원칙을 준수하는 코드를 작성하게 도와준다. 순수한 자바 코드에서는 프로그램 실행 과정에서, 자바 객체(Bean)을 자동으로 생성해주기 위해, Reflection API를 활용해, 복잡한 코딩을 해야했지만, 스프링 프레임워크가 도입되면서, 설정 정보를 작성하고, 스프링 컨테이너를 생성만 하면 빈 객체가 저장소에 자동으로 등록되어, 구성 영역을 사용영역으로부터 분리할 수 있게 되었다.

 

결국, 객체지향 프로그래밍의 관점, 프로그래밍의 생산성의 관점에서 바라본다면 스프링 컨테이너의 필요성을 충분히 이해할 수 있을 것이다.