들어가며

  • 이전 포스트 에서 스프링에서 자체적으로 만들고 관리하는 스프링 DI 컨테이너에 대해 알아보았다.
  • 스프링 DI 컨테이너에서 @Bean으로 등록한 모든 객체들을 관리하는 동시에 DI가 필요할 시 DI도 프레임워크에서 자체적으로 수행해주며 이렇게 개발자가 아닌 프레임워크에서 프로그램을 컨트롤하는 것을 IoC라고 하였다.
  • 이번 포스트에서는 스프링 DI 컨테이너에서 어떻게 빈을 조회하는지와 빈을 조회하는데 있어서 있을 수 있는 문제점들과 해결책을 알아보도록 한다.

 

 

모든 Bean 조회

  • 스프링 DI 컨테이너에 존재하는 모든 Bean을 조회하기 위해서는 다음과 같은 코드를 사용한다.
public class BeanTest{
    static AnnotationConfigApplicationContext ac = 
        new AnnotationConfigApplicationContext(AppConfig.class);
    
    public static void main(String[] args){
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        
        for(String beanDefinitionName : beanDefinitionNames){
            Object bean = ac.getBean(beanDefinitionName);
            System.out.println("beanName = " + beanDefinitionName);
            System.out.println("object = " + bean);
        }
    }
}
  • ac.getBeanDefinitionNames() 메서드는 현재 스프링 DI 컨테이너에 등록되어있는 모든 Bean 이름을 조회한다.
  • 이때 주의할 점은 ApplicationContext 인터페이스에서는 이 메서드를 지원하고 있지 않기 때문에 이 메서드를 사용하려면 구현체를 사용하여 정의해야한다.
  • ac.getBean("beanName") 메서드는 Bean이름을 사용하여 스프링 DI 컨테이너에 존재하는 동일한 이름을 가진 Bean을 조회하여 그 객체를 가져온다.
  • 만약 스프링이 자체적으로 미리 등록해놓은 Bean이 아닌 내가 등록한 Bean만 조회하고 싶다면 코드를 다음과 같이 바꾸면 된다.
public class BeanTest{
    static AnnotationConfigApplicationContext ac = 
        new AnnotationConfigApplicationContext(AppConfig.class);
    
    public static void main(String[] args){
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        
        for(String beanDefinitionName : beanDefinitionNames){          
            if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){
                Object bean = ac.getBean(beanDefinitionName);
                
                System.out.println("beanName = " + beanDefinitionName);
                System.out.println("object = " + bean);
            }
        }
    }
}
  • getRole() 메서드를 통해 해당 Bean이 스프링이 자체적으로 생성하여 등록시킨 것인지 아니면 개발자가 등록해놓은 Bean인지를 확인할 수 있다. ROLE_APPLICATION이 개발자가 직접 등록한 Bean을 의미한다.
  • ROLE_INFRASTRUCTURE는 스프링 내부에서 자체적으로 만들어 등록시킨 Bean을 의미한다.

 

 

 

단일 Bean 조회

  • 스프링 DI 컨테이너에서 스프링 빈을 찾는 가장 기본적인 방법은 위에서 살펴보았듯이 getBean() 메서드를 사용하는 것이다.
    • 이때 getBean()메서드는 getBean("beanName", beanType), getBean(beanType)으로 나뉘어져 있는데 이는 이름으로도 찾을 수 있고 Bean의 타입으로도 찾아서 반환할 수 있다는 의미이다.
  • 만약 해당 조건으로 조회할 Bean이 존재하지 않는 경우에 NoSuchBeanDefinitionException 예외를 발생시키게 된다.
public class BeanTest{
    static AnnotationConfigApplicationContext ac = 
        new AnnotaionConfigApplicationContext(AppConfig.class);
    
    public static void main(String[] args){
        // search by bean name
        MemberRepository bean = ac.getBean("memoryMemberRepository", MemberRepository.class);
        
        // search by bean type
        MemberRepository bean2 = ac.getBean(MemberRepository.class);
        
        // search by bean specific type
        MemberRepository bean3 = ac.getBean(MemoryMemberRepository.class);
    }

}
  • 각각 위에서부터 이름과 타입을 사용하여 Bean을 가져오는 방식, Bean 타입을 사용하여 가져오는 방식, Bean의 구체 타입을 사용하여 가져오는 방식이다.
  • 구체 타입을 사용하면 Bean 객체 조회 시 둘 이상의 Bean이 감지되는 문제를 회피할 수 있으나 이후에 변경하려고 할 시에 유연성이 떨어진다는 단점이 존재한다. 이는 구체화가 가지고 있는 문제점이기도 하다.
  • Bean 타입을 이용하여 Bean을 조회하는 것은 조회를 통해 얻어지는 Bean이 2개 이상일 시 문제가 발생한다. 이 문제에 대해서는 다음 장에서 살펴본다.

 

 

동일한 타입의 Bean이 2개 이상일 때

  • Bean Type으로 조회를 수행할 시 동일한 타입이 2개 이상 조회될 수도 있다.
  • 특히나 이것은 Bean Type이 인터페이스와 같이 추상 타입으로 조회할 때 많이 발생할 수 있다.
    • 예를 들어 MemberRepository라는 인터페이스가 존재하고 이를 구현한 구현체인 MemoryMemberRepository와 JdbcMemberRepository가 존재할 때 ac,getBean(MemberRepository)로 조회를 수행할 경우 둘 모두 다형성에 의해 MemberRepository로 될 수 있으므로 2개 이상 조회되어 에러가 발생한다.
  • 이러한 문제점은 Bean 조회의 특징 중 하나인 상속 관계 조회에서 들어나는데 부모 타입으로 Bean 조회를 수행하면 자식 타입도 같이 조회하는 특성 때문이다.
  • 모든 클래스들은 Object를 상속받기 때문에 만약 Bean 을 Object 타입으로 조회하면 모든 스프링 Bean을 조회하게 된다.
  • 이러한 문제는 타입과 이름을 같이 명시함으로서 해결이 가능하다.
public class BeanTest{
    static AnnotationConfigApplicationContext ac = 
        new AnnotaionConfigApplicationContext(AppConfig.class);
    
    // 2개의 MemberRepository type 존재
    static MemberRepository memoryMemberRepository = new MemoryMemberRepository();
    static MemberRepository jdbcMemberRepository = new JdbcMemberRepository();
    
    public static void main(String[] args){
        // search by bean name (타입과 이름으로 같이 조회)
        MemberRepository bean = ac.getBean("memoryMemberRepository", MemberRepository.class);
        
        // search by bean type(에러가 발생)
        // MemberRepository bean2 = ac.getBean(MemberRepository.class);
        
        // search by bean specific type (에러는 발생하지 않으나 변경에 대해 유연하지 않음)
        MemberRepository bean3 = ac.getBean(MemoryMemberRepository.class);
    }

}

 

 

 

정리

  • Bean 조회는 getBean()으로 수행할 수 있으며 이름과 타입으로 조회가 가능하다.
  • 타입으로 조회할 시 해당 타입에서 파생된 자식 타입들도 모두 조회하기 때문에 2개 이상의 Bean이 조회되는 문제가 발생할 수 있다. 이럴 때는 이름과 타입을 모두 사용하여 해결이 가능하다.
    • 구체화된 타입을 지정하여 수행할 수도 있으나 변경에 대해 유연하지 않기 때문에 추천되는 방식은 아니다.
  • 다음 포스트에서는 스프링 DI 컨테이너가 등록한 Bean을 어떻게 관리하고 있는지에 대해서 알아본다.
복사했습니다!