• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

스프링 컨테이너와 스프링 빈 섹터 질문있습니다.

23.11.14 13:52 작성 23.11.15 10:30 수정 조회수 169

0

[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오) 예
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예

[질문 내용]

  1. 스프링 빈 조회 - 기본 1분 19초

ac.getBean("memberService", MemberService.class);

입력하면 자동으로 MemberService memberService=가 자동으로 입력되는데 어떻게 하신건가요?


 

  1.  스프링 빈 조회 - 동일한 타입이 둘 이상 3분 49초

@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상있으면 중복 오류가 발생한다")
void findBeanByTypeDuplicate(){
    MemberRepository bean =ac.getBean(MemoryMemberRepository.class);
}

타입만 지정했다고 하셨는데 이게 무슨 뜻이에요?

 

 3. 스프링 빈 조회 - 동일한 타입이 둘 이상-5분 49초에서

아래와 같이 오류가 나는 위치가 아래인거는 알겠는데 정확히 이유가 뭔가요? 강의에서 아래와 같은 오류를 일부로 유발 시킨 건 알겠는데 클래스 타입이 같아도 에러 안난다고 했는데 왜 에러가 나나요?

@Configuration
static class SameBeanConfig{//static을 쓰는 이유는 ApplicationContextSameBeanFindTest 안에서만 사용하겠다.
    @Bean
        public MemberRepository memberRepository1(){
        return new MemoryMemberRepository();//클래스 타입이 같아도 에러 안남.

    }
    @Bean
    public MemberRepository memberRepository2(){
        return new MemoryMemberRepository();//클래스 타입이 같아도 에러 안남.

    }

}

 

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.core.member.MemoryMemberRepository' available: expected single matching bean but found 2: memberRepository1,memberRepository2

 

  1. 스프링 빈 조회 강의 에서 질문이요.

첫번째 테스트에 부분에 MemberRepository bean =ac.getBean(MemberRepository.class); 이걸 넣으면 아래와 같이 에러가 나는데요. 근데 이거는 bean에 할당하는건데 에러가 날 이유가 없지 않나요? 할당하는 것만으로도 왜 에러가 나는지 궁금합니다.

package hello.core.beanfind;

import hello.core.Appconfig;
import hello.core.discount.DiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertThrows;


public class ApplicationContextSameBeanFindTest {

    AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext(SameBeanConfig.class);

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

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


    @Configuration
    static class SameBeanConfig{//static을 쓰는 이유는 ApplicationContextSameBeanFindTest 안에서만 사용하겠다.
        @Bean
            public MemberRepository memberRepository1(){
            return new MemoryMemberRepository();//클래스 타입이 같아도 에러 안남.

        }
        @Bean
        public MemberRepository memberRepository2(){
            return new MemoryMemberRepository();//클래스 타입이 같아도 에러 안남.

        }

    }

}

 

No qualifying bean of type 'hello.core.member.MemberRepository' available: expected single matching bean but found 2: memberRepository1,memberRepository2

 

 

  1. 스프링 빈 조회 강의에서 8분 20초에 아래처럼 key와 value부분이 나뉘어져있는데 키는 메소드 이름인것 같은데 벨류는 뭔가요?

  1. 스프링 빈 조회 - 상속 관계 3분까지 따라 쳤는데 실수로 제가 bean을 한개만 등록했는데 실행해보니 아래와 같은 에러가 나옵니다. 이거는 예외 상황이 발생하지 않아서 오류가 생긴걸로 이해해도 되나요?

package hello.core.beanfind;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import static org.junit.jupiter.api.Assertions.assertThrows;

public class ApplicationContextExtendsFindTest {
    AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext(TestConfig.class);

    @Test
    @DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 중복 오류가 발생한다. 빈 이름을 지정하면 된다")
    void findBeanByParentTypeDuplicate(){
//        DiscountPolicy bean=ac.getBean(DiscountPolicy.class);
        assertThrows(NoUniqueBeanDefinitionException.class,()->ac.getBean(DiscountPolicy.class));
    }
    @Configuration
    static class TestConfig{
        @Bean
        public DiscountPolicy rateDiscountPolicy(){
            return new RateDiscountPolicy();
        }
    }


}

에러사항: org.opentest4j.AssertionFailedError: Expected org.springframework.beans.factory.NoUniqueBeanDefinitionException to be thrown, but nothing was thrown.

 

  1. 스프링 빈 조회 - 상속 관계 6분

하위 타입으로 조회한다는것이 class TestConfig 에서 return new RateDiscountPolicy(); 이부분과 이름이 같은걸로 조회한다는걸로 이해해도 되나요?

@Test
@DisplayName("특정 하위 타입으로 조회")//안좋은 방법
void findBeanBySubType(){
    RateDiscountPolicy bean=ac.getBean(RateDiscountPolicy.class);
    assertThat(bean).isInstanceOf(RateDiscountPolicy.class);
}


@Configuration
static class TestConfig{
    @Bean
    public DiscountPolicy rateDiscountPolicy(){
        return new RateDiscountPolicy();
    }

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

 

  1. 스프링 빈 조회 - 상속 관계 8분

@Test
@DisplayName("특정 하위 타입으로 조회")//안좋은 방법
void findBeanBySubType(){
    RateDiscountPolicy bean=ac.getBean(RateDiscountPolicy.class);
    assertThat(bean).isInstanceOf(RateDiscountPolicy.class);
}
assertThat(bean).isInstanceOf(RateDiscountPolicy.class);

위 코드에서 assert 부분이 아래와 같은 의미를 갖는거 같은데 인스턴스 검사는 왜하는거에요?

rateDiscountPolicyRateDiscountPolicy 클래스의 인스턴스인지를 검사합니다.

 

  1. 스프링 빈 설정 메타 정보 마지막부분에서 아래처럼 말씀하셨는데요.

스프링이 다양한 형태의 설정 정보를 BeanDefinition으로 추상화해서 사용하는 것 정도만 이해하면 된다.

추상화라는게 아래 사진처럼 나열한다는것으로 이해하면되는건가요?

  1. (위 질문 사항에서 추가 질문)빈을 직접 등록한다는게 강의에서 appconfig파일에 나와있는것처럼 직접 등록하는건가요?

public class Appconfig {
    @Bean
    public MemberService memberService(){
        return new MemberServiceImpl(memberRepository());
        //멤버서비스임플을 만들고 MemoryMemberRepository()만들어서 주입을 시켜줄꺼야.
    }

    @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();
    }// 디스카운트 바꾸면 여기만 바뀌면 됨됨
}

 

11.컨테이너에 등록된 모든 빈 조회
5분 34초에 보통

Object bean = ac.getBean(beanDefinitionName);
이부분 Object object 로 설정하는데 bean으로 다시 설정한 이유가 있나요? 아래는 전체 코드입니다.
    @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);
            }
        }
    }
}

답변 1

답변을 작성해보세요.

0

y2gcoder님의 프로필

y2gcoder

2023.11.16

안녕하세요. ekek님, 공식 서포터즈 y2gcoder입니다.

하나씩 혹은 묶어서 답변드려보겠습니다 :)

  1. 단축키를 사용하셨습니다! cmd + option + v 혹은 control + alt + v 를 눌러보십쇼!

  2. 적어주신 코드에서 getBean 메서드는 파라미터를 다양한 형태로 받을 수 있습니다. 빈으로 등록할 때의 이름만 받거나, 해당 빈 타입을 받거나 둘다 받을 수도 있습니다. 여기서는 그 클래스 타입을 기준으로 해당 빈을 조회해오는 것입니다!

  3. 4번과 함께 답변을 드리겠습니다. 3번과 4번의 오류를 읽어보시면 해당 빈 타입으로는 하나만 등록되어야 할 것 같은데 2개가 등록되었다고 합니다.
    ac.getBean(MemberRepository.class)
    로 빈을 조회하고 있는데 해당 타입은 인터페이스 타입입니다! 그리고 해당 인터페이스를 구현한 MemoryMemberRepository 타입의 빈을 memberRepository1, memberRepository2 라는 이름으로 빈으로 등록하고 있습니다. 그리고 위의 메서드로 조회하고 있는 상황입니다. 하나를 찾아오려고 하는데 해당 인터페이스 타입으로 할당할 수 있는 빈이 2개나 있으니 발생하는 에러입니다. 그리고 4번에서 첫번째 테스트 코드 안의 첫줄은 지워주십쇼! 뒤의 ac.getBean(MemberRepository.class) 시 예외가 발생할 것을 검증하는 assertThrows로 감싸주지 않으면 에러가 발생하면서 테스트가 실패로 끝납니다. 예외가 발생하는 것을 검증하는 부분에서는 예외가 발생하는 부분을 assertThrows와 같은 메서드로 감싸줘야 합니다!

  4. 3번 답변을 참고해주세요!

  5. key는 빈으로 등록할 때의 이름(@Bean 사용시 메서드 이름, @Component 사용시 해당 클래스의 앞글자만 소문자로 변경이 기본입니다.) value는 실제로 빈으로 등록된 인스턴스 입니다~

  6. 맞습니다!

  7. 여기도 2번과 비슷한 맥락입니다. 파라미터로 빈 이름이 아니라 클래스 타입을 넣고 있습니다. 클래스 타입으로 등록된 빈이 있는지 조회하고 있으면 해당 빈을 반환하는 것입니다~

  8. 해당 빈 타입으로 조회해온 빈이 있다는 것에 대해 테스트 코드에서 제공하는 검증 코드로 검증하는 것입니다. 저희가 테스트 코드를 짜는 목적은 개발자가 직접 눈으로 보기 위해서만이 아니라 테스트를 기계적으로 검증하기 위한 것도 있습니다. 이는 테스트 코드를 작성하는 이유에 대해 직접 찾아보시면 더 이해가 되실 것 같습니다 :)

  9. 네 그정도로만 이해하셔도 됩니다!

  10. 네 그렇게 이해해주시면 감사하겠습니다!

  11. 해당 경우에는 등록된 빈들을 전부다 가져오는 것인데 등록된 빈들은 타입이 다 다를 것입니다. 그래서 모든 클래스의 상위인 Object 를 반환타입으로 해주는 것입니다.

 

감사합니다.