본문 바로가기
Java/Spring, SpringBoot

[Spring] Spring IoC 컨테이너와 빈에 대한 소개 (day2)

by 마라민초닭발로제 2025. 1. 24.

Spring IoC 컨테이너와 빈에 대한 소개

이 장에서는 Inversion of Control(IoC) 원칙에 대한 Spring Framework의 구현 방식을 다룹니다. Dependency Injection(DI)IoC의 특화된 형태로, 객체가 자신의 의존성(즉, 함께 작동하는 다른 객체)을 생성자 인자, 팩토리 메서드 인자, 또는 객체 인스턴스가 생성된 이후나 팩토리 메서드에서 반환된 후에 설정되는 속성을 통해 정의합니다. 그런 다음 IoC 컨테이너가 빈을 생성할 때 이러한 의존성을 주입합니다. 이 과정은 본래의 객체가 클래스의 직접적인 생성이나 Service Locator 패턴과 같은 메커니즘을 사용해 의존성의 생성 또는 위치를 제어하는 방식과 반대되기 때문에 Inversion of Control(제어 역전)이라는 이름이 붙여졌습니다.

Spring Framework의 IoC 컨테이너는 org.springframework.beans와 org.springframework.context 패키지를 기반으로 합니다. BeanFactory 인터페이스는 모든 유형의 객체를 관리할 수 있는 고급 구성 메커니즘을 제공합니다. ApplicationContextBeanFactory의 하위 인터페이스로, 다음과 같은 기능을 추가합니다:

  • Spring의 AOP 기능과의 쉬운 통합
  • 메시지 리소스 처리(국제화를 위한 용도)
  • 이벤트 발행
  • 웹 애플리케이션에서 사용하기 위한 WebApplicationContext와 같은 애플리케이션 계층별 컨텍스트

간단히 말해, BeanFactory는 구성 프레임워크와 기본적인 기능을 제공하고, ApplicationContext는 더 많은 엔터프라이즈 특화 기능을 추가합니다. ApplicationContext BeanFactory의 완전한 상위 집합이며, 이 장에서는 Spring의 IoC 컨테이너에 대한 설명에서 ApplicationContext만 독점적으로 사용됩니다. BeanFactory 대신 ApplicationContext를 사용하는 방법에 대한 자세한 내용은 BeanFactory API를 다루는 섹션을 참조하세요.

Spring에서는 애플리케이션의 핵심을 형성하고 Spring IoC 컨테이너에 의해 관리되는 객체를 **빈(Bean)**이라고 합니다. 빈은 Spring IoC 컨테이너에 의해 인스턴스화되고, 조립되며, 관리되는 객체입니다. 그렇지 않다면, 빈은 단순히 애플리케이션 내의 많은 객체 중 하나일 뿐입니다. 빈과 그들 간의 의존성은 컨테이너가 사용하는 구성 메타데이터에 반영됩니다.

 

Container Overview

org.springframework.context.ApplicationContext 인터페이스는 Spring IoC 컨테이너를 나타내며, 빈(bean)을 생성, 설정 및 조립하는 책임을 갖습니다. 컨테이너는 구성 메타데이터를 읽어, 생성하고 설정하며 조립할 구성 요소에 대한 지침을 얻습니다. 구성 메타데이터는 주석이 달린 컴포넌트 클래스, 팩토리 메서드를 가진 구성 클래스, 외부 XML 파일 또는 Groovy 스크립트 형태로 표현될 수 있습니다. 어떤 형식을 사용하든 애플리케이션과 그 구성 요소 간의 풍부한 의존성을 구성할 수 있습니다.

 

Spring의 핵심에는 여러 ApplicationContext 인터페이스 구현체가 포함되어 있습니다. 독립 실행형 애플리케이션에서는 AnnotationConfigApplicationContext 또는 ClassPathXmlApplicationContext 인스턴스를 생성하는 것이 일반적입니다.

 

대부분의 애플리케이션 시나리오에서 Spring IoC 컨테이너의 하나 이상의 인스턴스를 명시적으로 사용자 코드로 인스턴스화할 필요는 없습니다. 예를 들어, 기본 웹 애플리케이션 시나리오에서는 웹 애플리케이션의 web.xml 파일에 간단한 보일러플레이트 웹 디스크립터 XML만 있으면 충분합니다(웹 애플리케이션을 위한 편리한 ApplicationContext 인스턴스화에 관한 내용은 참조하십시오). Spring Boot 시나리오에서는 일반적인 설정 규칙에 따라 애플리케이션 컨텍스트가 암묵적으로 부트스트랩됩니다.

 

다음 다이어그램은 Spring이 작동하는 방식에 대한 고수준 개요를 보여줍니다. 애플리케이션 클래스는 구성 메타데이터와 결합되어, ApplicationContext 가 생성되고 초기화된 후 완전히 구성되고 실행 가능한 시스템 또는 애플리케이션을 갖게 됩니다.

https://docs.spring.io/spring-framework/reference/core/beans/basics.html

 

 

구성 메타데이터

위 다이어그램에서 보여주듯, Spring IoC 컨테이너는 구성 메타데이터를 소비합니다. 이 구성 메타데이터는 애플리케이션 개발자인 사용자가 Spring 컨테이너에 애플리케이션의 구성 요소를 생성, 설정 및 조립하는 방법을 알려주는 역할을 합니다.

 

Spring IoC 컨테이너는 실제로 이 구성 메타데이터가 작성된 형식으로부터 완전히 분리되어 있습니다. 요즘 많은 개발자가 Spring 애플리케이션에 대해 Java 기반 구성을 선택합니다.

  • 주석 기반 구성: 애플리케이션의 컴포넌트 클래스에 주석 기반 구성 메타데이터를 정의하여 빈을 정의합니다.
  • Java 기반 구성: Java 기반 구성 클래스를 사용하여 애플리케이션 클래스 외부에 빈을 정의합니다. 이러한 기능을 사용하려면 @Configuration, @Bean, @Import, @DependsOn 주석을 참조하십시오.

Spring 구성은 컨테이너가 관리해야 하는 최소 한 개의 빈 정의(일반적으로 그 이상)로 구성됩니다. Java 구성은 일반적으로 @Configuration 클래스 내 @Bean 주석이 달린 메서드를 사용하며, 각각 하나의 빈 정의에 해당합니다.

 

이러한 빈 정의는 애플리케이션을 구성하는 실제 객체와 연결됩니다. 일반적으로 서비스 계층 객체, 데이터 접근 객체(DAO)와 같은 영속 계층 객체, Web 컨트롤러와 같은 프레젠테이션 객체, JPA EntityManagerFactory, JMS 큐와 같은 인프라 객체를 정의합니다. 보통 세부 도메인 객체는 컨테이너에서 구성하지 않습니다. 도메인 객체의 생성 및 로드는 보통 저장소와 비즈니스 로직의 책임이기 때문입니다.

 

 

XML as an External Configuration DSL

 

XML 기반 구성 메타데이터는 최상위 <beans/> 요소 내에서 <bean/> 요소로 이러한 빈을 구성합니다. 아래 예시는 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
       https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">  
        <!-- 이 빈의 협력자 및 구성은 여기에 작성됩니다 -->
    </bean>

    <bean id="..." class="...">
        <!-- 이 빈의 협력자 및 구성은 여기에 작성됩니다 -->
    </bean>

    <!-- 추가 빈 정의는 여기에 작성됩니다 -->
</beans>

 

  • id 속성: 개별 빈 정의를 식별하는 문자열입니다.
  • class 속성: 빈의 유형을 정의하며, 완전히 한정된 클래스 이름을 사용합니다.
  • id 속성 값은 협력 객체를 참조하는 데 사용될 수 있습니다.

 

XML 리소스 파일 경로를 ClassPathXmlApplicationContext 생성자에 제공하여 컨테이너가 다양한 외부 리소스(예: 로컬 파일 시스템, Java CLASSPATH 등)에서 구성 메타데이터를 로드하도록 해야 합니다.

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

 

 

Spring의 IoC 컨테이너를 이해한 후, Spring의 Resource 추상화(Resources 참조)에 대해 배우면 유용할 수 있습니다. Resource 추상화는 URI 문법으로 정의된 위치에서 InputStream을 읽는 편리한 메커니즘을 제공합니다. 특히 Resource 경로는 Application Context와 Resource Path를 구성하는 데 사용됩니다.

 

The following example shows the service layer objects (services.xml) configuration file:

<?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="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- 추가 협력자 및 구성은 여기에 작성됩니다 -->
    </bean>

    <!-- 추가 서비스 빈 정의는 여기에 작성됩니다 -->
</beans>

The following example shows the data access objects daos.xml file:

<?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="accountDao"
		class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
		<!-- additional collaborators and configuration for this bean go here -->
	</bean>

	<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
		<!-- additional collaborators and configuration for this bean go here -->
	</bean>

	<!-- more bean definitions for data access objects go here -->

</beans>

 

앞의 예에서 서비스 계층은 PetStoreServiceImpl 클래스와 (JPA 객체 관계형 매핑 표준에 기반한) JpaAccountDao 및 JpaItemDao 유형의 데이터 액세스 객체 두 개로 구성됩니다. 속성 이름 요소는 JavaBean 속성의 이름을 참조하고 참조 요소는 다른 빈 정의의 이름을 참조합니다. id 요소와 ref 요소 사이의 이러한 연결은 공동 작업하는 개체 간의 종속성을 나타냅니다. 객체의 종속성 구성에 대한 자세한 내용은 종속성문서를 참조하세요.

 

XML 기반 구성 메타데이터 작성(Composing XML-based Configuration Metadata)

빈 정의를 여러 XML 파일에 걸쳐 나누는 것이 유용할 때가 있습니다. 일반적으로 각 개별 XML 구성 파일은 아키텍처의 논리적 계층이나 모듈을 나타냅니다.

ClassPathXmlApplicationContext 생성자를 사용하여 XML 조각으로부터 빈 정의를 로드할 수 있습니다. 이 생성자는 이전 섹션에서 보여준 것처럼 여러 Resource 위치를 인수로 받습니다. 또는 <import/> 요소를 하나 이상 사용하여 다른 파일 또는 여러 파일에서 빈 정의를 로드할 수도 있습니다. 아래 예시는 이를 수행하는 방법을 보여줍니다:

<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

 

위의 예제에서, 외부 빈 정의는 세 개의 파일에서 로드됩니다: services.xml, messageSource.xml, 그리고 themeSource.xml입니다. 모든 경로는 가져오는 정의 파일에 상대적입니다. 따라서 services.xml은 가져오는 파일과 동일한 디렉터리 또는 클래스패스 위치에 있어야 하며, messageSource.xml과 themeSource.xml은 가져오는 파일의 위치보다 하위에 있는 resources 위치에 있어야 합니다. 보시다시피, 선행 슬래시는 무시됩니다. 그러나 이러한 경로가 상대적이라는 점을 고려할 때 슬래시를 아예 사용하지 않는 것이 더 나은 형태입니다. 가져오는 파일의 내용은 최상위 <beans/> 요소를 포함하여 Spring 스키마에 따라 유효한 XML 빈 정의여야 합니다.

상위 디렉터리에 있는 파일을 상대 경로 "../"를 사용하여 참조할 수도 있지만 권장되지 않습니다. 그렇게 하면 현재 애플리케이션 외부의 파일에 대한 의존성이 생성됩니다. 특히, 클래스패스 URL(예: classpath:../services.xml)에서는 런타임 해상도 과정이 "가장 가까운" 클래스패스 루트를 선택한 다음 상위 디렉터리를 찾기 때문에 이 참조는 권장되지 않습니다. 클래스패스 구성 변경은 다른, 잘못된 디렉터리를 선택하게 만들 수 있습니다.

 

항상 상대 경로 대신 완전한 리소스 위치를 사용할 수 있습니다. 예를 들어, file:C:/config/services.xml 또는 classpath:/config/services.xml과 같이 사용할 수 있습니다. 그러나 이는 애플리케이션 구성을 특정 절대 위치에 결합시키는 결과를 초래할 수 있다는 점을 인지해야 합니다. 일반적으로 이러한 절대 위치에 대한 간접 경로를 유지하는 것이 더 바람직합니다. 예를 들어, JVM 시스템 속성에 따라 런타임 시 해결되는 "${...}" 플레이스홀더를 사용하는 방식입니다.

네임스페이스 자체가 import 지시어 기능을 제공합니다. 기본 빈 정의 외에도 Spring에서 제공하는 다양한 XML 네임스페이스(예: context와 util 네임스페이스)를 통해 추가적인 구성 기능을 사용할 수 있습니다.

Groovy 빈 정의 DSL(The Groovy Bean Definition DSL)

외부화된 구성 메타데이터에 대한 또 다른 예로, 빈 정의는 Grails 프레임워크에서 알려진 Spring의 Groovy 빈 정의 DSL로도 표현될 수 있습니다. 일반적으로 이러한 구성은 다음 예제와 같은 구조로 ".groovy" 파일에 작성됩니다:

beans {
    dataSource(BasicDataSource) {
        driverClassName = "org.hsqldb.jdbcDriver"
        url = "jdbc:hsqldb:mem:grailsDB"
        username = "sa"
        password = ""
        settings = [mynew:"setting"]
    }
    sessionFactory(SessionFactory) {
        dataSource = dataSource
    }
    myService(MyService) {
        nestedBean = { AnotherBean bean ->
            dataSource = dataSource
        }
    }
}

이 구성 스타일은 XML 빈 정의와 거의 동등하며 Spring의 XML 구성 네임스페이스도 지원합니다. 또한 importBeans 지시어를 통해 XML 빈 정의 파일을 가져올 수 있도록 합니다.

 

테이너 사용하기 (Using the Container)

ApplicationContext는 다양한 빈과 그 의존성을 유지하는 레지스트리를 관리할 수 있는 고급 팩토리 인터페이스입니다. T getBean(String name, Class<T> requiredType) 메서드를 사용하여 원하는 빈 인스턴스를 검색할 수 있습니다.

 

ApplicationContext를 통해 빈 정의를 읽고 이를 액세스할 수 있습니다. 다음 예제는 이를 보여줍니다:

// 빈 생성 및 설정
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// 설정된 인스턴스 가져오기
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// 설정된 인스턴스 사용
List<String> userList = service.getUsernameList();

 

Groovy 설정을 사용할 경우, 부트스트래핑은 매우 유사합니다. Groovy-aware(그리고 XML 빈 정의도 이해할 수 있는) 컨텍스트 구현 클래스가 사용됩니다. 다음은 Groovy 설정 예제입니다:

ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");

// 설정된 인스턴스 가져오기
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// 설정된 인스턴스 사용
List<String> userList = service.getUsernameList();

위 예제에서, PetStoreService 빈은 서비스 계층의 일부이며, services.xml, services.groovy 또는 다른 지원되는 형식의 메타데이터 파일에서 설정됩니다. GenericGroovyApplicationContext는 Groovy를 기반으로 한 설정을 지원하지만 XML 설정 파일도 처리할 수 있습니다.

GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();

 

또한 GroovyBeanDefinitionReader를 사용할 수 있습니다. 이는 Groovyfile을 지원합니다. 

GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();

 

다양한 구성 소스에서 빈 정의를 읽어들이는 리더 위임자를 같은 ApplicationContext에서 혼합하여 사용할 수 있습니다.
그 후, getBean을 사용하여 빈 인스턴스를 검색할 수 있습니다. ApplicationContext 인터페이스에는 빈을 검색하는 몇 가지 다른 메서드가 있지만, 이상적으로는 애플리케이션 코드에서 이러한 메서드를 절대 사용해서는 안 됩니다. 실제로, 애플리케이션 코드에는 getBean() 메서드를 호출하는 부분이 전혀 없어야 하며, 따라서 Spring API에 의존성도 전혀 없어야 합니다. 예를 들어, Spring의 웹 프레임워크와의 통합은 컨트롤러 및 JSF 관리 빈과 같은 다양한 웹 프레임워크 구성 요소에 대한 의존성 주입을 제공하며, 이를 통해 메타데이터(예: 자동 주입 주석)를 사용하여 특정 빈에 대한 의존성을 선언할 수 있습니다.

 

 



레퍼런스
https://docs.spring.io/spring-framework/reference/core/beans/basics.html

 

Container Overview :: Spring Framework

As the preceding diagram shows, the Spring IoC container consumes a form of configuration metadata. This configuration metadata represents how you, as an application developer, tell the Spring container to instantiate, configure, and assemble the component

docs.spring.io