Spring Core - 스프링 컨테이너 생성과 Bean 등록, 의존성 주입

목차

스프링 컨테이너 생성 과정과 Bean 등록, 의존성 주입

스프링 실행시 스프링 컨테이너가 생성되면 전달 받은 설정 정보를 이용해 스프링 빈을 생성 및 등록 합니다. 빈 등록이 완료된 후에는 설정 정보를 참고해 의존관계를 주입합니다.

이렇게 스프링은 빈을 생성하고 의존관계를 주입하는 관계가 나눠져 있습니다. 하지만 자바 코드로 빈을 등록하게 되면 객체의 생성자를 호출하면서 의존관계 주입까지 한번에 이뤄지게 됩니다.

1. 스프링 컨테이너 생성

스프링 컨테이너를 생성할 때는 구성 정보를 지정해주어야 합니다. 구성 정보는 자바 클래스나 xml 을 이용해 정의할 수 있습니다.

스프링 컨테이너 생성시 설정 정보를 이용해 스프링 컨테이너를 생성합니다. 아래에서는 자바 클래스 AppConfig.class 를 설정 정보로 이용해 스프링 컨테이너를 생성합니다.

//스프링 컨테이너 생성
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

2. 스프링 빈 등록

스프링 컨테이너는 파라미터로 넘어온 설정 클래스 정보를 사용해서 스프링 빈을 등록한다. 아래에서는 AppConfig.class 에 정의된 객체를 Bean 으로 등록합니다.

빈 이름은 메소드 이름으로 등록합니다. @Bean("Bean 이름") 형식으로 직접 빈 이름을 등록할 수 있습니다. 빈 이름을 직접 등록할때 중복이 생기면 프로그램 실행시 오류가 발생합니다.

3. 스프링 빈 의존관계 설정 - 준비

4. 스프링 빈 의존관계 설정 - 완료

등록된 모든 빈 확인

ApplicationContext 에 등록된 모든 Bean 을 조회하고 싶을 경우 ApplicationContext 객체의 getBeanDefinitionNames 메소드를 사용하면 됩니다.

@Test
@DisplayName("모든 빈 출력하기")
void findAllBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();

for(String beanDefinitionName : beanDefinitionNames){
Object bean = ac.getBean(beanDefinitionName);
System.out.println("bean = " + beanDefinitionName + " 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);
}
}
}

컨테이너에 등록된 Bean 조회

컨테이너에 등록된 Bean 조회시 getBean 메소드를 사용합니다.

ApplicationContext 는 등록된 Bean 을 가져와 사용할 수 있게 getBean 메소드를 제공합니다. getBean 메소드를 통해 Bean 정보를 방법으로는 객체타입으로 조회하는 방법, 객체타입과 Bean 이름으로 조회하는 방법, 구체 객체타입으로 조회하는 방법이 있습니다.

@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName(){
MemberService memberService = ac.getBean("memberService", MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

@Test
@DisplayName("이름없이 타입으로 조회")
void findBeanByType(){
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

@Test
@DisplayName("구체 타입으로 조회")
void findBeanByType2(){
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
// 구체적으로 적는 것은 별로 않좋다.
// 될 수 있는한 추상화한 인터페이스에 테스트를 맞춘다.
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

@Test
@DisplayName("빈 이름으로 조회X")
void findBeanByNameX(){
assertThatThrownBy(()->{
MemberService xxxxx = ac.getBean("xxxxx", MemberService.class);
}).isInstanceOf(NoSuchBeanDefinitionException.class);
}

Bean 의 동일한 타입이 둘 이상인 경우

같은 타입으로 등록된 빈이 여러개 있을 경우 getBean 을 이용해 Bean 을 가져오게 되면 NoUniqueBeanDefinitionException 에러가 발생하게 됩니다. 이때는 특정 Bean 을 가져올 수 있도록 등록된 Bean 이름 까지 같이 지정해주면 에러를 해결할 수 있습니다.

또한, ApplicationContext 에 특정 타입으로 등록된 모든 Bean 목록을 조회 하고 싶을 경우 ApplicationContext 객체의 getBeansOfType 메소드를 사용하면 됩니다.

같은 타입으로 여러개의 Bean 등록

memberRepository1 와 memberRepository2 가 ApplicationContext 에 MemberRepository 타입 Bean 으로 등록됩니다.

public class ApplicationContextExtendsFindTest {

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

@Configuration
static class SameBeanConfig {
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}

@Bean
public MemberRepository memberRepository2() {
return new MemoryMemberRepository();
}
}
}

같은 타입으로 여러개의 Bean 조회

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

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

@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType(){
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
for(String key : beansOfType.keySet()){
System.out.println("key = " + key + " value =" + beansOfType.get(key));
}

System.out.println("beansOfType = " + beansOfType);
assertThat(beansOfType.size()).isEqualTo(2);
}
Share