※ 본문은 김영한 선생님의 인프런 '스프링 핵심 원리 - 기본편' 강의를 듣고 정리한 내용임을 알립니다.
▶ 스프링 컨테이너
- ApplicationContext : 스프링 컨테이너이자 인터페이스
- @Configuration이 붙은 AppConfig와 같은 클래스를 설정(구성) 정보로 사용
- @Configuration이 붙은 클래스 내에 @Bean이 적힌 메소드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록 → 이렇게 등록된 객체를 스프링 빈이라 부름 (※ 스프링 빈의 이름은 @Bean이 붙은 메소드의 이름)
- 이후 applicationContext.getBean()과 같은 메소드를 사용해서 스프링 빈을 찾을 수 있음
▶ 스프링 빈 조회
- 조회하는 코드 : ac.getBean(빈 이름, 타입) / ac.getBean(타입)
- ac.getBeansOfType()을 사용하면 해당 타입의 모든 빈 조회 (※ 타입은 쉽게 말해서 클래스)
- 타입으로 조회 시 같은 타입의 스프링 빈이 둘 이상이면 오류가 발생 (빈 이름 지정할 것)
- 조회 대상 스프링 빈이 없으면 NoSuchBeanDefinitionException: No bean named 'xxxx' available 발생
- 상속관계 조회 : 부모 타입으로 조회하면 모든 자식을 함께 조회
다음 코드 블록은 Bean을 조회하는 방법의 예시이다.
public class ApplicationContextExtendsFindTest {
AnnotaionConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회 시, 자식이 둘 이상 있으면 중복 오류가 발생한다")
void findBeanByParentTypeDuplicate() {
assertThrows(NoUniqueBeanDefinitionException.class,
() -> ac.getBean(DiscountPolicy.class));
}
@Test
@DisplayName("부모 타입으로 조회 시, 자식이 둘 이상 있으면 빈 이름을 지정한다")
void findBeanByParentTypeBeanName() {
DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy", DiscountPolicy.class);
assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}
@Test
@DisplayName("특정 하위 타입으로 조회")
void findBeanBySubType() {
RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
assertThat(bean).isInstanceOf(RateDiscountPolicy.class);
}
@Test
@DisplayName("부모 타입으로 모두 조회")
void findAllByBeanByParentType() {
Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
assertThat(beansOfType.size()).isEqualTo(2);
for (String key : beansOfType.keySet()) {
System.out.println("key = " + key + "value = " + beansOfType.get(key));
}
}
@Test
@DisplayName("부모 타입으로 모두 조회 - Object")
void findAllBeanByObject() {
Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
for (String key : beansOfType.keySet()) {
System.out.println("key = " + key + "value = " + beansOfType.get(key));
}
}
}
★ 실무에서 개발할 때 빈을 조회할 일은 거의 없지만(가끔 순수 자바 애플리케이션에서 컨테이너를 쓸 때 정도)
사용하는 방법은 꼭 숙지해두고 있어야 한다.
▶ BeanFactory
- 스프링 컨테이너의 최상위 인터페이스
- 스프링 빈을 관리하고 조회하는 역할
▶ ApplicationContext
- BeanFactory의 기능을 모두 상속받아서 사용
- 메세지 소스를 활용한 국제화 기능 : 사용자가 선택한 국적 언어에 따라 텍스트 변경
- 환경변수 : 로컬 / 개발(테스트 서버) / 운영 등을 구분해서 처리
- 애플리케이션 이벤트 : 이벤트를 발행하고 구독하는 모델을 편리하게 지원
- BeanFactory를 직접 사용하기 보다는 부가기능이 포함된 ApplicationContext를 주로 사용함
★ BeanFactory나 ApplicationContext를 스프링 컨테이너라 함
▶ 스프링 빈 설정 메타 정보 - BeanDefinition
- '스프링은 어떻게 다양한 설정 형식(자바코드, XML 등)을 지원하는가?'에 대한 답변
- 스프링 컨테이너는 설정 형식이 자바 코드인지 XML인지 알 필요 없이 BeanDefintion만 알면 된다.
- 쉽게 이야기해서 역할과 구현을 개념적으로 나눈 것
- BeanClassName : 생성할 빈의 클래스 명 (자바 설정처럼 팩토리 역할의 빈을 사용하면 없음)
- factoryBeanName : 팩토리 역할의 빈을 사용할 경우의 이름 ex) appConfig
- Scope : 싱글톤(기본값)
- lazyInit : 스프링 컨테이너를 생성할 때 빈을 생성하는 것이 아니라, 실제 빈을 사용할 때 까지 최대한 생성을 지연처리하는지 여부
- InitMethodName : 빈을 생성하고 의존관계를 적용한 뒤에 호출되는 메소드 명
- DestroyMethodName : 빈의 생명주기가 끝나서 제거하기 직전에 호출되는 메소드 명
- Constructor argument, Properties : 의존관계 주입에서 사용 (자바 설정처럼 팩토리 역할의 빈을 사용하면 없음)
BeanDefinition의 정보는 아래의 코드 예시와 같은 방법으로 확인할 수 있다.
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)
}
}
}
}
'JVM > Spring' 카테고리의 다른 글
ComponentScan과 의존관계 주입 방법에 대해서 (0) | 2021.03.24 |
---|---|
싱글톤 컨테이너 (0) | 2021.03.24 |
AppConfig와 IoC, DI에 관하여 (0) | 2021.03.22 |
Spring이란 무엇인가 (0) | 2021.03.16 |
김영한 선생님의 스프링 입문 강의를 공부하고서 (0) | 2021.03.12 |