스프링 컨테이너
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
위의 코드가 스프링 컨테이너를 생성하는 코드이다.
여기서, ApplicationContext를 스프링 컨테이너라고 한다. ( 이는 인터페이스로 구성됨 )
스프링 컨테이너는 XML이나 애노테이션 기반 자바 설정 클래스로 만들 수 있다.
(여기서는 자바 설정 클래스를 기반으로 스프링 컨테이너를 만들 것이다.)
즉, 위의 코드를 통해 ac는 ApplicationContext 인터페이스의 구현체가 된다.
[ 엄밀히 말하자면, 스프링 컨테이너를 부를 때는 BeanFactoy, ApplicationContext를 구분해서 이야기한다.
그러나, BeanFactory를 직접 사용하는 경우는 거의 없으므로 ApplicationContext를 스프링 컨테이너라고 하겠다. ]
AppConfig.class 생성
위에서 말한 코드에서 AppConfig.class 가 뭔지 몰랐을 것이다. 이를 생성해보자.
스프링 컨테이너를 생성할 때는 구성 정보를 지정해주어야 한다.
여기서 AppConfig.class 를 구성 정보로 지정했다.
내가 만든 인터페이스, 구현체들을 AppConfig를 통해서 스프링 빈으로 등록하고 파라미터 및 DI를 설정해준다.
이를 스프링 컨테이너에 등록하는 것이다.
( 빈 이름은 항상 다른 이름을 부여해야 함. )
코드를 보면 알 수 있듯이, 해당 의존 관계는 위와 같다.
이렇게 자바 코드로 스프링 빈을 등록하면 생성자를 호출하면서 DI도 한번에 처리된다.
컨테이너 내 모든 빈 조회
스프링 컨테이너에 실제 스프링 빈이 잘 등록되었는지 코드를 통해 확인해보자.
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);
}
}
// @DisplayName을 통해 Test 메서드의 이름을 실행창에서 바꿔서 보여줌 ( findAllBean -> 모든 빈 출력하기 )
ac.getBeanDefinitionNames() : 스프링에 등록된 모든 빈 이름을 조회한다.
ac.getBean() : 빈 이름으로 빈 객체(인스턴스)를 조회한다.
해당 코드를 실행하면, 스프링이 내부에서 사용하는 빈까지 다 출력됨.
내가 등록한 빈만 보고싶다면 아래의 코드를 실행해보자.
@DisplayName("애플리케이션 빈 출력하기")
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);
}
}
//getRole()로 구분할 수 있다. Role_APPLICATION을 사용하면 된다.
스프링 빈 조회
스프링 컨테이너에서 스프링 빈을 찾는 가장 기본적인 방법
ac.getBean( 빈이름, 타입 )
ac.getBean( 타입 )
( 조회 대상 스프링 빈(xxxxx)이 없으면 예외 발생
→ NoSuchBeanDefinitionException: No bean named 'xxxxx' available )
때문에 빈 이름으로 조회가 가능하고 이름 없이 타입만으로도 조회가 가능하다.
( 타입 조회 : 인터페이스 타입 말고 구체 타입만으로 조회가 가능한데 이는 유지보수에 유연성이 떨어진다. )
그렇다면, 같은 타입의 빈이 둘 이상 있으면 어떻게 될까?
→ 중복 오류가 발생한다. ( NoUniqueBeanDefinitionException )
이를 해결하기 위해선, 빈 이름을 지정하면 된다.
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 빈 이름을 지정하면 된다")
void findBeanByName() {
MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}
// Config에서 memberRepository1,2를 같은 타입인 MemoryMemberRepository로 객체(스프링 빈)를 만들어 놨음.
스프링 빈 조회 시에 상속 관계를 잘 알아야 한다.
왜냐하면, 부모 타입으로 조회 시에 자식이 둘 이상 있으면 중복 오류가 발생하기 때문이다.
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 중복 오류가 발생한다")
void findBeanByParentTypeDuplicate() {
assertThrows(NoUniqueBeanDefinitionException.class, () -> ac.getBean(DiscountPolicy.class));
}
//DiscountPolicy 인터페이스 아래로 RateDiscountPolicy, FixDiscountPolicy 구현체가 자식으로 있음.
BeanFactory와 ApplicationContext
스프링 컨테이너의 상속 관계이다.
BeanFactory가 스프링 컨테이너의 최상위 인터페이스이다.
스프링 빈을 관리하고 조회하는 역할을 한다. ( getBean()을 제공 )
그렇다면, ApplicationContext와 BeanFactory의 차이는 뭘까?
애플리케이션을 개발할 때는 빈을 관리하고 조회하는 기능은 물론이고, 수 많은 부가기능이 필요하다.
제공하는 부가 기능이 이렇게나 더 있다.
메세지 소스를 활용한 국제화 기능, 환경 변수, 애플리케이션 이벤트, 편리한 리소스 조회 등등..
때문에, 위에서 말햇듯이 BeanFactory를 직접 사용할 일은 많이 없다. 부가 기능이 추가된 ApplicationContext를 사용하면 된다.
정리
빈들의 의존관계 및 등록을 설정하는 Configuration 클래스를 만들어서 이를 스프링 컨테이너로 보낸다.
ApplicationContext 스프링 컨테이너를 통해서 빈을 조회할 수 있다.
조회 시에는 getBean()을 통해 빈 이름, 타입 으로 조회할 수 있다.
다음 시간에는, 싱글톤 패턴과 싱글톤 패턴의 일부인 싱글톤 컨테이너에 대해서 작성해보겠다.
'Spring' 카테고리의 다른 글
컴포넌트 스캔 (@ComponentScan) (0) | 2023.08.02 |
---|---|
싱글톤 패턴, 싱글톤 컨테이너 (Singleton) (0) | 2023.08.02 |
Spring Basic- Bean Scope(웹 스코프) (0) | 2023.06.10 |
Spring Basic- Bean Scope(프로토타입 스코프) (0) | 2023.06.09 |
Spring Basic- Bean LifeCycle CallBack (0) | 2023.05.24 |