-
[Spring][Kotlin] 스프링 컨테이너Spring 2022. 5. 12. 16:37
ApplicationContext를 스프링 컨테이너라고 한다.
ApplicationContext 는 인터페이스인데 XML기반, 애노테이션 기반의 자바 설정 클래스로 하는 구현체를 선택할 수 있다.
애노테이션 기반의 자바 설정 클래스를 기반으로 하는 스프링 컨테이너는 아래와 같이 만들 수 있다.
//스프링 컨테이너 생성 val applicationContext = AnnotationConfigApplicationContext(AppConfig::class.java)AnnotationConfigApplicationContext -> 이 클래스는 ApplicationContext 인터페이스의 구현체이다.
스프링컨테이너를 부를 때 Bean Factory, ApplicationContext로 구분하지만 일반적으로는 ApplicationContext를 스프링 컨테이너라고 한다.
스프링 컨테이너
: 사용 할 객체들 담고 있는 것.
스프링 컨테이너의 생성 과정
1. 스프링 컨테이너 생성

여기서는 AppConfig.class를 구성 정보를 지정하였다. 2. 스프링 빈 등록

스프링 컨테이너는 설정 클래스 정보를 사용해서 스프링 빈을 등록한다. @Bean Annotation을 붙여주면 된다. 3. 스프링 빈 의존관계 주입

스프링 컨테이너는 설정 정보를 참고해서 DI 한다. 빈 이름은 겹치지 않게 설정해야 한다.
겹치면 다른 빈을 무시하거나 기존 빈을 덮어버리거나 설정에 따른 오류가 발생한다.컨테이너에 등록된 빈 조회
컨테이너에 등록된 모든 빈 조회
아래는 구성 정보를 담고 있는 AppConfig class이다.
package soyang.hellocore import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import soyang.hellocore.discount.DiscountPolicy import soyang.hellocore.discount.RateDiscountPolicy import soyang.hellocore.member.MemberRepository import soyang.hellocore.member.MemberService import soyang.hellocore.member.MemberServiceImpl import soyang.hellocore.member.MemoryMemberRepository import soyang.hellocore.order.OrderService import soyang.hellocore.order.OrderServiceImpl @Configuration class AppConfig { @Bean fun memberService(): MemberService = MemberServiceImpl(memberRepository()) @Bean fun orderService(): OrderService = OrderServiceImpl(memberRepository(), discountPolicy()) @Bean fun memberRepository(): MemberRepository = MemoryMemberRepository() @Bean fun discountPolicy(): DiscountPolicy = RateDiscountPolicy() }모든 Bean을 출력하는 테스트 코드를 작성했다.
package soyang.hellocore.beanfind import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.springframework.context.annotation.AnnotationConfigApplicationContext import soyang.hellocore.AppConfig class ApplicationContextInfoTest { val ac = AnnotationConfigApplicationContext(AppConfig::class.java) @Test @DisplayName("모든 빈 출력하기") fun findAllBean(){ val beanDefinitionNames = ac.beanDefinitionNames beanDefinitionNames.forEach{ val bean = ac.getBean(it) println("name=$it obejct=$bean") } } }
Test 실행 결과 그 중 이 부분이 모든 Bean 조회 결과이다.

AppConfig도 스프링 빈도 등록된다!!
직접 등록한 memberService, orderService, memberReository, discountPolicy 외에도 Bean들이 있다.
그 위에 출력되는 빈들은 스프링이 내부적으로 사용하는 빈들이다.
애플리케이션 빈 출력하기
이때 Role을 사용하여 사용자 정의 빈을 따로 조회할 수 있다.
- ROLE_APPLICATION : 직접 등록한 애플리케이션 빈
- ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈
@Test @DisplayName("애플리케이션 빈 출력하기") fun findApplicationBean(){ val beanDefinitionNames = ac.beanDefinitionNames beanDefinitionNames.forEach{ val beanDefinition = ac.getBeanDefinition(it) if(beanDefinition.role == BeanDefinition.ROLE_APPLICATION){ val bean = ac.getBean(it) println("name=$it obejct=$bean") } } }Test를 돌려보면 내가 등록한 빈들만 출력된다.
빈 조회 방법
val bean1 = ac.getBean(빈 이름, 타입) //또는 val bean2 = ac.getBean(타입)메서드에서 반환하는 대상이 없으면 suchNoBeanDefinitionException이 발생한다.
빈 조회 시 구체 타입으로 조회가 가능하지만 유연성이 떨어지므로 좋지 않다.
동일한 타입이 둘 이상인 경우
//해당 타입 모든 빈 조회. Map 반환 val map = ac.getBeansOfType(타입)Configuration 클래스를 inner class로 만들면 UnsatifiedDependencyException이 발생하였다.
이유를 찾아봤는데,
innerclass는 outerclass에 종속되어 있다.
따라서 outerclass가 생성되어야 사용이 가능하다.
근데 외부 클래스 객체가 생성되기도 전에 스프링 컨테이너에서 innerclass를 찾아오려고 한다.
그럼 그런 클래스가 없다고 오류가 발생하는 것이다.
=> Sol) static(companion object)로 생성해주면 외부 클래스 객체 생성 유무와 별개로 생성되어서 독립적으로 사용이 가능하다.상속관계일 경우 조회
: 부모타입 조회 시 자식타입이 함께 조회된다. (줄줄이 다 끌려나온다.)
그래서 Object 타입으로 조회 시 모든 스프링 빈이 조회된다.
BeanFactory와 ApplicationContext

BeanFactory
- 스프링 컨테이너 최상위 인터페이스이다.
- 스프링 빈을 관리하고 조회하는 역할을 담당한다.
ex) getBean()
ApplicationContext
: BeanFactory의 모든 개능을 상속받아 제공한다.
그 외 제공하는 부가기능( + Bean 관리 기능)
- 메시지 소스 이용한 국제화 기능 : 언어별 파일 분리
- 환경변수 : 각 환경별로 어떤 DB연결해야할지 등 환경변수와 관련된 정보 처리
- 애플리케이션 이벤트 : 이벤트 발행, 구독 모델 편리하게 지원
- 편이한 리소스 조회 : 파일, 클래스 패스, 외부 리소스 추상화 등 편리하게 조회
스프링 컨테이너는 다양한 설정 정보 받아들일 수 있게 유연하게 설계되어 있다.
XML로 설정
GenericXmlApplicationContext() 사용한다.
장점 : 컴파일 없이 빈 설정정보를 변경 기능하다.
스프링 빈 설정 메타정보 - BeanDefinition
Spring이 다양한 설정 형식을 지원할 수 있는 건 BeanDefinition이라는 추상화가 있기 때문이다.
(역할과 구현을 분리하였다.)
이 BeanDefinition이 빈 설정 메타정보이다.
@Bean 하나 당 메타정보가 하나씩 생성된다.
스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈을 생성한다.
(스프링 컨테이너는 추상화에만 의존하는 것이다.)
ApplicationContext의 Reader가 설정정보(AppConfig.class)를 쭉 읽은 다음 빈 메타정보를 생성한다(BeanDefinition)

필요하다면 ApplicationContext를 상속받은 임의의 형식을 만들어도 된다.
스프링 빈 등록 방법에는
1. 직접 등록 방법
2. Factory Bean 통해서 쓰는 법
이 있다.
2번은 지금까지 한 방법이고,
BeanDefinition에서 팩토리빈, 팩토리 메서드 확인이 가능하다.
[출처]
스프링 핵심 원리 - 기본편
스프링 핵심 원리 - 기본편 - 인프런 | 강의
스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...
www.inflearn.com
'Spring' 카테고리의 다른 글
[Spring][Kotlin]컴포넌트 스캔 (0) 2022.05.19 [Spring][Kotlin] 싱글톤 컨테이너 (0) 2022.05.18 [Spring][Kotlin] 제어의 역전(Ioc)과 의존관계 주입(DI) (0) 2022.04.13 [Spring][Kotlin] 순수 자바로 객체지향 설계원칙 지키기 + 스프링으로 전환 (0) 2022.04.12 [Spring][Kotlin] 좋은 객체지향 설계 5가지 원칙 SOLID와 스프링 (0) 2022.04.12