공식 문서
Introduction to the Spring IoC Container and Beans
이 장에서는 Spring Framework가 제어의 역전(IoC) 원칙을 구현하는 방법에 대해 다룹니다. IoC는 종속성 주입(DI)으로도 알려져 있습니다. 이것은 객체가 자신의 의존성(즉, 그들이 함께 작업하는 다른 객체들)을 오직 생성자 인자, 팩토리 메소드로 전달된 인자, 또는 객체가 생성되거나 팩토리 메소드에서 리턴된 후에 객체 인스턴스에 설정된 속성을 통해서만 정의하는 과정입니다. 그런 다음 컨테이너는 빈을 생성할 때 그 의존성을 주입합니다. 이 과정은 근본적으로 빈이 클래스의 직접 생성 또는 서비스 로케이터 패턴과 같은 메커니즘을 사용하여 자신의 의존성의 인스턴스화나 위치를 제어하는 것의 반대(따라서 제어 역전이라는 이름)입니다.
Spring Framework의 IoC 컨테이너를 위한 기반은 org.springframework.beans 및 org.springframework.context 패키지에 있습니다. BeanFactory 인터페이스는 어떤 유형의 객체도 관리할 수 있는 고급 구성 메커니즘을 제공합니다. ApplicationContext는 BeanFactory의 하위 인터페이스입니다. 이것은 다음을 추가합니다:
- Spring의 AOP 기능과의 더 쉬운 통합
- 메시지 리소스 처리 (국제화에서 사용됨)
- 이벤트 발행
- 웹 애플리케이션에서 사용하기 위한 WebApplicationContext와 같은 애플리케이션 계층 특정 컨텍스트
간단히 말해서, BeanFactory는 구성 프레임워크와 기본 기능을 제공하고, ApplicationContext는 더 많은 엔터프라이즈 특정 기능을 추가합니다. ApplicationContext는 BeanFactory의 완전한 상위 집합이며, 이 장에서 Spring의 IoC 컨테이너에 대한 설명에서만 사용됩니다. BeanFactory 대신 ApplicationContext를 사용하는 방법에 대한 자세한 정보는 BeanFactory API를 다루는 섹션을 참조하세요.
Spring에서, 여러분의 애플리케이션의 기반을 형성하고 Spring IoC 컨테이너에 의해 관리되는 객체들은 빈(beans)이라고 불립니다. 빈은 Spring IoC 컨테이너에 의해 인스턴스화되고, 조립되며, 관리되는 객체입니다. 그렇지 않으면 빈은 단순히 귀하의 애플리케이션에서 많은 객체들 중 하나입니다. 빈들과 그들 사이의 의존성은 컨테이너가 사용하는 구성 메타데이터에 반영됩니다.
Container Overview
org.springframework.context.ApplicationContext 인터페이스는 Spring IoC 컨테이너를 나타내며 빈들을 인스턴스화, 구성 및 조립하는 역할을 합니다. 컨테이너는 구성 메타데이터를 읽음으로써 어떤 객체를 인스턴스화, 구성 및 조립할지에 대한 지시를 받습니다. 구성 메타데이터는 XML, 자바 어노테이션, 또는 자바 코드로 표현됩니다. 이를 통해 애플리케이션을 구성하는 객체들과 그 객체들 간의 복잡한 상호 의존성을 표현할 수 있습니다.
ApplicationContext 인터페이스의 여러 구현체가 Spring과 함께 제공됩니다. 독립 실행형 애플리케이션에서는
ClassPathXmlApplicationContext 또는 FileSystemXmlApplicationContext의 인스턴스를 생성하는 것이 일반적입니다. XML이 구성 메타데이터를 정의하는 전통적인 형식이었지만, 컨테이너에게 추가적인 메타데이터 형식에 대한 지원을 선언적으로 활성화하기 위해 소량의 XML 구성을 제공함으로써 자바 어노테이션 또는 코드를 메타데이터 형식으로 사용하도록 지시할 수 있습니다.
대부분의 애플리케이션 시나리오에서, 하나 이상의 Spring IoC 컨테이너 인스턴스를 인스턴스화하기 위해 사용자 코드가 명시적으로 필요하지 않습니다. 예를 들어, 웹 애플리케이션 시나리오에서는 애플리케이션의 web.xml 파일에 대략 8줄 정도의 보일러플레이트 웹 서술자 XML이 일반적으로 충분합니다(웹 애플리케이션을 위한 편리한 ApplicationContext 인스턴스화 참조). Eclipse 기반 개발 환경인 Spring Tools for Eclipse를 사용한다면, 몇 번의 마우스 클릭이나 키 입력으로 이러한 보일러플레이트 구성을 쉽게 생성할 수 있습니다.
다음 다이어그램은 Spring이 어떻게 작동하는지에 대한 고수준의 관점을 보여줍니다. ApplicationContext가 생성되고 초기화된 후, 구성 메타데이터와 결합된 애플리케이션 클래스를 통해 완전히 구성되고 실행 가능한 시스템 또는 애플리케이션이 있게 됩니다.
Figure 1. The Spring IoC container
Configuration Metadata
앞서 언급된 다이어그램에서 볼 수 있듯이, Spring IoC 컨테이너는 구성 메타데이터의 한 형태를 사용합니다. 이 구성 메타데이터는 애플리케이션 개발자가 Spring 컨테이너에게 애플리케이션 내의 객체들을 어떻게 인스턴스화, 구성 및 조립하는지를 지시하는 정보입니다.
구성 메타데이터는 전통적으로 간단하고 직관적인 XML 형식으로 제공됩니다. 이것이 바로 이 장에서 Spring IoC 컨테이너의 핵심 개념과 기능을 전달하기 위해 주로 사용하는 형식입니다.
XML 기반 메타데이터는 구성 메타데이터의 유일하게 허용된 형식이 아닙니다. Spring IoC 컨테이너 자체는 실제로 이 구성 메타데이터가 작성되는 형식과 완전히 분리되어 있습니다. 요즘 많은 개발자들이 자신의 Spring 애플리케이션에 대해 자바 기반 구성을 선택하고 있습니다.
Spring 컨테이너와 함께 다른 형태의 메타데이터를 사용하는 방법에 대한 정보는 다음을 참조하세요:
- 어노테이션 기반 구성: 어노테이션 기반 구성 메타데이터를 사용하여 빈을 정의합니다.
- 자바 기반 구성: XML 파일 대신 자바를 사용하여 애플리케이션 클래스 외부에서 빈을 정의합니다. 이러한 기능을 사용하려면, @Configuration, @Bean, @Import, @DependsOn 어노테이션을 참조하세요.
Spring 구성은 컨테이너가 관리해야 하는 최소한 하나 이상의 빈 정의로 구성됩니다. XML 기반 구성 메타데이터는 최상위 <beans/> 요소 내의 <bean/> 요소로 이러한 빈들을 구성합니다. 자바 구성은 일반적으로 @Configuration 클래스 내의 @Bean으로 주석이 달린 메소드를 사용합니다.
이러한 빈 정의는 애플리케이션을 구성하는 실제 객체에 해당합니다. 일반적으로, 서비스 계층 객체, 저장소 계층 객체(예: 리포지토리나 데이터 접근 객체(DAO)), 웹 컨트롤러와 같은 프레젠테이션 객체, JPA EntityManagerFactory, JMS 큐 등과 같은 인프라 객체를 정의합니다. 일반적으로, 세부적인 도메인 객체는 컨테이너에서 구성하지 않으며, 이는 보통 리포지토리와 비즈니스 로직이 도메인 객체를 생성하고 로드하는 책임이기 때문입니다.
다음 예시는 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="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
- id 속성은 개별 빈 정의를 식별하는 문자열입니다.
- class 속성은 빈의 유형을 정의하며 완전한 클래스 이름을 사용합니다.
id 속성의 값은 협력하는 객체를 참조하는 데 사용될 수 있습니다. 이 예제에서는 협력하는 객체를 참조하기 위한 XML이 표시되지 않았습니다. 더 많은 정보는 '의존성' 부분에서 확인할 수 있습니다.
Instantiating a Container
ApplicationContext 생성자에 제공된 위치 경로 또는 경로들은 컨테이너가 로컬 파일 시스템, 자바 CLASSPATH 등과 같은 다양한 외부 리소스에서 구성 메타데이터를 로드할 수 있게 하는 리소스 문자열입니다.
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
Spring의 IoC 컨테이너에 대해 배운 후, URI 문법으로 정의된 위치에서 InputStream을 읽기 위한 편리한 메커니즘을 제공하는 Spring의 Resource 추상화(자원 부분에서 설명됨)에 대해 더 알고 싶어질 수 있습니다. 특히, Resource 경로는 애플리케이션 컨텍스트 구성에 사용되며, 이는 애플리케이션 컨텍스트 및 리소스 경로 부분에서 설명됩니다.
다음 예시는 서비스 계층 객체(service.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>
앞서의 예시에서 서비스 계층은 PetStoreServiceImpl 클래스와 JPA 객체-관계 매핑 표준을 기반으로 한 두 종류의 데이터 접근 객체, JpaAccountDao와 JpaItemDao로 구성됩니다. property name 요소는 자바빈 속성의 이름을 나타내며, ref 요소는 다른 빈 정의의 이름을 참조합니다. 이 id와 ref 요소 사이의 연결은 협력하는 객체들 사이의 의존성을 나타냅니다. 객체의 의존성을 구성하는 세부 사항은 '의존성' 부분에서 확인할 수 있습니다.
Composing XML-based Configuration Metadata
생략...
The Groovy Bean Definition DSL
생략...
Using the Container
ApplicationContext는 다양한 빈과 그 의존성을 관리하는 레지스트리를 유지할 수 있는 고급 팩토리의 인터페이스입니다. T getBean(String name, Class<T> requiredType) 메소드를 사용하여 빈 인스턴스를 검색할 수 있습니다.
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();
이와 같은 리더 대리자들을 동일한 ApplicationContext에서 혼합하고 일치시킬 수 있으며, 다양한 구성 소스에서 빈 정의를 읽을 수 있습니다.
그런 다음 getBean을 사용하여 빈의 인스턴스를 검색할 수 있습니다. ApplicationContext 인터페이스에는 빈을 검색하기 위한 몇 가지 다른 메소드가 있지만, 이상적으로 귀하의 애플리케이션 코드는 그것들을 사용해서는 안 됩니다. 실제로, 귀하의 애플리케이션 코드는 getBean() 메소드를 전혀 호출해서는 안 되며, 따라서 Spring API에 대한 의존성도 전혀 없어야 합니다. 예를 들어, Spring의 웹 프레임워크와의 통합은 컨트롤러와 JSF 관리 빈과 같은 다양한 웹 프레임워크 컴포넌트에 대한 의존성 주입을 제공하며, 메타데이터(예: 자동 연결 어노테이션)를 통해 특정 빈에 대한 의존성을 선언할 수 있게 해줍니다.
Bean Overview
Spring IoC 컨테이너는 하나 이상의 빈을 관리합니다. 이 빈들은 컨테이너에 제공하는 구성 메타데이터(예: XML <bean/> 정의 형식)를 통해 생성됩니다.
컨테이너 자체 내에서 이러한 빈 정의들은 BeanDefinition 객체로 표현되며, 이 객체들은 (다른 정보들 중에서) 다음과 같은 메타데이터를 포함합니다:
1. 패키지-한정 클래스 이름: 일반적으로 정의되는 빈의 실제 구현 클래스입니다.
2. 빈의 행동 구성 요소들로, 빈이 컨테이너에서 어떻게 행동해야 하는지를 나타냅니다 (scope, life cycle callback 등).
3. 빈이 작업을 수행하는 데 필요한 다른 빈들에 대한 참조들. 이러한 참조들은 협력자 또는 의존성이라고도 합니다.
4. 새로 생성된 객체에 설정할 다른 구성 설정들 — 예를 들어, 풀의 크기 제한 또는 연결 풀을 관리하는 빈에서 사용할 연결의 수와 같은 것들입니다.
이 메타데이터는 각 빈 정의를 구성하는 일련의 속성들로 변환됩니다. 다음 표는 이러한 속성들을 설명합니다:
Table 1. The bean definitionPropertyExplained in
Class | Instantiating Beans |
Name | Naming Beans |
Scope | Bean Scopes |
Constructor arguments | Dependency Injection |
Properties | Dependency Injection |
Autowiring mode | Autowiring Collaborators |
Lazy initialization mode | Lazy-initialized Beans |
Initialization method | Initialization Callbacks |
Destruction method | Destruction Callbacks |
특정 빈을 생성하는 방법에 대한 정보를 포함하는 빈 정의 외에도, ApplicationContext 구현체는 사용자가 컨테이너 외부에서 생성한 기존 객체들의 등록을 허용합니다. 이는 getBeanFactory() 메소드를 통해 ApplicationContext의 BeanFactory에 접근하여 수행되며, 이 메소드는 DefaultListableBeanFactory 구현체를 리턴합니다. DefaultListableBeanFactory는 registerSingleton(..) 및 registerBeanDefinition(..) 메소드를 통해 이러한 등록을 지원합니다. 그러나, 일반적인 애플리케이션은 정규 빈 정의 메타데이터를 통해 정의된 빈들만으로 작업합니다.
Bean 메타데이터와 수동으로 제공된 싱글톤 인스턴스는 컨테이너가 자동 연결 및 기타 내부 검사 단계에서 제대로 추론할 수 있도록 가능한 한 빨리 등록되어야 합니다. 기존 메타데이터와 기존 싱글톤 인스턴스를 어느 정도까지 덮어쓰는 것이 지원되지만, 런타임에 새로운 빈을 등록하는 것(팩토리에 대한 실시간 접근과 동시에)은 공식적으로 지원되지 않으며, 동시 접근 예외, 빈 컨테이너의 일관성 없는 상태 또는 둘 다 발생할 수 있습니다.
Naming Beans
모든 빈은 하나 이상의 식별자를 가집니다. 이러한 식별자는 빈을 호스팅하는 컨테이너 내에서 고유해야 합니다. 빈은 보통 하나의 식별자만 가지지만, 둘 이상이 필요한 경우 추가 식별자는 별칭으로 간주될 수 있습니다.
XML 기반 구성 메타데이터에서는 id 속성, name 속성 또는 둘 다를 사용하여 빈 식별자를 지정합니다. id 속성을 사용하면 정확히 하나의 id를 지정할 수 있습니다. 전통적으로 이러한 이름은 영숫자(myBean, someService 등)이지만, 특수 문자도 포함될 수 있습니다. 빈에 대한 다른 별칭을 도입하고 싶다면, name 속성에도 쉼표(,), 세미콜론(;), 또는 공백으로 구분하여 지정할 수 있습니다. id 속성이 xsd:string 타입으로 정의되어 있지만, 빈 id의 고유성은 XML 파서가 아닌 컨테이너에 의해 강제됩니다.
빈에 이름이나 id를 제공할 필요는 없습니다. 이름이나 id를 명시적으로 제공하지 않으면, 컨테이너는 그 빈에 대해 고유한 이름을 생성합니다. 그러나 그 빈을 ref 요소나 서비스 로케이터 스타일 조회를 통해 이름으로 참조하려면 이름을 제공해야 합니다. 이름을 제공하지 않는 동기는 내부 빈 사용과 자동 연결 협력자와 관련이 있습니다.
빈 명명 규칙
빈을 명명할 때 표준 자바 인스턴스 필드 이름 규칙을 사용하는 것이 관례입니다. 즉, 빈 이름은 소문자로 시작하며 거기서부터 카멜 케이스를 사용합니다. 이러한 이름의 예로는 accountManager, accountService, userDao, loginController 등이 있습니다. 빈을 일관되게 명명하면 구성을 읽고 이해하기가 더 쉬워집니다. 또한, Spring AOP를 사용한다면 이름으로 연관된 빈 세트에 조언을 적용할 때 큰 도움이 됩니다.
클래스패스에서 컴포넌트 스캐닝을 사용할 때, Spring은 앞서 설명한 규칙을 따라 이름이 지정되지 않은 컴포넌트에 대해 빈 이름을 생성합니다: 기본적으로, 간단한 클래스 이름을 가져와서 첫 글자를 소문자로 바꿉니다. 그러나, 첫 번째와 두 번째 문자가 모두 대문자인 (흔하지 않은) 특별한 경우에는 원래의 대소문자가 유지됩니다. 이것은 java.beans.Introspector.decapitalize에 의해 정의된 규칙과 동일한 규칙입니다(이 경우 Spring에서 사용됨).
Aliasing a Bean outside the Bean Definition
빈 정의 자체에서는, id 속성에 의해 지정된 최대 하나의 이름과 name 속성의 여러 다른 이름을 조합하여 빈에 여러 이름을 제공할 수 있습니다. 이러한 이름들은 동일한 빈에 대한 동등한 별칭이 될 수 있으며, 각 구성 요소가 그 자체로 공통 의존성을 참조하기 위해 구성 요소에 특정한 빈 이름을 사용하는 것과 같은 일부 상황에서 유용합니다.
그러나 빈이 실제로 정의된 곳에서 모든 별칭을 지정하는 것은 항상 적절하지 않습니다. 때로는 다른 곳에서 정의된 빈에 대한 별칭을 도입하는 것이 바람직합니다. 이는 대규모 시스템에서 각 하위 시스템마다 구성이 분리되고 각 하위 시스템이 자체 객체 정의 세트를 가진 경우에 흔히 발생합니다. XML 기반 구성 메타데이터에서는 <alias/> 요소를 사용하여 이를 수행할 수 있습니다. 다음 예시는 그 방법을 보여줍니다:
<alias name="fromName" alias="toName"/>
이 경우, 같은 컨테이너 안에 있는 fromName이라는 이름의 빈은 이 별칭 정의를 사용한 후에 toName이라는 이름으로도 참조될 수 있습니다.
예를 들어, 하위 시스템 A의 구성 메타데이터는 DataSource를 subsystemA-dataSource라는 이름으로 참조할 수 있습니다. 하위 시스템 B의 구성 메타데이터는 DataSource를 subsystemB-dataSource라는 이름으로 참조할 수 있습니다. 이 두 하위 시스템을 모두 사용하는 주 애플리케이션을 구성할 때, 주 애플리케이션은 DataSource를 myApp-dataSource라는 이름으로 참조합니다. 이 세 이름이 모두 동일한 객체를 참조하도록 하려면, 구성 메타데이터에 다음과 같은 별칭 정의를 추가할 수 있습니다:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
이제 각 구성 요소와 주 애플리케이션은 고유하며 다른 정의와 충돌하지 않는 보장된 이름을 통해 dataSource를 참조할 수 있으며, 그럼에도 불구하고 동일한 빈을 참조하게 됩니다.
Java-configuration
If you use Java Configuration, the @Bean annotation can be used to provide aliases. See Using the @Bean Annotation for details.
Instantiating Beans
빈 정의는 본질적으로 하나 이상의 객체를 생성하기 위한 레시피입니다. 컨테이너는 요청된 이름을 가진 빈에 대한 레시피를 확인하고, 해당 빈 정의에 의해 캡슐화된 구성 메타데이터를 사용하여 실제 객체를 생성(또는 획득)합니다.
XML 기반 구성 메타데이터를 사용하는 경우, <bean/> 요소의 class 속성에서 인스턴스화할 객체의 유형(또는 클래스)을 지정합니다. 이 class 속성(내부적으로는 BeanDefinition 인스턴스의 Class 속성)은 보통 필수적입니다. (예외에 대해서는 인스턴스 팩토리 메소드 사용을 통한 인스턴스화 및 빈 정의 상속 참조). Class 속성을 다음 두 가지 방법 중 하나로 사용할 수 있습니다:
- 일반적으로, 컨테이너가 직접 생성자를 반사적으로 호출하여 빈을 생성하는 경우에 구성할 빈 클래스를 지정합니다. 이는 Java 코드에서 new 연산자를 사용하는 것과 다소 유사합니다.
- 컨테이너가 클래스의 정적 팩토리 메소드를 호출하여 빈을 생성하는 덜 일반적인 경우에는 객체를 생성하기 위해 호출되는 정적 팩토리 메소드를 포함하는 실제 클래스를 지정합니다. 정적 팩토리 메소드 호출에서 반환된 객체 유형은 동일한 클래스일 수도 있고 전혀 다른 클래스일 수도 있습니다.
Nested class names
중첩된 클래스에 대한 빈 정의를 구성하고자 할 때, 중첩 클래스의 바이너리 이름 또는 소스 이름 중 하나를 사용할 수 있습니다. 예를 들어, com.example 패키지에 SomeThing이라는 클래스가 있고, 이 SomeThing 클래스에 OtherThing이라는 정적 중첩 클래스가 있다면, 달러 기호($) 또는 점(.)으로 구분할 수 있습니다. 따라서 빈 정의에서 class 속성의 값은 com.example.SomeThing$OtherThing 또는 com.example.SomeThing.OtherThing이 될 수 있습니다.
Instantiation with a Constructor
생성자 접근 방식으로 빈을 생성할 때, 모든 일반 클래스는 Spring에서 사용하고 호환될 수 있습니다. 즉, 개발 중인 클래스는 특정 인터페이스를 구현하거나 특정 방식으로 코딩될 필요가 없습니다. 단순히 빈 클래스를 지정하는 것으로 충분합니다. 그러나 특정 빈에 대해 사용하는 IoC의 유형에 따라, 기본(빈) 생성자가 필요할 수 있습니다.
Spring IoC 컨테이너는 원하는 거의 모든 클래스를 관리할 수 있습니다. 오직 진정한 JavaBeans에만 한정되지 않습니다. 대부분의 Spring 사용자들은 컨테이너의 속성을 모델링한 적절한 세터와 게터를 가진 기본적인(인자 없는) 생성자만 있는 실제 JavaBeans를 선호합니다. 또한, 컨테이너에 좀 더 이국적인 비-빈 스타일 클래스를 가질 수도 있습니다. 예를 들어, JavaBean 사양을 전혀 준수하지 않는 레거시 연결 풀을 사용해야 하는 경우에도 Spring은 그것을 관리할 수 있습니다.
XML 기반 구성 메타데이터를 사용하여 다음과 같이 빈 클래스를 지정할 수 있습니다:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
생성자에 인자를 제공하는 메커니즘(필요한 경우)에 대한 세부 사항과 객체가 생성된 후 객체 인스턴스 속성을 설정하는 방법에 대해서는 ' Injecting Dependencies '을 참조하십시오.
Instantiation with a Static Factory Method
When defining a bean that you create with a static factory method, use the class attribute to specify the class that contains the static factory method and an attribute named factory-method to specify the name of the factory method itself. You should be able to call this method (with optional arguments, as described later) and return a live object, which subsequently is treated as if it had been created through a constructor. One use for such a bean definition is to call static factories in legacy code. The following bean definition specifies that the bean will be created by calling a factory method. The definition does not specify the type (class) of the returned object, but rather the class containing the factory method. In this example, the createInstance() method must be a static method. The following example shows how to specify a factory method:
정적 팩토리 메소드로 인스턴스화
정적 팩토리 메소드로 생성하는 빈을 정의할 때는, class 속성을 사용하여 정적 팩토리 메소드를 포함하는 클래스를 지정하고, factory-method라는 이름의 속성으로 팩토리 메소드 자체의 이름을 지정합니다. 이 메소드는 (나중에 설명할 선택적 인자를 사용하여) 호출될 수 있어야 하며, 활성 객체를 반환해야 합니다. 이후 이 객체는 생성자를 통해 생성된 것처럼 처리됩니다. 이러한 빈 정의의 사용 사례 중 하나는 레거시 코드에서 정적 팩토리를 호출하는 것입니다.
다음 빈 정의는 빈이 팩토리 메소드를 호출하여 생성될 것임을 명시합니다. 정의는 반환되는 객체의 유형(클래스)을 지정하지 않고, 대신 팩토리 메소드를 포함하는 클래스를 지정합니다. 이 예시에서, createInstance() 메소드는 정적 메소드여야 합니다. 다음 예시는 팩토리 메소드를 지정하는 방법을 보여줍니다:
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
다음 예시는 앞서의 빈 정의와 함께 작동할 수 있는 클래스를 보여줍니다:
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
팩토리 메소드에 (선택적) 인자를 제공하는 메커니즘의 세부 사항과 팩토리에서 객체가 반환된 후 객체 인스턴스 속성을 설정하는 방법에 대해서는 ' Dependencies and Configuration in Detail '를 참조하세요.
Instantiation by Using an Instance Factory Method
Similar to instantiation through a static factory method , instantiation with an instance factory method invokes a non-static method of an existing bean from the container to create a new bean. To use this mechanism, leave the class attribute empty and, in the factory-bean attribute, specify the name of a bean in the current (or parent or ancestor) container that contains the instance method that is to be invoked to create the object. Set the name of the factory method itself with the factory-method attribute. The following example shows how to configure such a bean:
정적 팩토리 메소드를 통한 인스턴스화와 유사하게, 인스턴스 팩토리 메소드를 사용한 인스턴스화는 컨테이너의 기존 빈의 비정적 메소드를 호출하여 새로운 빈을 생성합니다. 이 메커니즘을 사용하려면, class 속성을 비워두고, factory-bean 속성에서 객체를 생성하기 위해 호출될 인스턴스 메소드를 포함하는 현재(또는 부모 또는 조상) 컨테이너의 빈 이름을 지정합니다. factory-method 속성으로 팩토리 메소드 자체의 이름을 설정합니다. 다음 예시는 이러한 빈을 구성하는 방법을 보여줍니다:
<!-- the factory bean, which contains a method called createClientServiceInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
The following example shows the corresponding class:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
한 개의 팩토리 클래스가 둘 이상의 팩토리 메소드를 가질 수도 있습니다. 다음 예시에서 이를 보여줍니다:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
The following example shows the corresponding class:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
이 접근 방식은 팩토리 빈 자체가 의존성 주입(DI)을 통해 관리되고 구성될 수 있음을 보여줍니다. ' Dependencies and Configuration in Detail '를 참조하십시오.
Spring 문서에서 "factory bean"은 Spring 컨테이너에서 구성되고 인스턴스 또는 정적 팩토리 메소드를 통해 객체를 생성하는 빈을 말합니다. 반면, FactoryBean(대문자로 표기된 것에 주목)은 Spring 특정의 FactoryBean 구현 클래스를 의미합니다.
Determining a Bean’s Runtime Type
특정 빈의 런타임 유형을 결정하는 것은 간단하지 않습니다. 빈 메타데이터 정의에서 지정된 클래스는 초기 클래스 참조에 불과하며, 선언된 팩토리 메소드와 결합되거나 FactoryBean 클래스일 수 있어 빈의 런타임 유형이 다를 수 있습니다. 또는 인스턴스 수준의 팩토리 메소드의 경우에는 전혀 설정되지 않고 대신 지정된 factory-bean 이름을 통해 해결됩니다. 추가적으로, AOP 프록싱은 빈 인스턴스를 인터페이스 기반 프록시로 래핑하여 타깃 빈의 실제 유형(구현된 인터페이스만)을 제한적으로 노출할 수 있습니다.
특정 빈의 실제 런타임 유형에 대해 알아보는 권장 방법은 지정된 빈 이름에 대한 BeanFactory.getType 호출입니다. 이는 위에서 언급한 모든 경우를 고려하고 BeanFactory.getBean 호출이 동일한 빈 이름에 대해 반환할 객체의 유형을 반환합니다.
Dependencies
전형적인 기업용 애플리케이션은 단일 객체(또는 Spring 용어로는 빈)로 구성되지 않습니다. 가장 간단한 애플리케이션조차도 몇 개의 객체가 협력하여 최종 사용자가 일관된 애플리케이션으로 인식하는 것을 제공합니다. 다음 섹션에서는 독립적인 여러 빈 정의를 정의하는 것에서 시작하여 객체들이 협력하여 목표를 달성하는 완전히 구현된 애플리케이션으로 나아가는 방법에 대해 설명합니다.
Dependency Injection
의존성 주입(DI)은 객체들이 그들의 의존성(즉, 그들이 함께 작업하는 다른 객체들)을 오직 생성자 인자, 팩토리 메소드로의 인자, 또는 객체가 생성되거나 팩토리 메소드에서 반환된 후에 객체 인스턴스에 설정된 속성을 통해서만 정의하는 과정입니다. 그런 다음 컨테이너는 빈을 생성할 때 그 의존성을 주입합니다. 이 과정은 근본적으로 빈이 자신의 의존성의 인스턴스화나 위치를 직접 클래스의 생성 또는 서비스 로케이터 패턴을 사용하여 스스로 제어하는 것과 반대입니다(따라서 제어의 역전이라는 이름).
DI 원칙으로 코드는 더 깔끔해지며, 객체에 의존성이 제공될 때 결합이 더 효과적으로 해소됩니다. 객체는 자신의 의존성을 찾지 않으며, 의존성의 위치나 클래스를 알지 못합니다. 결과적으로, 특히 의존성이 인터페이스나 추상 기본 클래스인 경우, 단위 테스트에서 스텁이나 목 구현을 사용할 수 있으므로 클래스가 테스트하기 더 쉬워집니다.
DI에는 주로 두 가지 주요 변형이 있습니다:
- 생성자 기반 의존성 주입
- 세터 기반 의존성 주입
Constructor-based Dependency Injection
생성자 기반 DI는 컨테이너가 의존성을 나타내는 여러 인자를 가진 생성자를 호출함으로써 이루어집니다. 특정 인자를 가진 정적 팩토리 메소드를 호출하여 빈을 구성하는 것도 거의 동일하며, 이 논의에서는 생성자와 정적 팩토리 메소드로의 인자를 유사하게 다룹니다. 다음 예시는 생성자 주입으로만 의존성 주입될 수 있는 클래스를 보여줍니다:
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on a MovieFinder
private final MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
이 클래스에 특별한 점은 없습니다. 이것은 컨테이너 특정 인터페이스, 기본 클래스 또는 어노테이션에 대한 의존성이 없는 POJO(Plain Old Java Object)입니다.
Constructor Argument Resolution
생성자 아규먼트 해석은 아규먼트의 타입을 사용하여 일치시킵니다. 빈 정의의 생성자 인자에 잠재적인 모호성이 존재하지 않는 경우, 빈 정의에서 생성자 인자가 정의된 순서가 빈이 인스턴스화될 때 적절한 생성자에 그 인자들이 제공되는 순서입니다. 다음과 같은 클래스를 고려해보세요:
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
ThingTwo와 ThingThree 클래스가 상속 관계에 있지 않다고 가정하면, 잠재적인 모호성이 존재하지 않습니다. 따라서, 다음 구성은 잘 작동하며, <constructor-arg/> 요소에서 생성자 인자의 인덱스나 타입을 명시적으로 지정할 필요가 없습니다.
<beans>
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg ref="beanTwo"/>
<constructor-arg ref="beanThree"/>
</bean>
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
</beans>
다른 빈을 참조할 때는 타입이 알려져 있어 일치시킬 수 있습니다(앞의 예시와 같은 경우). <value>true</value>와 같은 간단한 타입을 사용할 때, Spring은 값을 타입을 결정할 수 없으므로 도움 없이는 타입에 의한 일치를 할 수 없습니다. 다음과 같은 클래스를 고려해보세요:
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private final int years;
// The Answer to Life, the Universe, and Everything
private final String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
앞선 시나리오에서, 다음 예시와 같이 type 속성을 사용하여 생성자 인자의 타입을 명시적으로 지정하면, 컨테이너는 간단한 타입에 대해 타입 매칭을 사용할 수 있습니다:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
다음 예시와 같이 index 속성을 사용하여 생성자 인자의 인덱스를 명시적으로 지정할 수 있습니다:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
여러 간단한 값의 모호성을 해결하는 것 외에도, 인덱스를 지정하면 생성자에 동일한 유형의 두 인자가 있는 경우의 모호성도 해결할 수 있습니다.
Setter-based Dependency Injection
생략...
Constructor-based or setter-based DI?
생성자 기반과 세터 기반 DI를 혼합하여 사용할 수 있으므로, 필수 의존성에는 생성자를 사용하고 선택적 의존성에는 세터 메소드나 구성 메소드를 사용하는 것이 좋은 지침입니다. 세터 메소드에 @Autowired 어노테이션을 사용하면 속성을 필수 의존성으로 만들 수 있지만, 인자에 대한 프로그래매틱 검증을 포함하는 생성자 주입이 선호됩니다. Spring 팀은 일반적으로 생성자 주입을 권장합니다. 이는 애플리케이션 구성 요소를 불변 객체로 구현할 수 있게 하며 필수 의존성이 null이 아님을 보장합니다. 또한, 생성자로 주입된 구성 요소는 항상 완전히 초기화된 상태로 클라이언트(호출) 코드에 반환됩니다. 한편, 생성자 인자가 많은 것은 나쁜 코드 냄새로, 클래스가 너무 많은 책임을 가지고 있으며 관심사의 적절한 분리를 더 잘 다루기 위해 리팩토링되어야 함을 나타냅니다. 세터 주입은 클래스 내에서 합리적인 기본값을 할당할 수 있는 선택적 의존성에 주로 사용되어야 합니다. 그렇지 않으면 의존성을 사용하는 코드 곳곳에서 null이 아닌지 확인해야 합니다. 세터 주입의 이점 중 하나는 세터 메소드가 해당 클래스의 객체를 나중에 재구성하거나 재주입하기 적합하게 만든다는 것입니다. 따라서 JMX MBeans를 통한 관리는 세터 주입을 위한 강력한 사용 사례입니다. 특정 클래스에 가장 의미 있는 DI 스타일을 사용하세요. 때때로 소스가 없는 타사 클래스를 다룰 때 선택이 이미 결정될 수 있습니다. 예를 들어, 타사 클래스가 세터 메소드를 노출하지 않는 경우, 생성자 주입이 유일하게 가능한 DI 형태일 수 있습니다.
Dependency Resolution Process
컨테이너는 다음과 같이 빈 의존성 해결을 수행합니다:
- ApplicationContext가 생성되고, 모든 빈을 설명하는 구성 메타데이터로 초기화됩니다. 구성 메타데이터는 XML, 자바 코드 또는 어노테이션을 통해 지정될 수 있습니다.
- 각 빈에 대해, 그 의존성은 속성, 생성자 인자 또는 정적 팩토리 메소드에 대한 인자(일반 생성자 대신 사용하는 경우)의 형태로 표현됩니다. 이러한 의존성은 빈이 실제로 생성될 때 제공됩니다.
- 각 속성 또는 생성자 인자는 설정할 값의 실제 정의이거나 컨테이너의 다른 빈에 대한 참조입니다.
- 값으로 되어 있는 각 속성 또는 생성자 인자는 지정된 형식에서 해당 속성 또는 생성자 인자의 실제 타입으로 변환됩니다. 기본적으로, Spring은 문자열 형식으로 제공된 값을 int, long, String, boolean 등과 같은 모든 내장 타입으로 변환할 수 있습니다.
Spring 컨테이너는 컨테이너가 생성될 때 각 빈의 구성을 검증합니다. 그러나 빈 속성 자체는 빈이 실제로 생성될 때까지 설정되지 않습니다. 싱글톤 범위로 지정되고 사전 인스턴스화(기본값)가 설정된 빈들은 컨테이너가 생성될 때 생성됩니다. 범위는 '빈 범위'에서 정의됩니다. 그렇지 않으면 빈은 요청될 때만 생성됩니다. 빈의 생성은 빈의 의존성과 그 의존성의 의존성(그리고 이어지는 의존성)이 생성되고 할당됨에 따라 빈의 그래프를 생성할 수 있습니다. 이러한 의존성 간의 해결 불일치는 늦게 나타날 수 있습니다 — 즉, 영향을 받는 빈의 첫 생성 시에 나타납니다.
Circular dependencies
순환 의존성 주로 생성자 주입을 사용하는 경우, 해결할 수 없는 순환 의존성 시나리오를 만들 수 있습니다. 예를 들어: 클래스 A가 생성자 주입을 통해 클래스 B의 인스턴스를 필요로 하고, 클래스 B가 생성자 주입을 통해 클래스 A의 인스턴스를 필요로 하는 경우입니다. 클래스 A와 B에 대한 빈을 서로 주입하도록 구성하면, Spring IoC 컨테이너는 런타임에 이 순환 참조를 감지하고 BeanCurrentlyInCreationException을 던집니다. 한 가지 가능한 해결책은 일부 클래스의 소스 코드를 수정하여 생성자가 아닌 세터를 통해 구성하도록 하는 것입니다. 또는 생성자 주입을 피하고 세터 주입만 사용합니다. 즉, 권장되지는 않지만, 세터 주입으로 순환 의존성을 구성할 수 있습니다. 일반적인 경우(순환 의존성이 없는 경우)와 달리, 빈 A와 빈 B 간의 순환 의존성은 빈 중 하나가 완전히 초기화되기 전에 다른 하나에 주입되도록 강제합니다(전형적인 닭과 달걀 시나리오).
일반적으로 Spring이 올바른 일을 하도록 신뢰할 수 있습니다. Spring은 컨테이너 로드 시점에 존재하지 않는 빈에 대한 참조와 순환 의존성과 같은 구성 문제를 감지합니다. Spring은 빈이 실제로 생성될 때 가능한 한 늦게 속성을 설정하고 의존성을 해결합니다. 이는 정확하게 로드된 Spring 컨테이너가 나중에 객체를 요청할 때 문제가 있는 객체나 그 의존성을 생성하는 데 문제가 있을 경우 예외를 발생시킬 수 있다는 것을 의미합니다 - 예를 들어, 빈이 누락되었거나 잘못된 속성으로 인해 예외를 던집니다. 이러한 일부 구성 문제의 잠재적으로 지연된 가시성 때문에 ApplicationContext 구현은 기본적으로 싱글톤 빈을 사전에 인스턴스화합니다. 실제로 필요하기 전에 이러한 빈을 생성하는 데 드는 일부 초기 시간과 메모리 비용을 치르더라도 ApplicationContext가 생성될 때 구성 문제를 발견하게 됩니다, 나중에 발견하는 것이 아니라. 여전히 이 기본 동작을 오버라이드하여 싱글톤 빈이 성급하게 사전 인스턴스화되는 대신 게으르게 초기화되도록 설정할 수 있습니다.
순환 의존성이 존재하지 않는 경우, 하나 이상의 협력 빈이 의존 빈에 주입될 때, 각 협력 빈은 의존 빈에 주입되기 전에 완전히 구성됩니다. 이는 빈 A가 빈 B에 의존하는 경우, Spring IoC 컨테이너가 빈 A의 세터 메소드를 호출하기 전에 빈 B를 완전히 구성한다는 것을 의미합니다. 즉, 빈이 인스턴스화됩니다(만약 사전 인스턴스화된 싱글톤이 아니라면), 그 의존성이 설정되고, 관련 생명주기 메소드(예: 구성된 init 메소드 또는 InitializingBean 콜백 메소드)가 호출됩니다.
Examples of Dependency Injection
다음 예시는 세터 기반 DI에 대한 XML 기반 구성 메타데이터를 사용합니다. Spring XML 구성 파일의 일부분은 다음과 같이 몇 가지 빈 정의를 명시합니다:
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
The following example shows the corresponding ExampleBean class:
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
앞선 예시에서는 XML 파일에 지정된 속성과 일치하도록 세터가 선언됩니다. 다음 예시는 생성자 기반 DI를 사용합니다:
<bean id="exampleBean" class="examples.ExampleBean">
<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
The following example shows the corresponding ExampleBean class:
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public ExampleBean(
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}
빈 정의에 명시된 생성자 인자들은 ExampleBean의 생성자에 인자로 사용됩니다.
이 예시의 변형을 고려해 보겠습니다. 여기서는 생성자를 사용하는 대신, Spring에게 정적 팩토리 메소드를 호출하여 객체의 인스턴스를 반환하도록 지시합니다:
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
<constructor-arg ref="anotherExampleBean"/>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
다음 예시는 해당 ExampleBean 클래스를 보여줍니다:
public class ExampleBean {
// a private constructor
private ExampleBean(...) {
...
}
// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
ExampleBean eb = new ExampleBean (...);
// some other operations...
return eb;
}
}
정적 팩토리 메소드에 대한 인자는 <constructor-arg/> 요소를 통해 제공되며, 실제로 생성자가 사용되었더라도 동일하게 적용됩니다. 팩토리 메소드에서 반환되는 클래스의 타입은 정적 팩토리 메소드를 포함하는 클래스의 타입과 동일할 필요는 없습니다(비록 이 예시에서는 그렇습니다). 인스턴스(비정적) 팩토리 메소드도 본질적으로 동일한 방식으로 사용될 수 있습니다(클래스 속성 대신 factory-bean 속성을 사용하는 점을 제외하고는), 그래서 여기서 그 세부사항에 대해 논의하지 않습니다.
'Spring Framework' 카테고리의 다른 글
토비의 스프링 1.5 (0) | 2024.04.09 |
---|---|
토비의 스프링 1.4 (0) | 2024.04.09 |
토비의 스프링 1.3 (0) | 2024.04.09 |
토비의 스프링 1. 오브젝트와 의존관계 (0) | 2024.04.09 |
Spring – JDBC Template (0) | 2024.04.09 |