Spring Framework/스프링 핵심 원리

3. 스프링 컨테이너와 스프링 빈

hnjee 2025. 4. 4. 10:37

1. 스프링 컨테이너와 스프링 빈

1)  IoC Container

  • IoC: Inversion of Control, 제어의 역전 
    • 프로그래밍 제어의 흐름을 개발자가 아닌 프레임워크에서 처리하는 것
    • 구현 객체를 생성하고, 연결하고, 실행하는 것은 프레임 워크에 맡기고 개발자는 로직을 구현하는 것에 집중
  • IoC Container
    • IoC를 구현한 구체적인 프레임워크
    • IoC Container는 객체의 생성, 초기화, 의존성 처리 등을 자동으로 수행해준다. 
    • 의존관계 주입에 초점을 맞추어 최근에는 주로 DI 컨테이너라 한다. 또는 어샘블러, 오브젝트 팩토리 등으로 불리기도 한다. 
    • 대표적인 IoC Container로는 Spring Framework의 ApplicationContext 가 있다.

2) Bean, BeanFactory, Configuration Metadata

  • POJO(Plain Old Java Object): 일반적인 자바 객체 
  • Bean: Spring IoC Container에서 관리되는 객체
  • Bean Factory: Spring에서 사용되는 IoC Container의 가장 기본적인 형태로, Bean의 생성, 초기화, 연결, 제거 등의 라이프사이클을 관리한다. 
  • Configuration Metadata: BeanFactory가 Bean 객체를 생성하고 구성하기 위해 사용하는 설정 정보
    • 스프링 컨테이너를 생성할 때 메타 정보와 POJO가 필요하다
    • 스프링 컨테이너는 다양한 형식의 설정 정보를 받아들일 수 있게 유연하게 설계되어 있다.

3) ApplicationContext

  • BeanFactory: 스프링 컨테이너의 최상위 인터페이스
  • ApplicationContext: 빈 관리 기능 + 편리한 부가 기능 추가
    BeanFactory를 직접 사용할 일은 거의 없고, 부가 기능이 추가된 ApplicationContex를 사용한다.
    • ListableBeanFactory : BeanFactory가 제공하는 모든 기능을 포함한다.
    • ApplicationEventPublisher : 이벤트 처리(Event Handling) 기능을 제공한다.
    • MessageSource : 국제화(i18n) 를 지원하는 메세지를 해결하는 부가 기능을 제공한다.
    • ResourceLoader : 리소스 핸들링(Resource Handling) 기능을 제공한다.
  • ApplicationContext 구현 클래스
    스프링 컨테이너는 XML을 기반으로 만들 수 있고, 애노테이션 기반의 자바 설정 클래스로도 만들 수 있다.
    • AnnotationConfigApplicationContext : Java MetaData Configuration을 읽어 컨테이너 역할을 수행한다.
    • GenericXmlApplicationContext : XML MetaData Configuration을 읽어 컨테이너 역할을 수행한다.
  • BeanFactory나 ApplicationContext를 스프링 컨테이너라 한다. 

 

2. 스프링 컨테이너 생성과 스프링 빈 조회 

1) 스프링 컨테이너 생성 과정 

// 애노테이션 기반의 자바 설정 클래스로 스프링 컨테이너 생성
ApplicationContext ac =
	new AnnotationConfigApplicationContext(AppConfig.class);

 

(1) 스프링 컨테이너 생성

→ (2) 구성 정보(AppConfig.class) 활용하여 스프링 빈 등록

→ (3) 스프링 빈 의존관계 주입 

 

(1) 스프링 컨테이너 생성
(2) 구성 정보 활용하여 스프링 빈 등록

빈 이름은 메서드 이름을 사용하지만, 직접 설정할 수도 있다. 반드시 빈 이름은 각자 다르게 사용해야 함.

(3) 스프링 빈 의존관계 주입

 

스프링은 빈을 생성하고, 의존관계를 주입하는 단계가 나누어져 있다.

그런데 자바 코드로 스프링 빈을 등록하는 경우에는 생성자를 호출하면서 의존관계 주입도 한번에 처리된다.

 

2) 컨테이너에 등록된 Bean 조회

//컨테이너에 등록된 모든 빈 조회 
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
//빈 이름으로 조회 + 애플리케이션 빈만 출력하기 
String beanDefinitionName = beanDefinitionNames[0];
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
	Object bean = ac.getBean(beanDefinitionName);
}

//이름+타입으로 조회 
MemberService memberService = ac.getBean("memberService", MemberService.class);
//이름+구체 타입으로 조회 
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);

//타입으로 조회 -> 같은 타입 둘 이상이면 중복 오류 발생 
MemberService memberService = ac.getBean(MemberService.class);
//특정 타입 모두 조회 
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);

//부모 타입으로 조회시 자식까지 다 조회됨 -> 자식 둘 이상이면 중복 오류 발생 
DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
  • 컨테이너에 등록된 모든 Bean 조회
    • ac.getBeanDefinitionNames() : 스프링에 등록된 모든 빈 이름을 조회한다
    • ac.getBean() : 빈 이름으로 빈 객체(인스턴스)를 조회한다.
    • getRole()
      • ROLE_APPLICATION : 일반적으로 사용자가 정의한 빈
      • ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈
  • 기본 Bean 조회
    • ac.getBean(빈이름, 타입)
    • ac.getBean(타입)
    • 조회 대상 스프링 빈이 없으면 예외 발생
      NoSuchBeanDefinitionException: No bean named 'xxxxx' available
  • 동일한 타입이 둘 이상인 경우
    • 타입으로 조회시 같은 타입의 스프링 빈이 둘 이상이면 오류가 발생한다. 이때는 빈 이름을 지정하자.
    • ac.getBeansOfType() : 해당 타입의 모든 빈을 조회할 수 있다.
      Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
  • 상속 관계 
    • 부모 타입으로 조회하면, 자식 타입도 함께 조회한다.
    • 그래서 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면, 모든 스프링 빈을 조회한다.

 

3. 다양한 설정 형식 지원 

스프링 컨테이너는 다양한 형식의 설정 정보를 받아들일 수 있게 유연하게 설계되어 있다.

단, 최근에는 스프링 부트를 많이 사용하면서 XML기반의 설정은 잘 사용하지 않는다.

//1. 애노테이션 기반 자바 코드 설정 사용
//AnnotationConfigApplicationContext 클래스를 사용하면서 자바 코드로된 설정 정보를 넘긴다. 
ApplicationContext ac =
	new AnnotationConfigApplicationContext(AppConfig.class);
    
//2. XML 설정 사용
//GenericXmlApplicationContext 클래스를 사용하면서 xml 설정 파일을 넘긴다. 
ApplicationContext ac = 
	new GenericXmlApplicationContext("appConfig.xml");
// 자바코드 설정 정보: AppConfig.class

@Configuration
public class AppConfig {
    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }
    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
    @Bean
    public DiscountPolicy discountPolicy() {
        return new FixDiscountPolicy();
    }
}
//xml 설정 정보 : appConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="memberService" class="spring.core.member.MemberServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository" />
    </bean>
    
    <bean id="memberRepository"
          class="spring.core.member.MemoryMemberRepository" />
          
    <bean id="orderService" class="spring.core.order.OrderServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository" />
        <constructor-arg name="discountPolicy" ref="discountPolicy" />
    </bean>
    
    <bean id="discountPolicy" class="spring.core.discount.RateDiscountPolicy" />
    
</beans>

4. 스프링 빈 설정 메타 정보 - BeanDefinition 

1) BeanDefinition이란?

  • 스프링이 이처럼 다양한 설정 형식을 지원할 수 있는 이유는 'BeanDefinition'이라는 추상화이다.
  • BeanDefinition은 Bean 설정 메타정보(Configuration Metadata)
  • 스프링은 여러 형식(자바 코드, XML 등)의 설정 정보를 읽고 BeanDefinition을 생성할 수 있다.  
    @Bean, <bean> 당 각각 하나씩 메타 정보가 생성된다.
  • 스프링 컨테니어는 BeanDefinition를 기반으로 스프링 빈을 생성한다.
  • 코드 레벨에서의 BeanDefinition과 Bean 생성 과정
    (1) XxxBeanDefinitionReader xxx형식의 설정 정보를 읽고 BeanDefinition를 생성
    (2) XxxApplicationContext 스프링 컨테이너가 BeanDefinition을 기반으로 Bean을 생성  

 

2) BeanDefinition 정보

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);
        } 
    }
}
  • BeanClassName: 생성할 빈의 클래스 명(자바 설정 처럼 팩토리 역할의 빈을 사용하면 없음)
  • factoryBeanName: 팩토리 역할의 빈을 사용할 경우 이름, 예) appConfig
  • factoryMethodName: 빈을 생성할 팩토리 메서드 지정, 예) memberService
  • Scope: 싱글톤(기본값)
  • lazyInit: 스프링 컨테이너를 생성할 때 빈을 생성하는 것이 아니라, 실제 빈을 사용할 때 까지 최대한 생성을 지연 처리 하는지 여부
  • InitMethodName: 빈을 생성하고, 의존관계를 적용한 뒤에 호출되는 초기화 메서드 명
  • DestroyMethodName: 빈의 생명주기가 끝나서 제거하기 직전에 호출되는 메서드 명
  • Constructor arguments, Properties: 의존관계 주입에서 사용한다. (자바 설정 처럼 팩토리 역할의 빈을 사용 하면 없음)