ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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에서 팩토리빈, 팩토리 메서드 확인이 가능하다.

     

     

     

     

     

    [출처]

    스프링 핵심 원리 - 기본편

    https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

     

    스프링 핵심 원리 - 기본편 - 인프런 | 강의

    스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...

    www.inflearn.com

     

Designed by Tistory.