컨테이너에 등록된 모든 빈 조회
package hello.core.beanfind;
import hello.core.AppConfig;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ApplicationContextInfoTest {
//스프링 컨테이너 불러옴
//구성 정보는 AppConfig에 있다.
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("모든 빈 출력하기")
void findAllBean(){
//스프링 빈 이름들을 String으로 빼냄
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name = " + beanDefinitionName + " Object = " + bean );
}
}
//스프링에 등록된 모든 빈정보가 나옴 (스프링 내부에서 사용하는 빈까지 다 나옴)
@Test
@DisplayName("애플리케이션 빈 출력하기")
void findApplicationBean(){
//스프링 빈 이름들을 String으로 빼냄
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
//BeanDefinition => 빈에대한 정보들, 정보들 가져옴
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
//role_appliction과 role_infrastructure(내부에서 사용하는 빈)가 있음
//역할로 걸러서 출력함
if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name = " + beanDefinitionName + " Object = " + bean);
}
}
}
}
스프링 빈 조회 - 기본
스프링 컨테이너에서 스프링 빈을 찾는 가장 기본적인 조회방법은
ac.getBean(빈이름, 타입)
ac.getBean(타입) => 이름 생략가능
만약 조회 대상이 없을 경우엔 예외가 발생한다.
NoSuchBeanDefinitionException: No bean named "xxxxx" available
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.assertThrows;
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);
//isInstanceOf => 어떤 클래스인가?
}
@Test
@DisplayName("이름 없이 빈 타입으로 조회")
void findBeanByType(){
//인터페이스로 조회하면 알아서 사용하는 구현체(스프링 빈에 등록된)가 조회된다.
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
//isInstanceOf => 어떤 클래스인가?
}
@Test
@DisplayName("구체 타입으로 조회")
void findByName2(){
//구현체에 의존하게 된다.....
MemberService memberService = ac.getBean(MemberServiceImpl.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
//isInstanceOf => 어떤 클래스인가?
}
//조회가 안되는 경우도 테스트 해봐야해!!
@Test
@DisplayName("빈 이름으로 조회X")
void findByNameX(){
//MemberService memberService = ac.getBean("XXX", MemberService.class);
//조회가 안되면 NoSuchBeanDefinitionException 터친다.
//Junit Assertion의 assertThrows를 사용해서 예외를 확인 해야한다.
assertThrows(NoSuchBeanDefinitionException.class, ()->ac.getBean("XXX", MemberService.class));
}
}
**참고 assertThrows
org.junit.jupiter.api.Assertions.assertThrows(
NoSuchBeanDefinitionException.class, // 발생이 예상되는 예외의 타입
()-> ac.getBean("xxxx", MemberService.class) // 예외가 발생될 수 있는 코드 블록
);
/*
.
먼저 해당 테스트 메소드는 존재하지 않는 빈의 이름으로 빈을 가져오려고 할 때 예외가 발생되는 상황을 테스트 하기 위한 메소드입니다.
ac.getBean("xxxx", MemberService.class); 해당 문장을 실행하면 존재하지 않는 빈의 이름(xxxx)으로 빈을 꺼내오려고 할 것입니다. 그러나 당연히 xxxx라는 이름으로 등록된 빈이 없기 때문에 NoSuchBeanDefinitionException 예외가 발생합니다.
.
assertThrows 메소드는 발생이 예상되는 예외의 타입, 예외가 발생될 수 있는 코드 블록을 파라미터로 받아서 실행됩니다.
이 때, assertThrows 내부에서는 예외가 발생될 수 있는 코드 블록을 실행합니다. 만약 해당 코드 블록을 실행 중 예외가 발생한다면 발생된 예외가 발생이 예상되는 예외의 타입과 일치하는지 아닌지 확인합니다. 이때 발생된 예외 타입과 예상되는 예외의 타입이 일치하면 테스트는 성공으로 처리됩니다.
.
아래 코드는 assertThrows의 내부 동작입니다. try 블록 안에서 코드를 실행하여 예외 발생시 catch 내에서 발생한 예외 타입과 예상되는 예외 타입을 비교하고 있습니다.
*/
스프링 빈 조회 - 동일한 타입이 둘 이상
타입으로 조회시 같은 타입의 스프링 빈이 둘 이상이면 오류 발생!!!!
이때는 빈 이름을 지정하자
ac.getBeansOfType()을 사용하면 해당 타입의 모든 빈을 조회할 수 있다.
package hello.core.beanfind;
import hello.core.discount.DiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;
import org.junit.jupiter.api.Assertions;
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 java.util.Map;
//타입 으로 조회시 같은 타입이 둘 이상이면...
// 테스트를 위해 간단한 설정 클래스 만듬
public class ApplicationContextSameBeanFindTest {
//테스트를 위한 sameBean 설정 정보를 가져옴
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상이면 중복오류 발생")
void findBeanTypeDuplicate(){
//먼저 어떤 예외를 던지는지 확인해본다.
//MemberRepository bean = ac.getBean(MemberRepository.class);
/*
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.core.member.MemberRepository' available: expected single matching bean but found 2: memberRepository1,memberRepository2
NoUniqueBeanDefinitionException를 던진다.
*/
Assertions.assertThrows(NoUniqueBeanDefinitionException.class, ()->ac.getBean(MemberRepository.class));
}
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 빈 이름을 지정하면 된다.")
void findBeanByName(){
//설정클래스에 메소드 둘다 MemberRepository이다.따라서 이름을 같이 지정
MemberRepository bean = ac.getBean("memberRepository1", MemberRepository.class);
org.assertj.core.api.Assertions.assertThat(bean).isInstanceOf(MemberRepository.class);
}
//특정 타입 모두 조회 => 둘 다 꺼내고 싶음
//getBeansOfType 사용하면 된다.
//MemberRepository형 모두 꺼냄
@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanType(){
//getBeansOfType은 Map형으로 반환함 <이름, 타입>
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
for (String key : beansOfType.keySet()) {
System.out.println("key = " + key);
}
System.out.println("beansOfType = " + beansOfType);
org.assertj.core.api.Assertions.assertThat(beansOfType.size()).isEqualTo(2);
//해당 타입의 개수가 2개이므로 2개 다 꺼내졌는지 확인
}
//구성 정보이므로 어노테이션 붙임
//메서드 명은 다르지만, 반환하는 타입은 같음
@Configuration
static class SameBeanConfig {
@Bean
public MemberRepository memberRepository1(){
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2(){
return new MemoryMemberRepository();
}
}
}
스프링 빈 조회 - 상속관계
부모타입으로 조회하면 자식타입도 함께 조회된다.
예를 들어 Object타입으로 조회시 모든 스프링빈을 조회하게 된다.
테스트코드로 확인해보자
package hello.core.beanfind;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import java.util.Map;
public class ApplicationContextExtendsFindTest {
//스프링 컨테이너
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회시 자식이 둘 이상 있으면 중복 오류")
void findBeanByParentTypeDuplicate(){
//DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
//->NoUniqueBeanDefinitionException
Assertions.assertThrows(NoUniqueBeanDefinitionException.class, ()->ac.getBean(DiscountPolicy.class));
}
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 빈 이름 지정해서 쓰면댐")
void findBeanByParentTypeBeanName(){
DiscountPolicy bean = ac.getBean("fixDiscountPolicy", DiscountPolicy.class);
org.assertj.core.api.Assertions.assertThat(bean).isInstanceOf(FixDiscountPolicy.class);
}
@Test
@DisplayName("특정 하위 타입으로 조회")
void findBeansBySubType(){
RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
org.assertj.core.api.Assertions.assertThat(bean).isInstanceOf(RateDiscountPolicy.class);
}
@Test
@DisplayName("부모 타입으로 다 조회")
void findBeansByParentType1(){
Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
for (String key : beansOfType.keySet()) {
System.out.println("key + "+ key + "value = "+ beansOfType.get(key));
}
}
@Test
@DisplayName("부모 타입으로 다 조회-object")
void findBeansByParentType2(){
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();
}
}
}
직접 getBean할 경우는 잘 없음!!
개발하면서 애플리케이션 컨텍스트에서 빈을 조회할 일이 거의 없음.
하지만 기본기능이고, 가끔 순수 자바애플리케이션에서 스프링컨테이너를 생성해서 쓸때 사용한다.
BeanFactory와 ApplicationContext
ApplicationContext, AnnotationConfig(구현클래스) ---> ApplicationContext(인터페이스) ---> BeanFactory(인터페이스)
BeanFactory
스프링 컨테이너의 최상위 인터페이스.
스프링 빈을 관리, 조회하는 역할 담당, 예시로 getBean을 제공해줌
이전 코드들에서 대부분 사용한 기능은 BeanFactory가 제공하는 기능임,
Then, 왜 BeanFactory가 아닌 ApplicationContext를 사용했을까??
ApplicationContext
BeanFactory 기능을 모두 상속받아서 제공한다.
차이점은, 애플리케이션 개발시, 빈은 관리하고 조회하는 기능이외에 수많은 기능들이 필요하다.
예를 들면 ApplicationContext가 구현하는 인터페이스들을 보면
MessageSource = 메시지 소스를 활용한 국제화기능(한국에서 들어오면 한국어, 외국에서 들어오면 영어)
EnvironmnetCapable = 환경변수이다. 로컬, 개발, 운영등 구분해서 처리 (예를 들면 로컬 개발환경, 테스트서버 개발환경, 실제운영환경 등등 사용하는 DB가 다르다면? 맞게 설정)
ApplicationEventPublisher = 애플리케이션 이벤트이다. 이벤트를 발행하고 구독하는 모델을 편리하게 지원
ResourceLoader = 편리한 리소스 조회. 파일, 클래스패스,외부 등에서 리소스를 편리하게 조회
일단 이런게 있다 정도만 알고 가자...
정리하면 ApplicationContext는 BeanFactory 기능들에 편리한 기능을 추가한것이다. 따라서 BeanFactory는 거의 잘 안쓴다.
'공부 > Spring_2-스프링 핵심 원리 이해1' 카테고리의 다른 글
스프링 핵심 원리 이해1_6 - 싱글톤 컨테이너 (0) | 2022.04.07 |
---|---|
스프링 핵심원리 이해1_5 - 다양한 설정 형식 지원(자바코드, xml) (0) | 2022.04.07 |
스프링 핵심원리 이해1_3 - 스프링으로 전환(AppConfig 기반) (0) | 2022.04.07 |
스프링 핵심원리 이해1_2 - 새로운 할인정책 개발, AppConfig (0) | 2022.04.07 |
스프링 핵심 원리 이해1_1 - 회원, 주문, 할인정책 (0) | 2022.04.07 |