스프링

Spring IoC 컨테이너 - 1

hs-archive 2023. 7. 5. 00:50

왜 스프링을 사용하나요

스프링(Spring)은 Java 기반의 오픈 소스 애플리케이션 프레임워크입니다. 스프링은 엔터프라이즈급 애플리케이션을 개발하기 위한 다양한 기능추상화된 라이브러리들을 제공하여 개발자가 애플리케이션을 더 쉽고 효율적으로 개발할 수 있도록 도와줍니다.

 

사실 위 문장만 보고 과연 스프링은 저래서 사용하는 거구나 하고 이해하기는 어렵습니다. 이어지는 포스팅에서는 스프링이 제공해주는 "다양한 기능과 추상화된 라이브러리들" 중에서 핵심인 IoC 컨테이너Spring AOP를 알아볼 것입니다. 스프링을 사용하는 이유는 앞으로 배울 이러한 기술들을 간편하게 사용할 수 있도록 제공해주기 때문이라고 생각하시면 됩니다.

 

Core Technologies

스프링 프레임워크에 절대적으로 필요한 기술들을 뜻합니다. 이들 중 가장 중요한 것은 IoC(Inversion of Control) 컨테이너입니다. 그 외에 Spring AOP도 Spring의 Core Technologies에 속합니다.

 

The IoC Container

IoC는 DI라고도 합니다. IoC는 객체가 자신이 작업하는 다른 객체들과의 의존성을 "생성자 인자/팩토리 메서드의 인자/객체 인스턴스가 생성된 후에 설정되는 속성/"을 통해서만 정의하는 프로세스를 말합니다. IoC 컨테이너는 빈을 생성할 때 해당 의존성을 주입합니다. 이 과정은 빈 자체가 클래스의 직접적인 생성 또는 Service Locator 패턴과 같은 매커니즘을 사용하여 의존성을 인스턴스화하거나 위치를 제어하는 것과 근본적으로 반대되는 개념입니다.

 

 

** Service Locator 패턴

객체의 의존성을 찾아주는 서비스(로케이터)를 중간에 두고, 클라이언트가 해당 서비스를 통해 필요한 의존성을 직접 가져오는 패턴입니다. 이 패턴은 의존성을 직접적으로 주입하는 대신 중앙 서비스를 통해 찾아오는 방식으로 작동합니다. IoC컨테이너는 일단 등록을 해놓으면 클라이언트가 서비스를 통해 의존성을 직접 가져다가 사용하는 것이 아닌, IoC가 알아서 적절한 때에(빈을 생성할 때) 해당 의존성을 주입하기에 IoC 컨테이너의 DI는 Service Locator 패턴의 DI와 다릅니다.

 

org.springframework.beans 및 org.springframework.context 패키지는 Spring Framework의 IoC 컨테이너의 기반이 됩니다. BeanFactory 인터페이스는 모든 유형의 객체를 관리할 수 있는 고급 구성 메커니즘을 제공합니다. ApplicationContext는 BeanFactory의 하위 인터페이스입니다. 간단히 말하면, BeanFacory는 구성 프레임워크와 기본 기능을 제공하고, ApplicationContext는 BeanFactory의 완전한 상위 집합입니다. 이 장에서 Spring의 IoC 컨테이너에 대한 설명에 주로 ApplicationContext가 사용되므로, BeanFactory를 사용하는 방법에 대한 자세한 내용은 BeanFactory API를 다루는 섹션을 참조하세요.

 

Spring IoC 컨테이너에서 관리되는 주요 구성 요소를 bean이라고 합니다. bean은 Spring IoC 컨테이너에 의해 인스턴스화, 조립 및 관리되는 객체입니다. bean과 bean 간의 종속성은 컨테이너에서 사용하는 구성 메타데이터(configuration metadata)에 반영됩니다.

 

Container Overview

org.springframework.context.ApplicationContext 인터페이스는 Spring IoC 컨테이너를 나타내며, 빈을 인스턴스화하고 구성하며 조립하는 역할을 담당합니다. 컨테이너는 구성 메타데이터(configuration metadata)를 읽어서 어떤 객체를 인스턴스화하고 구성하며 조립해야 하는지에 대한 지시를 받습니다. 구성 메타데이터는 XML, Java 어노테이션 또는 Java 코드로 표현됩니다. 이러한 방식으로 애플리케이션을 구성하는 객체 간의 풍부한 상호 의존성을 표현합니다.

 

Spring은 ApplicationContext 인터페이스의 여러 구현체를 제공합니다. 독립 실행형 애플리케이션에서는 ClassPathXmlApplicationContext 또는 FileSystemXmlApplicationContext의 인스턴스를 생성하는 것이 일반적입니다. 구성 메타데이터를 정의하는 전통적인 형식은 XML이지만, Java 어노테이션을 섞어서 구성 메타데이터를 지정할 수도 있습니다.

 

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

 

그림 1. Spring IoC 컨테이너

 

Configuration Metadata(구성 메타데이터)

앞의 다이어그램에서 볼 수 있듯이 Spring IoC 컨테이너는 구성 메타데이터(configuration metadata) 형식을 필요로합니다. 이 구성 메타데이터는 애플리케이션에서 개체를 인스턴스화, 구성 및 어셈블하도록 Spring 컨테이너에 지시하는 방법을 나타냅니다. 구성 메타데이터는 일반적으로 XML 형식으로 제공되며, 이 장의 대부분은 Spring IoC 컨테이너의 주요 개념과 기능을 전달하기 위해 XML 형식을 사용합니다. 앞서 말씀드렸다싶이 구성 메타데이터는 오직 XML로만 작성될 수 있는 것은 아닙니다. Spring IoC 컨테이너는 이 구성 메타데이터가 작성되는 형식과는 완전히 독립적입니다. 요즘은 많은 개발자들이 Spring 애플리케이션에서 Java 기반의 구성을 선택합니다.

 

Spring configuration은 컨테이너가 관리해야 하는 하나 이상의 bean 정의로 구성됩니다. XML 기반 구성 메타데이터는 최상위 <bean/> 요소 내의 <bean/> 요소로 이러한 빈을 구성합니다. Java 기반 구성 메타데이터는 일반적으로 @Configuration 클래스 내의 @Bean 메서드를 사용합니다.

 

이러한 빈 정의는 애플리케이션을 구성하는 실제 객체에 해당합니다. 일반적으로 서비스 계층 객체, 레포지토리나 데이터 액세스 객체(DAO)와 같은 영속성 계층 객체, 웹 컨트롤러와 같은 프레젠테이션 객체, JPA EntityManagerFactory, JMS 큐 등의 인프라 객체를 정의합니다. **일반적으로 도메인 객체는 세밀하게 컨테이너에서 구성하지 않습니다. 도메인 객체의 생성과 로딩은 대개 레포지토리와 비즈니스 로직의 책임이기 때문입니다.**

 

다음은 XML 기반 구성 메타데이터의 기본 구조를 보여주는 예시입니다. id 속성은 개별 빈 정의를 식별하는 문자열입니다. class 속성은 빈의 유형을 정의하며, 전체 패키지 경로를 사용합니다. id 속성 값은 협업(collaboration)하는 객체를 참조하는 데 사용될 수 있습니다.

 

<?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="userService" class="com.example.UserService">
		<property name="userRepository" ref="userRepository" />
	</bean>

	<bean id="userRepository" class="com.example.UserRepositoryImpl">
		<!-- collaborators and configuration for this bean go here -->
	</bean>
    
    <!-- more bean definitions go here -->
</beans>

 

아래 자바 코드는 위 XML과 동일한 내용을 갖는 Java 기반 구성 메타데이터 코드입니다.

@Configuration
public class AppConfig {
	
	@Bean
	public UserService userService(UserRepository userRepository) {
		UserService userService = new UserService();
		userService.setUserRepository(userRepository);
		return userService;
	}
	
	@Bean
	public UserRepository userRepository() {
		return new UserRepositoryImpl();
	}
	
}

 

 

** "일반적으로 도메인 객체는 세밀하게 컨테이너에서 구성하지 않습니다. 도메인 객체의 생성과 로딩은 대개 레포지토리와 비즈니스 로직의 책임이기 때문입니다."

 

이 문장은 스프링 프레임워크에서의 관례와 모범 사례에 대한 설명입니다. 도메인 객체는 일반적으로 스프링의 IoC 컨테이너에서 세밀하게 구성되지 않고, 레포지토리와 비즈니스 로직에서 책임을 가지고 생성되고 로딩되어야 한다는 의미입니다. 

 

도메인 객체는 주로 비즈니스 도메인에서 중요한 개념을 표현하고, 도메인 규칙을 구현하는 역할을 합니다. 이러한 도메인 객체는 도메인 모델의 핵심 부분이며, 도메인의 의미와 도메인 규칙을 가장 잘 표현해야 합니다. 이런 이유로 도메인 객체는 비즈니스 로직과 레포지토리에서 관리되는 것이 일반적입니다. 

 

일반적으로 도메인 객체는 생성과 초기화에 대한 책임을 갖고 있으며, 이는 레포지토리나 비즈니스 로직에서 처리됩니다. 레포지토리는 데이터베이스와의 상호작용을 담당하고, 비즈니스 로직은 도메인 객체들 간의 상호작용과 비즈니스 규칙의 실행을 담당합니다. 따라서 도메인 객체의 생성과 초기화는 이러한 책임을 가진 클래스에서 처리하는 것이 일반적입니다. 

 

스프링에서는 도메인 객체를 레포지토리와 비즈니스 로직에서 생성하고 활용하며, 이를 위해 스프링의 IoC 컨테이너에 의존하지 않습니다. 대신, 레포지토리나 비즈니스 로직에서 필요한 도메인 객체를 직접 생성하고 의존성을 주입하여 사용합니다. 이렇게 함으로써 도메인 객체는 독립적으로 테스트하고 유지보수할 수 있으며, 도메인 규칙을 명확하게 표현할 수 있습니다.

 

Instantiating a Container

ApplicationContext 생성자에 제공되는 위치 경로는 컨테이너가 로컬 파일 시스템, Java CLASSPATH 등과 같은 다양한 외부 리소스로부터 구성 메타데이터를 로드할 수 있도록 하는 문자열입니다. 아래와 같이 작성될 수 있습니다.

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

 

다음 예는 서비스 계층 개체(services.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">

	<!-- services -->

	<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
		<property name="accountDao" ref="accountDao"/>
		<property name="itemDao" ref="itemDao"/>
		<!-- additional collaborators and configuration for this bean go here -->
	</bean>

	<!-- more bean definitions for services go here -->

</beans>

 

다음 예는 데이터 액세스 개체(daos.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="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>

 

앞의 예에서 서비스 계층은 petSoreServiceImpl 클래스와 JpaAccountDao 및 JpaItemDao 유형의 두 데이터 액세스 개체로 구성됩니다. property name 요소는 JavaBean 속성의 이름을 참조하고 ref 요소는 다른 bean 정의의 이름을 참조합니다. id와 ref 요소 간의 이러한 연결은 개체 간의 종속성을 나타냅니다. 

 

Composing XML-based Configuration Metadata

Bean 정의가 여러 XML 파일에 걸쳐 있는 것이 유용할 수 있습니다. 종종 각 개별 XML 구성 파일은 아키텍처의 논리적 계층 또는 모듈을 나타냅니다. 애플리케이션 컨텍스트 생성자를 사용하여 이러한 모든 XML 조각에서 빈 정의를 로드할 수 있습니다. 앞에서 보여준 것처럼 여러 개의 리소스 위치를 인수로 받거나 <import/> 요소를 사용하여 다른 파일에서 bean 정의를 로드할 수 있습니다. 다음 예시는 이를 보여줍니다.

 

<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 세 개의 파일에서 외부 bean 정의를 로드합니다. 모든 위치 경로는 가져오는 파일과의 상대적인 위치로 해석되므로, services.xml은 가져오는 파일과 동일한 디렉토리 또는 클래스 패스 위치에 있어야 하며, messageSource.xml과 themeSource.xml은 가져오는 파일의 하위 resources 위치에 있어야 합니다. 앞에 슬래시가 있어도 무시되는 것을 볼 수 있습니다. 그러나 이러한 경로가 상대적이기 때문에 슬래시를 사용하지 않는 것이 좋은 습관입니다.

 

Using the Container

ApplicationContext는 getBean() 메서드를 사용하여 Bean의 인스턴스를 검색할 수 있습니다. ApplicationContext를 사용하면 다음 예제와 같이 빈 정의를 읽고 액세스할 수 있습니다.

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

 

Groovy 구성을 사용하면 부트스트래핑이 매우 유사해 보입니다. Groovy를 인식하는(또한 XML bean 정의도 인식하는) 다른 컨텍스트 구현 클래스가 있습니다. 다음 예는 Groovy configuration을 보여줍니다.

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

 

가장 유연한 변형은 reader delegates와 결합된 GenericApplicationContext입니다. 예를 들어, XML 파일의 경우 XmlBeanDefinitionReader와 함께 사용됩니다.

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

 

다음 예제와 같이 Groovy 파일에 GroovyBeanDefinitionReader를 사용할 수도 있습니다.

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

 

다양한 구성 소스에서 빈 정의를 읽어 동일한 ApplicationContext에서 이러한 reader delegates를 mix and match시킬 수 있습니다.

 

그런 다음 getBean을 사용하여 Bean의 인스턴스를 검색할 수 있습니다. ApplicationContext 인터페이스에는 bean을 검색하기 위한 몇 가지 다른 메서드가 있지만 이상적으로는 애플리케이션 코드에서 이러한 매서드를 사용하지 않아야 합니다. 실제로 애플리케이션 코드에는 getBean() 메서드에 대한 호출이 전혀 없어야 하며 따라서 Spring API에 대한 종속성이 전혀 없어야 합니다.

 

 

 

 

 


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 objects i

docs.spring.io

'스프링' 카테고리의 다른 글

Spring IoC 컨테이너 - 2  (0) 2023.07.15