본문 바로가기
Back-End/Spring

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

by 두두리안 2021. 1. 4.
728x90

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

  • 목차
    • 1.스프링 컨테이너 생성
    • 2.컨테이너에 등록된 모든 빈 조회
    • 3.스프링빈 조회(기본)
    • 4.스프링빈 조회(동일한 타입이 둘이상)
    • 5.스프링빈 조회(상속관계)
    • 6.BeanFactory 와 ApplicationContext
    • 7.다양한 설정 형식 지원 - 자바코드 , XML
    • 8.스프링 빈 설정 메타정보 - BeanDefinition

1.스프링 컨테이너 생성

스프링 컨테이너 생성

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
  • ApplicationContext - 스프링 컨테이너 , 인터페이스
  • AnnotationConfigApplicationContext - 애노테이션 기반의 자바 클래스로 만드는것
  • XML 기반으로 만들수 있다
  • AppConfig - 애노테이션 기반의 자반 설정 클래스로 만든것
  • AnnotationConfigApplicationContext 는 ApplicationContext 의 구현체이다!

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

모든 빈 출력하기

public class ApplicationContextInfoTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("모든 빈 출력하기")
    void findAllBean(){

        String[] beanDefinitionNames= ac.getBeanDefinitionNames();

        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean= ac.getBean(beanDefinitionName);
            System.out.println("Name = " + beanDefinitionName + "Object" + bean);
        }
    }
}

애플리케이션 빈 출력하기

public class ApplicationContextInfoTest {

    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);
            }
        }
    }
}
  • BeanDefinition.ROLE_APPLICATION 을 이용해서 내가등록한 애플리케이션 빈을 출력한다

3.스프링빈 조회(기본)

빈 이름 조회

public class ApplicationContextBasicInfoTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("빈 이름 조회")
    void findBeanByName(){
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);

    }
}
  • memberService 의 isInstanceOf 가 MemberServiceImpl 을 가리키면 성공!

이름 없이 타입으로 조회

 public class ApplicationContextBasicInfoTest {

     AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

     @Test
     @DisplayName("이름 없이 타입으로 조회")
     void findBeanByType(){
         MemberService memberService = ac.getBean(MemberService.class);
         assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
     }
 }
  • "memberService" 없이 타입으로 조회 가능

구체 타입으로 조회

 public class ApplicationContextBasicInfoTest {

     AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

     @Test
     @DisplayName("구체 타입으로 조회")
     void findBeanByName2(){
         MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
         assertThat(memberService).isInstanceOf(MemberServiceImpl.class);

     }
 }
  • MemberServiceImpl 구체클래스로 조회가 가능하다

구체 타입으로 조회

 public class ApplicationContextBasicInfoTest {

     AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

     @Test
     @DisplayName("구체 타입으로 조회")
     void findBeanByName2(){
         MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
         assertThat(memberService).isInstanceOf(MemberServiceImpl.class);

     }
 }
  • MemberServiceImpl 구체클래스로 조회가 가능하다

빈 이름으로 조회X

 public class ApplicationContextBasicInfoTest {

     AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("빈 이름으로 조회X")
    void findBeanByNameX(){
        assertThrows(NoSuchBeanDefinitionException.class,
                () -> ac.getBean("xxxx", MemberServiceImpl.class));

    }   
 }
  • NoSuchBeanDefinitionException 예외가 터진다

4.스프링빈 조회(동일한 타입이 둘이상)

타입으로 조회시 같은 타입이 둘 이상 있으면 , 중복 오류가 발생

public class ApplicationContextSameBeanFindTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

    @Test
    @DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면 , 중복 오류가 발생")
    void findBeanByTypeDuplication(){
        Assertions.assertThrows(NoUniqueBeanDefinitionException.class,
            ()->ac.getBean(MemberRepository.class));
    }

    @Configuration
    static class SameBeanConfig{

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

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

}
  • SameBeanConfig 을 static 으로 만들면서 해당 클래스에만 쓴다고 만든다
  • NoUniqueBeanDefinitionException 같은타입이 2개가 있을때 발생하는 오류

타입으로 조회시 같은 타입이 둘 이상 있으면 , 빈 이름으로 지정한다

public class ApplicationContextSameBeanFindTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

    @Test
    @DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면 , 빈 이름으로 지정한다")
    void findBeanByName(){
        MemberRepository memberRepository=ac.getBean("memberRepository1",MemberRepository.class);
        assertThat(memberRepository).isInstanceOf(MemberRepository.class);
    }

    @Configuration
    static class SameBeanConfig{

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

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

}
  • 같은 타입이 있을 경우 빈이름으로 조회

특정 타입 모두 조회

public class ApplicationContextSameBeanFindTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

    @Test
    @DisplayName("특정 타입 모두 조회")
    void findBeanByType(){
        Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
        for (String s : beansOfType.keySet()) {
            System.out.println("s = " + s + " value =" + beansOfType.get(s));
        }
        System.out.println("beansOfType = " + beansOfType);
        assertThat(beansOfType.size()).isEqualTo(2);
    }

    @Configuration
    static class SameBeanConfig{

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

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

}
  • getBeansOfType 을 이용하면 Map 에 key,value 로 등록된다
  • beansOfType 을 호출하면서 모두 조회된것을 알수 있다

5.스프링빈 조회(상속관계)

  • 부모타입으로 조회하면 자식타입도 함께 조회된다
  • 자바의 최상위 부모는 Object 를 조회하면 모든스프링 빈 조회

부모타입 Object

public class ApplicationContextExtendsFindTest {

    AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext(TestConfig.class);

    @Test
    @DisplayName("부모타입 Object")
    void findAllBeanByObjectType(){
        Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
        for (String key : beansOfType.keySet()) {
            System.out.println("key = " + key + " value" + beansOfType.get(key));
        }

    }

    @Configuration
    static class TestConfig{

        @Bean
        public DiscountPolicy rateDiscountPolicy(){
            return new RateDiscountPolicy();
        }

        @Bean
        public DiscountPolicy fixDiscountPolicy(){
            return new FixDiscountPolicy();
        }

    }

}
  • object 조회시 스프링의 모든 빈이 조회된다

6.BeanFactory 와 ApplicationContext

image

  • BeanFactory 는 스프링 컨테이너에 최상위 인터페이스
  • BeanFactory 는 getBean 을 제공한다
  • ApplicationContext 는 애플리케이션할때 빈을 관리하고 조회기능등등 부가기능이 추가되었다.
  • 부가기능 - 언어변환,환경변수,이벤트,리소스조회..

정리

  • ApplicationContext 는 BeanFactory 기능을 상속받는다
  • ApplicationContext 는 빈기능 + 편리한 부가기능
  • BeanFactory , ApplicationContext 를 스프링 컨테이너라고 한다.

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

image

  • 자바코드 , xml , Groovy 설정가능
  • 자바코드설정
    • AnnotationConfigApplicationContext 를 이용해서 설정
  • xml
    • GenericXmlApplicationContext 를 이용
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="memberService" class="hello.Study_Spring.member.MemberServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository" />
    </bean>

    <bean id="memberRepository"
          class="hello.Study_Spring.member.MemoryMemberRepository" />

    <bean id="orderService" class="hello.Study_Spring.order.OrderServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository" />
        <constructor-arg name="discountPolicy" ref="discountPolicy" />
    </bean>

    <bean id="discountPolicy"
          class="hello.Study_Spring.discount.RateDiscountPolicy" />
</beans>
@Configuration
public class AppConfig {

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

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

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

    @Bean
    public DiscountPolicy discountPolicy() {
        //return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }

}
  • 두개는 똑같은 코드이다
public class XmlAppContext {

    @Test
    void xmlAppContext(){
        ApplicationContext ac =new GenericXmlApplicationContext("appConfig.xml");
        MemberService memberService = ac.getBean("memberService",MemberService.class);
        assertThat(memberService).isInstanceOf(MemberService.class);
    }

}
  • GenericXmlApplicationContext 을 이용해서 xml 을 불러온다
  • 컴파일 없이 빈설정 정보를 변경할수 있는 장점이 있다

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

  • 스프링이 이렇게 다양한 설정 형식을 지원하는 중심은 BeanDefinition 가 있다
  • 역할과 구현을 나눈것
    • 역할 - BeanDefinition
    • 구현 - xml,자바코드
  • BeanDefinition 을 빈 설정메타라 한다
  • 스프링컨테이너는 BeanDefinition 메타정보를 기반으로 스프링빈 생성
    image
  • 스프링 컨테이너는 BeanDefinition 만 의존한다
  • 자바코드
    • AnnotatedBeanDefinitionReader 를 이용해서 BeanDefinition 을 만든다
  • Xml
    • XmlBeanDefinitionReader 를 이용해서 BeanDefinition 을 만든다

beanDefinitionTest

public class beanDefinitionTest {

    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){
                System.out.println("beanDefinitionName = " + beanDefinitionName+
                        " beanDefinition = "+ beanDefinition);
            }

        }
    }

}
  • beanDefinition
    • BeanClassName : 생성할 빈 클래스 이름
    • factoryBeanName : 팩토리 역할빈 (appConfig)
    • factoryMethodName : 빈을 생성할 팩토리 매서드 (memberService)
    • Scope : 싱글톤(기본값)

정리

  • beanDefinition 을 직접 생성해서 스프링 컨테이너에 등록 할수있다
  • beanDefinition 을 이용해서 스프링의 다양한 형태의 설정 정보를 추상화 해서 사용한다
728x90