ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring]스프링 컨테이너와 스프링 빈
    개발관련 2023. 5. 29. 20:55

    1. 스프링 컨테이너의 생성, 의존성 주입

     

    스프링 컨테이너란, 내부의 객체들 간의 의존관계를 구성,관리하는 저장소로, 스프링에서 제공하는 대표적인 기능이다. 아래는 스프링 컨테이너를 생성하고 동적 의존성을 구입하는 과정을 살펴보자.

     

     //1
     ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
     //2
     OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
     MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
    package hello.core;
    
    import hello.core.Order.OrderService;
    import hello.core.Order.OrderServiceImpl;
    import hello.core.discount.DiscountPolicy;
    import hello.core.discount.RateDiscountPolicy;
    import hello.core.member.MemberService;
    import hello.core.member.MemberServiceImpl;
    import hello.core.member.MemoryMemberRepository;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    
    @Configuration
    public class AppConfig {
    
        @Bean
        public MemberService memberService(){
            return new MemberServiceImpl(getMemberRepository());
        }
    
        @Bean
        public MemoryMemberRepository getMemberRepository() {
            return new MemoryMemberRepository();
        }
    
        @Bean
        public OrderService orderService(){
            return new OrderServiceImpl(getMemberRepository(), getDiscountPolicy());
        }
    
        @Bean
        public DiscountPolicy getDiscountPolicy() {
            //return new FixDiscountPolicy();
            return new RateDiscountPolicy();
        }
    }

     

    1.ApplicationContext 객체(추상화 객체로 ApplicationContext를 상속하는 여러 스프링컨테이너 객체들을 인자로 받을 수 있다.)로 스프링컨테이너를 생성하고 사용에 알맞은 Spring Container객체를 주입한다.(annotation을 이용하여 Container를 구성하려고 AnnotationConfigApplicationContext객체를 사용한다.)  매개변수로는 스프링컨테이너로 사용할(@Configuration annotation이 있는) class파일명을 넣는다.(AppConfig.class)

     

    2.위 명령에 따라 Spring(bean) Container가 생성되고 name(AppConfig.class의 메소드명),type(AppConfig.class의 Return타입)으로 조회할 수 있다. 이 과정 중 각 Bean명칭은 동일하면 안된다.(intellij  최신 버전은 잡아 준다고 한다.)

    <그림 추가>

    3.컨테이너 생성 후 각 Bean들은 서로 의존성을 주입한다. 

    <그림 추가>

     

     

    2.Bean 전체 조회

    스프링 컨테이너에 생성된 빈을 전체 조회 할 수 있다. 

     

      AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    
        @Test
        @DisplayName("모든 빈 출력하기")
        // 앞에 public 생략
        void findAllBean() {
            String[] beanDefinitionNames = ac.getBeanDefinitionNames();
            for (String beanDefinitionName : beanDefinitionNames) {
                Object bean = ac.getBean(beanDefinitionName);
                System.out.println("name = " + beanDefinitionName + " object = "+ bean);
            }
        }

    getBeanDefinitionNames메소드로 Bean이름을 받아 출력,확인 하면 된다. 

    다만, Bean에는 Application(구현 Bean), InfraStructure(Spring 내부 Bean) Role이 있는데 이를 구분하기 위해서는 getBeanDefinition 메소드를 사용해야 한다. 그래서 ApplicationContext를 사용하면 안되고AnnotationConfigApplicationContext를 사용해야 한다. 

     

    3.Bean 조회 방법

    Bean을 조회하는 방법은 이름과 타입으로 할 수 있다. 다만, 타입을 실제 추상타입이 아닌 구체적인 객체타입(예제의 MemberServiceimpl)으로도 조회 가능한데 이러면 역할과 구현의 정확한 구분을 해야하는 객체지향프로그래밍과 맞지 않아 사용을 자제하는 게 좋다.

     

    package hello.core.beanfind;
    
    import hello.core.AppConfig;
    import hello.core.member.MemberService;
    import hello.core.member.MemberServiceImpl;
    import org.assertj.core.api.Assertions;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import static org.assertj.core.api.Assertions.*;
    import static org.junit.jupiter.api.Assertions.*;
    
    public class ApplicationContextBasicFindTest {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    
        @Test
        @DisplayName("빈 이름으로 조회")
        void findBeanByName() {
            MemberService memberService = ac.getBean("memberService", MemberService.class);
            assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
        }
    
        @Test
        @DisplayName("빈 Type으로 조회")
        void findBeanByType() {
            MemberService memberService = ac.getBean(MemberService.class);
            assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
        }
    
        // 구체로 조회해도 된다.(실제 빈에서 MemberServiceImple로 의존관계형성되어 있기 때문에) 근데 구현과 역할을 구분해야 하는 데 이렇게 하면 구분이 잘 안된다. 좋은 방법이 아니다.
        @Test
        @DisplayName("빈 Type으로 조회")
        void findBeanByType2() {
            MemberServiceImpl memberService = ac.getBean(MemberServiceImpl.class);
            assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
        }
    
        @Test
        @DisplayName("빈 이름으로 조회X")
        void findBeanByNameX(){
        //    ac.getBean("xxxxx",MemberService.class);
            assertThrows(NoSuchBeanDefinitionException.class,
                    ()->ac.getBean("xxxxx",MemberService.class));
        }
    }

     

Designed by Tistory.