Spring IoC Container Overview
정의
org.springframework.context.ApplicationContext 인터페이스가 Spring IoC 컨테이너를 대표한다. 컨테이너는 Bean의 인스턴스화, 설정, 조립을 담당하며, 어떤 컴포넌트를 어떻게 구성할지는 설정 메타데이터(Configuration Metadata) 를 읽어서 판단한다. 설정 메타데이터는 어노테이션 기반 컴포넌트 클래스, 팩토리 메서드가 있는 설정 클래스, 외부 XML 파일, Groovy 스크립트 등으로 표현할 수 있다.
동작 원리
컨테이너의 작동 흐름
애플리케이션 클래스(POJO)와 설정 메타데이터를 조합하여 ApplicationContext를 생성·초기화하면, 완전히 설정된 실행 가능한 시스템이 만들어진다.
POJO] --> C[ApplicationContext] B[설정 메타데이터
Annotation/Java/XML] --> C C --> D[완전히 설정된
실행 가능한 시스템]
ApplicationContext 구현체
Spring 코어에는 여러 ApplicationContext 구현체가 포함되어 있다.
| 구현체 | 용도 |
|---|---|
AnnotationConfigApplicationContext | 독립 실행 애플리케이션에서 어노테이션/Java 기반 설정 |
ClassPathXmlApplicationContext | 독립 실행 애플리케이션에서 XML 기반 설정 |
GenericApplicationContext | 가장 유연한 형태, Reader 델리게이트와 조합하여 사용 |
GenericGroovyApplicationContext | Groovy 기반 설정 (XML Bean 정의도 이해) |
대부분의 애플리케이션 시나리오에서 사용자 코드로 직접 컨테이너를 인스턴스화할 필요가 없다:
- 웹 애플리케이션:
web.xml의 보일러플레이트 웹 디스크립터로 충분 - Spring Boot: 공통 설정 컨벤션에 따라 컨텍스트가 암묵적으로 부트스트랩됨
설정 메타데이터 (Configuration Metadata)
컨테이너에게 “어떤 Bean을 어떻게 인스턴스화, 설정, 조립할지” 알려주는 정보. IoC 컨테이너 자체는 메타데이터가 실제로 작성되는 형식과 완전히 분리(decoupled) 되어 있다.
설정 방식 3가지
1. 어노테이션 기반 (Annotation-based)
애플리케이션 컴포넌트 클래스에 어노테이션 기반 설정 메타데이터를 사용하여 Bean을 정의한다.
2. Java 기반 (Java-based) — 현재 주류
애플리케이션 클래스 외부에서 Java 기반 설정 클래스를 사용하여 Bean을 정의한다. 관련 어노테이션:
@Configuration— 설정 클래스 선언@Bean— Bean 정의 메서드 (@Configuration클래스 내에서 사용, 각 메서드가 하나의 Bean 정의에 대응)@Import— 다른 설정 클래스 가져오기@DependsOn— Bean 간 의존 순서 지정
Spring 설정은 컨테이너가 관리해야 하는 최소 하나, 보통은 여러 개의 Bean 정의로 구성된다.
3. XML 기반 (XML-based) — 레거시
최상위 <beans/> 요소 내에 <bean/> 요소로 Bean을 설정한다:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="..."> <!-- (1) id: Bean 식별 문자열, (2) class: FQCN -->
<!-- collaborators and configuration for this bean go here -->
</bean>
</beans>
id속성: 개별 Bean 정의를 식별하는 문자열. 협력 객체(collaborating objects)를 참조할 때 사용class속성: Bean의 타입을 정의, 완전한 클래스명(FQCN) 사용
컨테이너를 인스턴스화하려면 XML 리소스 파일의 경로를 ClassPathXmlApplicationContext 생성자에 전달한다. 로컬 파일 시스템, Java CLASSPATH 등 다양한 외부 리소스에서 설정 메타데이터를 로드할 수 있다:
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
XML 설정의 실전 구조: 계층별 분리
일반적으로 서비스 계층과 데이터 접근 계층을 별도 XML 파일로 분리한다.
서비스 계층 (services.xml):
<beans xmlns="http://www.springframework.org/schema/beans" ...>
<bean id="petStore"
class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
</bean>
</beans>
데이터 접근 계층 (daos.xml):
<beans xmlns="http://www.springframework.org/schema/beans" ...>
<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao"/>
<bean id="itemDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao"/>
</beans>
<property name="...">: JavaBean 프로퍼티 이름을 참조<property ref="...">: 다른 Bean 정의의 이름을 참조id와ref간의 연결이 협력 객체 간의 의존성을 표현한다
XML 설정 파일 합성 (Composing)
Bean 정의를 여러 XML 파일에 걸쳐 구성할 수 있다. 각 XML 설정 파일이 아키텍처의 논리적 계층이나 모듈을 대표하는 것이 일반적이다.
방법 1: ClassPathXmlApplicationContext 생성자에 여러 리소스 경로 전달
방법 2: <import/> 요소로 다른 파일에서 Bean 정의 로드
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
- 모든 경로는 import하는 파일 기준의 상대 경로
../상대 경로로 부모 디렉토리를 참조하는 것은 권장하지 않음 — 현재 애플리케이션 외부 파일에 대한 의존성을 만들기 때문- 특히
classpath:../services.xml같은 classpath URL에서는 런타임 해석 프로세스가 “가장 가까운” classpath 루트를 선택하므로, 설정 변경 시 잘못된 디렉토리를 선택할 수 있다 - 대안: 완전한 리소스 경로 사용 (
file:C:/config/services.xml,classpath:/config/services.xml) 또는${...}플레이스홀더로 JVM 시스템 프로퍼티를 통한 간접 참조
Bean 정의의 대상
| 계층 | 예시 |
|---|---|
| Service Layer | 비즈니스 로직 서비스 (PetStoreServiceImpl) |
| Persistence Layer | Repository, DAO (JpaAccountDao, JpaItemDao) |
| Presentation | Web Controller |
| Infrastructure | JPA EntityManagerFactory, JMS Queue |
세밀한 도메인 객체(fine-grained domain objects)는 보통 컨테이너에 등록하지 않는다. Repository와 비즈니스 로직이 도메인 객체의 생성과 로드를 담당한다.
컨테이너 사용
ApplicationContext는 다양한 Bean과 그 의존성의 레지스트리를 유지하는 고급 팩토리 인터페이스다. T getBean(String name, Class<T> requiredType) 메서드로 Bean 인스턴스를 조회할 수 있다.
기본 사용법
// Bean 생성 및 설정
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// 설정된 인스턴스 조회
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// 설정된 인스턴스 사용
List<String> userList = service.getUsernameList();
Groovy 설정
Groovy 인식 컨텍스트를 사용하며, XML Bean 정의도 이해한다:
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
GenericApplicationContext + Reader 델리게이트
가장 유연한 방식. Reader 델리게이트와 조합하여 다양한 설정 소스에서 Bean 정의를 읽을 수 있다:
// XML Reader
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
// Groovy Reader
GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();
동일한 ApplicationContext에서 여러 Reader 델리게이트를 혼합하여 다양한 설정 소스로부터 Bean 정의를 읽을 수 있다.
getBean() 사용에 대한 권장사항
이상적으로는 애플리케이션 코드에서 getBean() 메서드를 전혀 호출하지 않아야 한다. Spring의 웹 프레임워크 통합은 Controller, JSF-managed Bean 등 다양한 컴포넌트에 대해 DI를 제공하므로, 메타데이터(autowiring 어노테이션 등)를 통해 특정 Bean에 대한 의존성을 선언할 수 있다. 이렇게 하면 Spring API에 대한 의존성이 전혀 없어진다.
관련 문서
- Bean Overview - BeanDefinition 메타데이터, 네이밍, 인스턴스화 방식
- Spring IoC 컨테이너와 Bean - IoC/DI 원리, BeanFactory vs ApplicationContext, Bean 정의
- Spring Core Technologies - Spring 핵심 기술 전체 개요
- Spring Framework Overview - Spring Framework 소개와 설계 철학