Spring Bean의 조회, 등록 그리고 구조
이영한 님의 스프링 핵심원리 - 기본편을 들으며 필기한 것이다.
스프링에 등록된 빈을 조회하는 방법을 정리해두려 한다.
자주 사용하지 않을 것같아서 막상 필요할 때 찾아보기 위한 요약본이다.
1. Bean 조회
import 는 생략한다.
public class ApplicationContextBeanFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
@Test
@DisplayName("이름으로 조회할 수 있다.")
void findBeanByName() {
MemberService memberService = ac.getBean("memberService", MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("이름없이 타입으로 조회할 수 있다.")
void findBeanByName() {
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("구체 타입으로 조회")
void findBeanByName2() {
MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("부모 타입으로 조회시 자식이 두개 이상인경우 예외가 발생한다.")
void findBeanByName() {
assertThrows(NoUniqueBeanDefinitionException.class,
() -> ac.getBean(DiscountPolicy.class));
}
@Test
@DisplayName("부모 타입으로 모두 조회하기 - Object")
void findBeanByObjectType() {
Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
}
//정의된 빈 전체 호출
@Test
@DisplayName("모든 bean 출력하기")
void findAllBean() {
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name = " + beanDefinitionName + "object = " + bean);
}
}
@Test
@DisplayName("application bean 출력하기")
void findApplicationBean() {
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
//Role ROLE_APPLICATION : 직접 등록한 애플리케이션 빈
//Role ROLE_INFRASTRUCTURE: 스프링이 내부에서 사용하는 빈
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name = " + beanDefinitionName + "object = " + bean);
}
}
}
}
Memo
- 스프링 빈은 등록된 "이름"과 "타입"으로 호출할 수 있다.
- 스프링 빈 등록시 이름의 기본값 : 등록한 Configuration 클래스의 매서드명은 빈 이름이 된다.
- @Bean("name2") 형식으로 이름을 직접 지정 할 수 있다.
- 타입으로 호출하는 경우 하위자식들이 모두 호출됨으로 주의해야 한다.
- 예시로 Object.class로 호출하면 모든 스프링 빈이 호출된다.
- 당연하지만 타입호출시 2개 이상이 호출되면 빈 이름 또는 구현채를 명시해서 불러올 수 있다.
Method
- getBeanDefinitionNames() : 모든 빈 이름을 조회한다.
- getBean() : 빈 이름, 타입을 사용해서 객체(인스턴스)를 조회한다.
- getRole() : 사용자가 정의한 빈과 스프링 내부 빈을 구분해볼 수 있다. (코드예시 최하단)
- getBeansOfType() : 특정 타입의 모든 빈을 조회
2. ApplicationContext 의 구조
<<interface>>ApplicationContext 는<<interface>>BeanFactory을 기능을 모두 상속받으며 getBean은 BeanFactory의 기능이다. 이외 다른 부가기능을 포함하고 있기 때문에 ApplicationContext를 주로 사용한다.
EnvironmentCapable : 애플리케이션 환경(Enviroment) 관련 정보 제공
ListableBeanFactory : 등록된 모든 빈을 조회할 수 있는 기능 제공
HierarchicalBeanFactory : 부모-자식 구조의 빈 팩토리 지원
MessageSource : 국제화(i18n) 메세지 처리 지원, 예를 들면 한국에서 들어오면 한국어로 출력
ApplicationEventPublisher : 애플리케이션 이벤트 발행 및 관리
ResoucePatternResolver : 패턴을 사용한 리소스 검색 기능 제공
- BeanFactory, ApplicationContext를 스프링 컨테이너라고 한다.
3. BeanDefinition - 스프링 빈 설정 메타 정보
스프링 빈을 등록하는 방법
xml을 이용하여 직접 등록
GenericXmlApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
appconfig.class 팩토리메서드/팩토리빈을 통해 등록
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
위 코드에서 ApplicationContext로 받은 이유는 "getDefinitionNames()"를 사용하기 위해서다.
https://spring.io/projects/spring-framework
Spring Framework
The Spring Framework provides a comprehensive programming and configuration model for modern Java-based enterprise applications - on any kind of deployment platform. A key element of Spring is infrastructural support at the application level: Spring focuse
spring.io
스프링 컨테이너는 BeanDefinition을 통해서 메타정보를 읽어온다. 각 설정방법에 따라 다른 Reader를 사용할 수 있으며 사용자가 직접 리더를 만들어서 새로운 등록 양식을 만들어 사용할수 있다.