스프링 컨테이너와 스프링 빈

udonehn·2023년 1월 28일
0

스프링 컨테이너 생성

AppConfig

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService(){
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }

    @Bean
    public OrderService orderService(){
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    @Bean
    public DiscountPolicy discountPolicy(){
        //return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }
}
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
  • ApplicationContext를 스프링 컨테이너라 한다
  • ApplicationContext는 인터페이스이다.

스프링 컨테이너의 생성 과정

  1. 스프링 컨테이너 생성
    • new AnnotationConfigApplicationContext(AppConfig.class); : AppConfig.class구성 정보로 지정한다.
    • 는 빈의 이름, 은 빈 객체로 빈(bean) 저장소를 생성한다.
  2. 스프링 빈 등록
    • 빈 이름(키)은 메소드 이름(memberService, memberRepository, orderService, orderService)으로 등록한다.
    • 빈 객체(값)는 반환되는 객체(MemberServiceImpl, MemoryMemberRepository, OrderServiceImpl, RateDiscountPolicy)로 등록한다.
  3. 스프링 빈 의존관계 설정 - 준비
  4. 스프링 빈 의존관계 설정 - 완료
  • 스프링 컨터에너는 설정 정보를 참고하여 의존관계를 주입(DI)한다.
  • 동적인 객체 인스턴트 의존관계를 스프링이 연결한다.
  • 스프링 빈, 객체의 참조값들이 연결된다.

컨테이너에 등록된 모든 빈 조회

  • test.java.hello.core 밑에 beanfind 패키지를 생성하고 ApplicationContextInfoTest 클래스를 생성한다.
  • 리스트나 배열 다음에 IntelliJ 단축키 iter을 입력하면 for문을 자동으로 완성할 수 있다.
  • IntelliJ 단축키 soutvsoutm를 통해 System.out.println을 쉽게 입력할 수 있다.
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    
    @Test
    @DisplayName("애플리케이션 빈 출력하기")
    void findApplicationBean(){
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
            if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){
                Object bean = ac.getBean(beanDefinitionName);
                System.out.println("name = " + beanDefinitionName + " object = " + bean);
            }
        }
    }
  • String[] beanDefinitionNames = ac.getBeanDefinitionNames(); : 스프링에 등록된 모든 빈 이름을 조회한다.
  • BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName); : 빈에 관한 정보(메타 데이터)를 꺼낸다.
  • if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){ : 빈이 사용자가 정의한 빈일 시에만 실행한다.
    • 스프링이 내부에서 사용하는 빈은 getRole() 로 구분할 수 있다.
      • ROLE_APPLICATION : 일반적으로 사용자가 정의한 빈
      • ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈
  • Object bean = ac.getBean(beanDefinitionName); : 빈 이름으로 빈 객체(인스턴스)를 조회한다.

스프링 빈 조회 - 기본

  • Test 코드의 beanfind 패키지 아래 ApplicationContextBasicFindTest 클래스를 생성한다.

<코드>

...
    @Test
    @DisplayName("빈 이름으로 조회")
    void findBeanByName(){
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
...
    }
  • 스프링 빈은 getBean()을 통해 찾을 수 있다.
    • ac.getBean(빈 이름, 타입)
    • ac.getBean(타입)
  • 구체 타입으로 조회할 수도 있지만 유연성이 떨어진다.

오류 발생 시 테스트 통과

...
    @Test
    @DisplayName("빈 이름으로 조회X")
    void findBeanByNameX(){
        org.junit.jupiter.api.Assertions.assertThrows(NoSuchBeanDefinitionException.class,
                () ->ac.getBean("XXXX", MemberService.class));
    }
}
...
  • XXXX라는 이름의 빈은 없기 때문에 오류가 발생한다.
    • assertThrows은 오류가 발생해야 성공으로 처리한다.

스프링 빈 조회 - 동일한 타입이 둘 이상

  • 타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다. (getBean()사용 시)
    • 이름을 추가하거나, 특정 타입을 모두 조회하는 getBeansOfType()메소드를 사용하면 된다.

ApplicationContextSameBeanFindTest

  • Test 폴더의 beanfind패키지 아래 ApplicationContextSameBeanFindTest 클래스를 추가한다.

<코드>

    @Test
    @DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다.")
    void findBeanByTypeDuplicate(){
        assertThrows(NoUniqueBeanDefinitionException.class,
                () -> ac.getBean(MemberRepository.class));
    }
  • 람다 함수를 사용하였다.
    • ac.getBean(MemberRepository.class) 를 실행하면, NoUniqueBeanDefinitionException 오류가 발생하여야 한다.
  • Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class); : getBeansOfType 메소드는 Map 형식으로 반환한다.

스프링 빈 조회 - 상속 관계

  • 부모 타입으로 조회 시 모든 자식 타입도 함께 조회한다.
    • 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면 모든 스프링 빈을 조회한다.
  • 부모 타입으로 조회시 자식이 둘 이상 있으면 중복 오류가 발생한다(NoUniqueBeanDefinitionException 오류). 이를 해결하기 위해서는 다음 방법을 사용한다.
    1. 빈 이름을 지정한다.
    2. 특정 하위 타입으로 조회한다. (단, 유연성이 떨어진다.)
    3. 부모 타입으로 모두 조회 후, 사용한다.

ApplicationContextExtendsFindTest

  • test 폴더의 beanfind 패키지 아래 ApplicationContextExtendsFindTest 클래스를 생성한다.

<코드>

스프링 컨테이너와 스프링 빈

  • BeanFactory는 스프링 컨테이너의 최상의 인터페이스이며, 스프링 빈에 관한 여러 기능을 제공한다.
  • ApplicationContextBeanFactory을 상속받는다.
    • 메시지소스를 활용한 국제화 기능, 환경변수, 애플리케이션 이벤트, 편리한 리소스 조회와 같은 편리한 부가 기능을 제공한다.
  • BeanFactory나 ApplicationContext를 스프링 컨테이너라 한다.

다양한 설정 형식 지원 - 자바 코드, XML

  • 스프링 컨테이너는 자바 코드, XML, Groovy 등 여러 설정 정보를 받아들일 수 있다.
  • 최근에는 xml 형식의 설정을 잘 사용하지 않지만 많은 레거시 프로젝트에서 사용하고 있다.
  • Test에 xml 패키지를 생성하고, XmlAppContext 클래스를 생성한다.

<코드>

  • src.main.resources 밑에appConfig.xml을 생성한다.

<코드>

  • Appconfig와 매우 유사하다.

스프링 빈 설정 메타 정보 - BeanDefinition

<생략>

본 포스팅은 김영한 강사의 스프링 핵심 원리 강의를 수강하고 요약한 것으로, 해당 강의의 영상 및 강의자료를 참고하였습니다.

profile
안녕하세요. 만나서 반갑습니다.

0개의 댓글

관련 채용 정보