IoC 컨테이너와 DI
IoC 컨테이너: 빈 팩토리와 애플리케이션 컨텍스트
스프링 애플리케이션에서는 오브젝트의 생성과 관계설정, 사용, 제거 등의 작업을 애플리케이션 코드 대신 독립된 컨테이너가 담당한다. 이를 컨테이너가 코드 대신
오브젝트에 대한 제어권을 갖고 있다고 해서 IoC라고 부른다. 그래서 스프링 컨테이너를 IoC 컨테이너라고도 한다.
스프링에선 IoC를 담당하는 컨테이너를 빈 팩토리 또는 애플리케이션 컨텍스트라고 부르기도 한다. 오브젝트의 생성과 오브젝트 사이의 런타임 관계를 설정하는
DI 관점으로 볼 때는 컨테이너를 빈 팩토리라고 한다.
IoC 컨테이너를 이용해 애플리케이션 만들기
가장 간단하게 IoC 컨테이너를 만드는 방법
StaticApplicationContext ac = new StaticApplicationContext();
이렇게 만들어진 컨테이너가 본격적인 IoC 컨테이너로서 동작하려면 POJO 클래스와 설정 메타정보가 필요하다.
POJO 클래스
각각의 POJO는 특정 기술과 스펙에서 독립적일뿐더러 의존관계에 있는 다른 POJO와 느슨한 결합을 갖도록 만들어야 한다.
각자 기능에 충실하게 독립적으로 설계된 POJO 클래스를 만들고, 결합도가 낮은 유연한 관계를 가질 수 있도록 인터페이스를 이용해 연결해주는 것까지가 IoC
컨테이너가 사용할 POJO를 준비하는 첫 단계다.
설정 메타정보
두 번째 필요한 것은 앞에서 만든 POJO 클래스들 중에 애플리케이션에서 사용할 것을 선정하고 이를 IoC 컨테이너가 제어할 수 있도록 적절한 메타정보를 만들어 제공하는
작업이다.
IoC 컨테이너의 가장 기초적인 역할은 오브젝트를 생성하고 이를 관리하는 것이다. 스프링 컨테이너가 관리하는 이런 오브젝트는 빈(bean)이라고 부른다.
IoC 컨테이너가 필요로 하는 설정 메타정보는 바로 이 빈을 어떻게 만들고 어떻게 동작하게 할 것인가에 관한 정보다.
스프링의 메타정보는 특정한 파일 포맷이나 형식에 제한되거나 종속되지 않는다. 대신 XML이든 소스코드 애노테이션이든 자바 코드이든 프로퍼티 파일이든 상관없이
BeanDefinition으로 정의되는 스프링의 설정 메타정보의 내용을 표현한 것이 있다면 무엇이든 사용 가능하다. 원본의 포맷과 구조, 자료의 특성에 맞게 읽어와
BeanDefinition 오브젝트로 변환해주는 BeanDefinitionReader가 있으면 된다. BeanDefinitionReader 인터페이스를 구현한 리더를 만들기만 하면 스프링의 설정
메타정보는 어떤 형식으로든 작성할 수 있다.
BeanDefinition 인터페이스로 정의되는, IoC 컨테이너가 사용하는 빈 메타정보
- 빈 아이디, 이름, 별칭: 빈 오브젝트를 구분할 수 있는 식별자
- 클래스 또는 클래스 이름: 빈으로 만들 POJO 클래스 또는 서비스 클래스 정보
- 스코프: 싱글톤, 프로토타입과 같은 빈의 생성 방식과 존재 범위
- 프로퍼티 값 또는 참조: DI에 사용할 프로퍼티 이름과 값 또는 참조하는 빈의 이름
- 생성자 파라미터 값 또는 참조: DI에 사용할 생성자 파라미터 이름과 값 또는 참조할 빈의 이름
- 지연된 로딩 여부, 우선 빈 여부, 자동와이어링 여부, 부모 빈 정보, 빈팩토리 이름 등
스프링 IoC 컨테이너는 각 빈에 대한 정보를 담은 설정 메타정보를 읽어들인 뒤에, 이를 참고해서 빈 오브젝트를 생성하고 프로퍼티나 생성자를 통해 의존
오브젝트를 주입해주는 DI 작업을 수행한다.
이 작업을 통해 만들어지고, DI로 연결되는 오브젝트들이 모여서 하나의 애플리케이션을 구성하고 동작하게 된다.
결국 스프링 애플리케이션이란 POJO 클래스와 설정 메타정보를 이용해 IoC 컨테이너가 만들어주는 오브젝트의 조합이라고 할 수 있다.
IoC 컨테이너는 일단 빈 오브젝트가 생성되고 관계가 만들어지면 그 뒤로는 거의 관여하지 않는다. 기본적으로 싱글톤 빈은 애플리케이션 컨텍스트의 초기화 작업
중에 모두 만들어진다.
IoC 컨테이너의 종류와 사용 방법
- StaticApplicationContext
- StaticApplicationContext는 코드를 통해 빈 메타정보를 등록하기 위해 사용한다. 스프링의 기능에 대한 학습 테스트를 만들 때를 제외하면 실제로 사용되지 않는다.
- GenericApplicationContext
- GenericApplicationContext는 가장 일반적인 애플리케이션 컨텍스트의 구현 클래스다. 실전에서 사용될 수 있는 모든 기능을 갖추고 있는 애플리케이션 컨텍스트다.
- 스프링 테스트 컨텍스트 프레임워크를 활용하는 JUnit 테스트는 테스트내에서 사용할 수 있도록 애플리케이션 컨텍스트를 자동으로 만들어주는데, 이때 생성되는 애플리케이션 컨텍스트가 바로 GenericApplicationContext다.
- GenericXmlApplicationContext
- GenericXmlApplicationContext는 XmlBeanDefinitionReader를 내장하고 있기 때문에, XML 파일을 읽어들이고 refresh() 를 통해 초기화하는 것까지 한 줄로 끝낼 수 있다.
- WebApplicationContext
계층구조 안의 모든 컨텍스트는 각자 독립적인 설정정보를 이용해 빈 오브젝트를 만들고 관리한다. 각자 독립적으로 자신이 관리하는 빈을 갖고 있긴 하지만 DI를 위해 빈을 찾을 때는 부모 애플리케이션 컨텍스트의 빈까지 모두 검색한다. 계층구조를 따라서 가장 위에 존재하는 루트 컨텍스트까지 요청이 전달된다.
중요한 건 자신의 부모 컨텍스트에게만 빈 검색을 요청하고 자식 컨텍스트에게는 요청하지 않는다. 그런 이유로 같은 레벨에 있는 형제 컨텍스트의 빈도 찾을 수 없다.
웹 애플리케이션의 IoC 컨테이너 구성
서버에서 동작하는 애플리케이션에서 스프링 IoC 컨테이너를 사용하는 방법은 크게 세 가지로 구분해볼 수 있다. 두 가지 방법은 웹 모듈 안에 컨테이너를 두는 것이고, 나머지 하나는 엔터프라이즈 애플리케이션 레벨에 두는 방법이다.
자바 서버에는 하나 이상의 웹 모듈을 배치해서 사용할 수 있다. 스프링을 사용한다면 보통 독립적으로 배치 가능한 웹 모듈(WAR) 형태로 애플리케이션을 배포한다. 하나의 웹 애플리케이션은 여러 개의 서블릿을 가질 수 있다. 몇 개의 서블릿이 중앙집중식으로 모든 요청을 다 받아서 처리하는 방식을 프론트 컨트롤러 패턴이라고 한다.
웹 애플리케이션의 컨텍스트 계층구조
웹 애플리케이션 레벨에 등록되는 컨테이너는 보통 루트 웹 애플리케이션 컨텍스트라고 불린다. 이 컨텍스트는 서블릿 레벨에 등록되는 컨테이너들의 부모 컨테이너가 되고, 일반적으로 전체 계층구조 내에서 가장 최상단에 위치한 루트 컨텍스트가 되기 때문이다.
웹 애플리케이션에는 하나 이상의 스프링 애플리케이션의 프로트 컨트롤러 역할을 하는 서블릿이 등록될 수 있다. 이 서블릿에는 각각 독립적으로 애플리케이션 컨텍스트가 만들어진다. 이런 경우 각 서블릿이 공유하게 되는 공통적인 빈들이 있을 것이고, 이런 빈들을 웹 애플리케이션 레벨의 컨텍스트에 등록하면 된다. 이런 경우 공통되는 빈들이 서블릿별로 중복돼서 생성되는 걸 방지할 수 있다.
하지만 일반적으로는 스프링의 애플리케이션 컨텍스트를 가지면서 프로트 컨트롤러 역할을 하는 서블릿은 하나만 만들어 사용한다. 그렇지만 언제든 간단히 웹 기술을 확장하거나 변경, 조합해서 사용할 수 있으므로 당장에는 스프링 서블릿 한 가지만 존재한다고 해도 계층구조로 만들어두는 것이 좋다.
웹 애플리케이션의 컨텍스트 구성 방법
- 서블릿 컨텍스트와 루트 애플리케이션 컨텍스트 계층구조
- 가장 많이 사용되는 기본적인 구성 방법이다. 스프링 웹 기술을 사용하는 경우 웹 관련 빈들은 서블릿의 컨텍스트에 두고 나머지는 루트 애플리케이션 컨텍스트에 등록한다.
- 루트 애플리케이션 컨텍스트 단일구조
- 스프링 웹 기술을 사용하지 않고 서드파티 웹 프레임워크나 서비스 엔진만을 사용해서 프레젠테이션 계층을 만든다면 스프링 서블릿을 둘 이유가 없다. 이때는 루트 애플리케이션 컨텍스트만 등록해주면 된다.
- 서블릿 컨텍스트 단일구조
- ContextLoaderListener 등록
```
org.springframework.web.context.ContextLoaderListener
</listenner-class>
</listener>
```
> ContextLoaderListener는 웹 애플리케이션이 시작할 때 자동으로 루트 애플리케이션 컨텍스트를 만들고 초기화해준다. 그리고 별다른 파라미터를 지정하지 않으면, 디폴트로 설정된 다음의 값이 적용된다.
* 애플리케이션 컨텍스트 클래스: XmlWebApplicationContext
* XML 설정파일 위치: /WEB-INF/applicationContext.xml
컨텍스트 클래스와 설정파일 위치는 서블릿 컨텍스트 파라미터를 선언해서 변경할 수 있다. ``````항목 안에 넣어주면 디폴트 설정 대신 파라미터로 지정한 내용이 적용된다.
* contextConfigLocation
```
contextConfigLocation
// 하나 이상의 XML 설정파일을 사용할 경우 여러줄에 걸쳐 넣어주거나 공백으로 분리하면 된다.
/WEB-INF/daoContext.xml
/WEB-INF/applicationContext.xml
```
서블릿 리소스 패스 대신 클래스패스로부터 설정파일을 찾게 할 수도 있다.
```
classpath:applicationContext.xml
```
> 애플리케이션의 규모가 커져서 등록해야 할 빈이 많아지면 빈 설정을 여러 개의 파일로 쪼개서 관리하는 게 편리할 수 있다. 계층별로 구분하거나 기능 모듈별로 파일을 분리해서 만드는 방법도 좋다. 물론 설정파일이 여러 개 만들어졌다고 해서 컨텍스트도 여러 개가 만들어지는 건 아니다. 하나의 루트 컨텍스트가 여러 파일의 빈 설정 메타정보를 통합해서 사용할 뿐이다.
* contextClass
ContextLoaderListener가 자동으로 생성하는 컨텍스트의 클래스는 기본적으로 XmlWebApplicationContext다. 이를 다른 애플리케이션 컨텍스트 구현 클래스로 변경하고 싶으면 contextClass 파라미터를 이용해 지정해주면 된다.
```
contextClass
org.springframework.web.context.support.AnntationConfigWebApplicationContext
```
> AnnotationConfigWebApplicationContext를 컨텍스트 클래스로 사용할 때는 contextConfigLocation 파라미터를 반드시 선언해줘야 한다. 이때는 XML 파일의 위치가 아니라 설정 메타정보를 담고 있는 클래스 또는 빈 스캐닝 패키지를 지정할 수 있다.
##### 서블릿 애플리케이션 컨텍스트 등록
스프링의 웹 기능을 지원하는 프론트 컨트롤러 서블릿은 DispatcherServlet이다. 서블릿 이름을 다르게 지정해주면 하나의 웹 애플리케이션에 여러 개의 DispatcherServlet을 등록할 수도 있다. 각 DispatcherServlet은 서블릿이 초기화될 때 자신만의 컨텍스트를 생성하고 초기화한다. 동시에 웹 애플리케이션 레벨에 등록된 루트 애플리케이션 컨텍스트를 찾아서 이를 자시느이 부모 컨텍스트로 사용한다.
* 서블릿 컨텍스트를 위한 서블릿 등록
```
spring
org.springframework.web.servlet.DispatcherServlet
1
```
* ``````
* DispatcherServlet에 의해 만들어지는 애플리케이션 컨텍스트는 모두 독립적인 네임스페이스를 갖게 된다. 이 네임스페이스는 서블릿 단위로 만들어지는 컨텍스트를 구분하는 키가 된다. 네임스페이스는 ``````으로 지정한 서블릿 이름에 -servlet을 붙여서 만든다. 서블릿 이름이 spring이라면 네임스페이스는 spring-servlet이 된다.
* 네임스페이스가 중요한 이유는 DispatcherServlet이 사용할 디폴트 XML 설정 파일의 위치를 네임스페이스를 이용해서 만들기 때문이다. 서블릿 컨텍스트가 사용할 디폴트 설정 파일은 다음과 같은 규칙으로 만들어진다.
```'/WEB-INF/' + 서블릿네임스페이스 + '.xml'```
따라서 디폴트 설정 위치는 ```/WEB-INF/spring-servlet.xml```이 된다.
* ``````
* ``````은 서블릿 컨테이너가 등록된 서블릿을 언제 만들고 초기화할지, 또 그 순서는 어떻게 되는지를 지정하는 정수 값이다. 이 항목을 아예 생략하거나 음의 정수로 넣으면 해당 서블릿은 서블릿 컨테이너가 임의로 정한 시점에서 만들어지고 초기화된다. 반대로 0 이상의 값을 넣으면 웹 애플리케이션이 시작되는 시점에서 서블릿을 로딩하고 초기화한다. 또한 여러 개의 서블릿이 등록되어 있다면 작은 수를 가진 서블릿이 우선적으로 만들어진다.
* DispatcherServlet은 서블릿의 초기화 작업 중에 스프링 컨텍스트를 생성한다. 컨텍스트의 설정이나 환경에 문제가 있다면 컨텍스트 생성 시 대부분 확인이 가능하다. 따라서 웹 애플리케이션이 시작되고 가능한 한 빨리 서블릿 컨텍스트의 초기화가 진행되는 것이 바람직하다.
###### 단일 서블릿 컨텍스트 구성방법
서블릿 컨텍스트의 파라미터 선언 방법은 루트 컨텍스트와 비슷하다. 파라미터의 선언에 `````` 대신 `````` 안에 있는 ``````을 이용한다는 점만 다르다.
```
spring
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
// 하나 이상의 XML 설정파일을 사용할 경우 여러줄에 걸쳐 넣어주거나 공백으로 분리하면 된다.
/WEB-INF/daoContext.xml
/WEB-INF/applicationContext.xml
1
```
> 루트 컨텍스트 설정과 마찬가지로 ``````에 contextClass 파라미터를 정의해서 컨텍스트 클래스도 변경할 수 있다.
### IoC/DI를 위한 빈 설정 메타정보 작성
IoC 컨테이너의 가장 기본적인 역할은 코드를 대신해서 애플리케이션을 구성하는 오브젝트를 생성하고 관리하는 것이다.
#### 빈 설정 메타정보
* 빈 설정 메타정보: BeanDefinition의 핵심 항목
이름 | 내용 | 디폴트 값
:----:|:-----:|:------
beanClass | 빈 오브젝트의 클래스 이름. 빈 오브젝트는 이 클래스의 인스턴스가 된다. | 없음. 필수항목
parentName | 빈 메타정보를 상속받을 부모 BeanDefinition의 이름. 빈의 메타정보는 계층구조로 상속할 수 있다. | 없음
factoryBeanName | 팩토리 역할을 하는 빈을 이용해 빈 오브젝트를 생성하는 경우에 팩토리 빈의 이름을 지정한다. | 없음
factoryMethodName | 다른 빈 또는 클래스의 메소드를 통해 빈 오브젝트를 생성하는 경우 그 메소드 이름을 지정한다. | 없음
scope | 빈 오브젝트의 생명주기를 결정하는 스코프를 지정한다. 크게 싱글톤과 비싱글톤 스코프로 구분할 수 있다. | 싱글톤
laxyInit | 빈 오브젝트의 생성을 최대한 지연할 것인지를 지정한다. 이 값이 true이면 컨테이너는 빈 오브젝트의 생성을 꼭 필요한 시점까지 미룬다. | false
dependsOn | 먼저 만들어져야 하는 빈을 지정할 수 있다. 빈 오브젝트의 생성 순서가 보자오대야 하는 경우 이용한다. 하나 이상의 빈 이름을 지정할 수 있다. | 없음
autowireCandidate | 명시적인 설정이 없어도 미리 정해진 규칙을 가지고 자동으로 DI 후보를 결정하는 자동와이어링의 대상으로 포함시킬지의 여부 | true
primary | 자동와이어링 작업 중에 DI 대상 후보가 여러 개가 발생하는 경우가 있다. 이때 최종 선택의 우선권을 부여할지 여부. primary가 지정된 빈이 없이 여러 개의 후보가 존재하면 자동와이어링 예외가 발생한다. | false
abstract | 메타정보 상속에만 사용할 추상 빈으로 만들지의 여부. 추상 빈이 되면 그 자체는 오브젝트가 생성되지 않고 다른 빈의 부모 빈으로만 사용된다. | false
autowireMode | 오토와이어링 전략. 이름, 타입, 생성자, 자동인식 등의 방법이 있다. | 없음
dependencyCheck | 프로퍼티 값 또는 레퍼런스가 모두 설정되어 있는지를 검증하는 작업의 종류 | 체크하지 않음
initMethod | 빈이 생성되고 DI를 마친 뒤에 실행할 초기화 메소드의 이름 | 없음
destroyMethod | 빈의 생명주기가 다 돼서 제거하기 전에 호출할 메소드의 이름 | 없음
propertyValues | 프로퍼티의 이름과 설정 값 또는 레퍼런스. 수정자 메소드를 통한 DI 작업에서 사용한다. | 없음
constructorArgumentValues | 생성자의 이름과 설정 값 또는 레퍼런스. 생성자를 통한 DI 작업에서 사용한다. | 없음
annotationMetadata | 빈 클래스에 담긴 애노테이션과 그 애트리뷰트 값. 애노테이션을 이용하는 설정에서 활용한다. | 없음
> 빈 설정 메타정보 항목 중에서 가장 중요한 것은 클래스 이름이다. 추상 빈으로 정의하지 않는 한 클래스 정보는 반드시 필요하다. 빈은 오브젝트이고, 오브젝트를 생성하려면 클래스가 반드시 필요하기 때문이다.
> 컨테이너에 빈의 메타정보가 등록될 때 꼭 필요한 것은 클래스 이름과 함께 빈의 아이디 또는 이름이다.
#### 빈 등록 방법
* XML: ``````태그
* ``````을 이용하면 스프링 빈 메타정보의 거의 모든 항목을 지정할 수 있으므로 세밀한 제어가 가능하다. 기본적으로 id와 class라는 두 개의 애트리뷰트가 필요하다.
```...```
``````은 다른 빈의 `````` 태그 안에 정의할 수도 있다. 이때는 아이디나 이름을 지정해주지 않는다. 이렇게 다른 빈의 설정 안에 정의되는 빈을 **내부 빈: inner bean**이라고 한다. 내부 빈은 특정 빈에서만 참조하는 경우에 사용된다. 아이디가 없으므로 다른 빈에서는 참조할 수 없다.
```
```
* XML: 네임스페이스와 전용 태그
* 예를 들어 `````` 태그는 ``````으로 선언한 것과 동일한 빈 설정 메타정보로 변환된다. 하지만 네임스페이스와 전용 태그, 전용 애트리뷰트를 이용해 선언됐기 때문에 내용이 매우 분명하게 드러나고 선언 자체도 깔끔해진다. 애플리케이션 로직을 담은 다른 빈 설정과 혼동되지도 않는다.
* 이 밖에도 jdbc 네임스페이스와 전용 태그, 개발자가 스스로 커스텀 태그를 만들어서 적용할 수도 있다.
* 자동인식을 이용한 빈 등록: 스테레오타입 애노테이션과 빈 스캐너
* 빈으로 사용될 클래스에 특별한 애노테이션을 부여해주면 이런 클래스를 자동으로 찾아서 빈으로 등록해주게 할 수 있다. 이러한 방식을 빈 스캐닝을 통한 자동인식 빈 등록 기능이라고 하고, 이런 스캐닝 작업을 담당하는 오브젝트를 빈 스캐너라고 한다.
* 스프링의 빈 스캐너는 지정된 클래스패스 아래에 있는 모든 패키지의 클래스를 대상으로 필터를 적용해서 빈 등록을 위한 클래스들을 선별해낸다. 빈 스캐너에 내장된 디폴트 필터는 @Component 애노테이션이 또는 @Component를 메타 애노테이션으로 가진 애노테이션이 부여된 클래스를 선택하도록 되어 있다. @Component를 포함해 디폴트 필터에 적용되는 애노테이션을 스프링에서는 **스테레오타입 애노테이션**이라고 부른다.
* 빈 스캐너는 기본적으로 클래스 이름의 첫 글자만 소문자로 바꾼 것을 빈의 아이디로 사용한다.
* 자동인식을 통한 빈 등록을 사용하려면 XML을 이용한 빈 스캐너를 등록하던지, 빈 스캐너를 내장한 애플리케이션 컨텍스트를 사용하면 된다.
* 스테레오 타입 애노테이션의 종류
스테레오 타입 애노테이션의 | 적용 대상
:-----------------------:|:------------:
@Repository | 데이터 액세스 계층의 DAO 또는 리포지토리 클래스에 사용된다. DataAccessException 자동변환과 같은 AOP의 적용 대상을 선정하기 위해서도 사용된다.
@Service | 서비스 계층의 클래스에 사용된다.
@Controller | 프레젠테이션 계층의 MVC 컨트롤러에 사용된다. 스프링 웹 서블릿에 의해 웹 요청을 처리하는 컨트롤러 빈으로 선정된다.
* 자바 코드에 의한 빈 등록: @Configuration 클래스의 @Bean 메소드
* 오브젝트 생성과 의존관계 주입을 담당하는 오브젝트를 오브젝트 팩토리라고 한다. 오브젝트 팩토리의 기능을 일반화해서 컨테이너로 만든 것이 스프링 컨테이너, 즉 빈 팩토리라고 볼 수 있다.
* 클래스에 @Configuration, 메소드에 @Bean이 붙으면 스프링 컨테이너가 인식할 수 있는 빈 메타정보 겸 빈 오브젝트 팩토리가 된다.
* @Configuration이 붙은 클래스는 자신도 빈으로 등록이 된다.
* @Configuration의 메타 애노테이션에 @Component가 있기 때문에 빈 스캐너의 애노테이션 필터를 통과하여 빈 스캐닝을 통해 자동등록 될 수 있다.
* 자바 코드에 의한 설정이 XML과 같은 외부 설정파일을 이용하는 것보다 유용한 점
1. 컴파일러나 IDE를 통한 타입 검증이 가능하다.
2. 자동완성과 같은 IDE 지원 기능을 최대한 이용할 수 있다.
3. 이해하기 쉽다.
4. 복잡한 빈 설정이나 초기화 작업을 손쉽게 적용할 수 있다.
* 자바 코드에 의한 빈 등록: 일반 빈 클래스의 @Bean 메소드
* @Configuration이 붙은 클래스가 아닌 일반 POJO 클래스에도 @Bean을 사용할 수 있다.
* 일반 빈 클래스에 @Bean을 사용한 경우, DI 설정을 위해 다른 @Bean 메소드를 직접 호출하면 매번 다른 오브젝트를 받게 된다. 싱글톤 빈으로 사용되지 않는다.
* 위험성이 있기 때문에 함부로 남용해서는 안 된다.
##### 빈 등록 메타정보 구성 전략
* XML 단독 사용
* 모든 빈을 명시적으로 XML에 등록하는 방법이다. 컨텍스트에서 생성되는 모든 빈을 XML에서 확인할 수 있다는 장점이 있는 반면에, 빈의 개수가 많아지면 XML 파일을 관리하기 번거로울 수 있다. 등록 방법은 ``````을 이용하는 것과 스키마에 정의된 전용 태그를 이용하는 것이 있다.
* 모든 설정정보를 자바 코드에서 분리하고 순수한 POJO 코드를 유지하고 싶을 때 가장 좋은 방법이다. 가장 선호하는 방식이다.
* XML과 빈 스캐닝의 혼용
* 애플리케이션 3계층의 핵심 로직을 담고 있는 빈 클래스는 그다지 복잡한 빈 메타정보를 필요로 하지 않아 빈 스캐닝에 의한 자동인식 대상으로 적절하다. 반면에 자동인식 방식으로 등록하기는 불편한 기술 서비스, 기반 서비스, 컨테이너 설정 등의 빈은 XML을 사용하면 된다.
* XML 없이 빈 스캐닝 단독 사용
* 모든 빈의 등록을 자동스캔만으로 가져가는 방식이다. 애플리케이션 컴포넌트는 물론이고, 각종 기술 서비스와 컨테이너 설정용 빈도 모두 스캔으로 자동등록시키는 것이다.
* 모든 빈의 정보가 자바 코드에 담겨 있으므로 빈의 설정정보를 타입에 안전한 방식으로 작성할 수 있다는 장점이 있다.
* 스프링이 제공하는 스키마에 정의된 전용 태그를 사용할 수 없다는 단점이 있다.
#### 빈 의존관계 설정 방법
빈 등록 방법에서 살펴봤던 방법과 비슷하게 XML `````` 태그, 스키마를 가진 전용 태그, 애노테이션, 자바 코드에 의한 직접적인 DI 방법이 있고 이 네 가지 방법은 다시 명시적으로 빈을 지정하는 방식과 자동 선정 방식으로 구분할 수 있다. 결과적으로 총 8가지의 빈 의존관계 주입 방법이 있다. 빈 등록 방식과 빈 의존관계 주입 방법은 메타정보 작성 방법이 같지 않아도 된다.
##### XML: ``````, ``````
``````을 이용해 빈을 등록했다면 프로퍼티와 생성자 두 가지 방식으로 DI를 지정할 수 있다.
* ``````: 수정자 주입
```
<bean ...>
</bean>
```
> ref 애트리뷰트를 사용하여 빈 이름을 이용해 주입할 빈을 찾는다.
```
```
> value 애트리뷰트는 단순 값 또는 빈이 아닌 오브젝트를 주입할 때 사용한다.
> value 애트리뷰트로 넣을 수 있는 값의 타입에는 제한이 없다. 정수, 실수, 스트링 값 같은 기본적인 값은 물론이고, 다양한 종류의 클래스도 value를 이용해 주입할 수 있다.
* ``````: 생성자 주입
``````는 생성자를 통한 빈 또는 값의 주입에 사용된다. 독립적인 수정자 메소드를 이용하는 수정자 주입 방식과는 달리 생성자 주입은 생성자의 파라미터를 이용하기 때문에 한 번에 여러 개의 오브젝트를 주입할 수 있다. 각 파라미터마다 하나의 빈 또는 값을 지정할 수 있다.
```
public class Hello {
...
public Hello(String name, Printer printer) {
this.name = nema;
this.printer = printer;
}
}
```
> 생성자 주입용 클래스
```
```
> index 지정 방법
```
```
> 타입 지정 방법
```
```
> 파라미터 이름을 이용하는 방법
> 생성자 주입을 사용할 때는 실수로 파라미터 순서가 뒤바뀌지 않도록 주의해야 한다.
##### XML: 자동와이어링
자동와이어링은 명시적으로 프로퍼티나 생성자 파라미터를 지정하지 않고 미리 정해진 규칙을 이용해 자동으로 DI 설정을 컨테이너가 추가하도록 만드는 것이다.
* byName: 빈 이름 자동와이어링
보통 빈의 이름은 클래스 이름이나 빈이 구현한 대표적인 인터페이스 이름을 따른다. 같은 방식으로 프로퍼티 이름도 프로퍼티 타입의 이름을 사용한다.
```
// 는 생략됐다.
자동와이어링을 통해 컨테이너가 자동으로 추가해준다.
```
> printer 프로퍼티 선언은 생략됐지만 ``````옵션으로 준 autowire="byName"에 의해 스프링은 Hello 클래스의 프로퍼티의 이름과 동일한 빈을 찾아서 자동으로 프로퍼티로 등록해준다.
> 자동와이어링 옵션은 ``````의 애트리뷰트로 선언해도 되지만 해당 설정파일의 모든 빈에 적용할 것이라면 아예 루트 태그인 ``````의 디폴트 자동와이어링 옵션을 변경해줘도 된다.
```
...
...
```
* byType: 타입에 의한 자동와이어링
타입에 의한 자동와이어링은 프로퍼티의 타입과 각 빈의 타입을 비교해서 자동으로 연결해주는 방법이다.
```
<bean id="hello" class"...Hello" autowire="byType">...</bean>
```
> 타입에 의한 자동와이어링
> 보통 클래스당 하나의 빈이 등록되는 DAO나 서비스 계층 빈과 같은 경우에는 타입에 의한 자동와이어링이 편리하다. 반면에 기술 서비스 빈이나 기반 서비스 빈 같은 경우는 동일한 타입의 빈이 하나 이상 등록될 가능성이 많기 때문에 이름에 의한 자동와이어링을 적용하는 편이 낫다.
##### XML: 네임스페이스와 전용 태그
규칙은 아니지만 관례적으로 전용 태그에 의해 만들어지는 빈을 다른 빈이 참조할 경우에는 id 애트리뷰트를 사용해 빈의 아이디를 지정한다.
```
```
```
```
> 전용 태그를 참조하는 빈
> 빈의 아이디와 레퍼런스를 명시적으로 선언하는 방식으로 사용한다면 네임스페이스를 쓰는 전용 태그도 간단히 의존관계를 정의할 수 있다. 다만 상당수의 전용 태그는 아이디조차 선언하지 않는 경우가 많다. 내부적으로는 빈이 만들어지지만 다른 빈과 DI로 연결되기보다는 컨테이너가 참조하는 설정정보로만 사용되기 때문이다.
> 전용 태그도 내부적으로는 ``````으로 선언한 것과 동일하게 빈 메타정보가 만들어지므로 자동와이어링의 대상이 될 수 있다. 하지만 가능하면 타입에 의한 자동와이어링과 같이 XML 안에서 잘 파악되지 않는 방식은 적용하지 않는 게 좋다. 기술 서비스나 기반 서비스의 경우는 가능한 한 id를 이용해 명시적으로 선언하는 것이 바람직하다.
##### 애노테이션: @Resource
* 수정자 메소드
```
public class Hello{
private Printer printer;
...
@Resource(name="printer") // 와 동일한 의존관계 메타정보로 변환된다.
public void setPrinter(Printer printer) {
this.printer = printer;
}
}
```
> @Resource와 같은 애노테이션으로 된 의존관계 정보를 이용해 DI가 이뤄지게 하려면 다음 세 가지 방법 중 하나를 선택해야 한다.
> 1. XML의
> 2. XML의
> 3. AnnotationConfigApplicationContext 또는 AnnotationConfigWebApplicationContext
> 첫 번째 ``````는 @Resource와 같은 애노테이션 의존 관계 정보를 읽어서 메타정보를 추가해주는 기능을 가진 빈 후처리기를 등록해주는 전용 태그다. 두 번째 전용 태그는 빈 스캐닝을 통한 빈 등록 방법을 지정하는 것인데, 내부적으로 첫 번째 태그로 만들어지는 빈을 함께 등록해준다. 세 번째 방법은 아예 빈 스캐너와 애노테이션 의존관계 정보를 읽는 후처리기를 내장한 애플리케이션 컨텍스트를 사용하는 것이다.
###### 필드
@Resource는 필드에도 붙을 수 있다. 필드의 정보를 참고해서 프로퍼티를 추가해준다.
```
@Component
public class Hello {
@Resource(name="printer") // 참조할 빈의 이름을 지정한다. 생략도 가능하다.
private Printer printer;
// setPrinter() 메소드 없음
}
```
> private 필드에 setPrinter() 메소드가 없어도 문제없다.
> 이런 방법을 **필드 주입**이라고 한다.
> XML의 자동와이어링은 각 프로퍼티에 주입할 만한 후보 빈이 없을 경우에 무시하지만, @Resource는 반드시 참조할 빈이 존재해야 한다. 만약 찾을 수 없다면 예외가 발생한다.
> @Resource는 이름을 이용한 프로퍼티 설정이다.
##### 애노테이션: @Autowired/@Inject
이 두 가지 애노테이션은 기본적으로 타입에 의한 자동와이어링 방식으로 동작한다. 의미나 사용법은 거의 동일하다.
@Autowired는 스프링 2.5부터 적용된 스프링 전용 애노테이션이고 @Inject는 JavaEE 6의 표준 스펙인 JSR-330(Dependency Injection for Java.)에 정의되어 있는 것으로, 스프링 외에도 JavaEE 6의 스펙을 따르는 여타 프레임워크에서도 동일한 의미로 사용되는 DI를 위한 애노테이션이다.
* 수정자 메소드와 필드
* 필드와 수정자 메소드는 @Resource와 사용 방법이 비슷하다. 이름 대신 필드나 프로퍼티 타입을 이용해 후보 빈을 찾는다는 점만 다르다.
```
public class Hello {
@Autowired
private Printer printer;
}
```
> 필드 주입 @Autowired
```
public class Hello {
private Printer printer;
@Autowired
public void setPrinter(Printer printer) {
this.printer = printer;
}
}
```
* 생성자
* @Autowired는 @Resource와 다르게 생성자에도 부여할 수 있다.
```
pubilc class BasSqlService implements SqlService {
protected SqlReader sqlReader;
protected SqlRegistry sqlRegistry;
@Autowired // 한 개의 생성자에만 부여할 수 있다.
public BasSqlService(SqlReader sqlReader, SqlRegistry sqlRegistry) {
this.sqlReader = sqlReader;
this.sqlRegistry = sqlRegistry;
}
...
}
```
> 생성자 주입 @Autowired
* 일반 메소드
* @Autowired는 수정자, 생성자 외에 일반 메소드에도 적용할 수 있다. 파라미터를 가진 메소드를 만들고 @Autowired를 붙여주면 각 파라미터의 타입을 기준으로 자동와이어링을해서 DI 해줄 수 있다. 생성자 주입과 달리 일반 메소드는 오브젝트 생성 후에 차례로 호출이 가능하므로 여러 개를 만들어도 된다.
```
pubilc class BasSqlService implements SqlService {
protected SqlReader sqlReader;
protected SqlRegistry sqlRegistry;
@Autowired // 한 개 이상의 설정용 메소드에 부여할 수 있다.
public void config(SqlReader sqlReader, SqlRegistry sqlRegistry) {
this.sqlReader = sqlReader;
this.sqlRegistry = sqlRegistry;
}
...
}
```
> 일반 메소드를 이용한 @Autowired
* 컬렉션과 배열
* @Autowired를 이용하면 같은 타입의 빈이 하나 이상 존재할 때 그 빈들을 모두 DI 받도록 할 수 있다. @Autowired의 대상이 되는 필드나 프로퍼티, 메소드의 파라미터를 컬렉션이나 배열로 선언하면 된다.
```
@Autowired
Collection printers; // Set, List로 선언해도 된다.
//컬렉션 대신 배열을 이용해도 된다.
@Autowired
Printer[] printers;
//Map을 이용하면 빈의 이름을 키로 하는 맵으로 DI받을 수 있다. 빈의 이름이 필요한 경우에 유용하다.
@Autowired
Map<String, Printer> printerMap;
```
> 컬렉션과 배열을 단지 같은 타입의 빈이 여러 개 등록되는 경우에 충돌을 피하려는 목적으로 사용해서는 안 된다. 의도적으로 타입이 같은 여러 개의 빈을 등록하고 이를 모두 참조하거나 그중에서 선별적으로 필요한 빈을 찾을 때 사용하는 것이 좋다.
> 한 가지 주의할 사항은 DI할 빈의 타입이 컬렉션인 경우에는 @Autowired로 자동설정이 불가능하다는 점이다. 빈 자체가 컬렉션인 경우에는 @Resource를 이용해야 한다. 따라서 @Autowired에 컬렉션을 사용했을 때는 같은 타입의 빈이 여러 개 존재해서 이를 모두 DI 하는 것으로 보면 된다.
* Qualifier
* Qualifier는 타입 외의 정보를 추가해서 자동와이어링을 세밀하게 제어할 수 있는 보조적인 방법이다.
```
@Autowired
@Qualifier("mainDB")
DataSource dataSource;
```
```
```
> 스프링은 DataSource 타입의 빈 중에서 `````` 태그가 있고 그 값이 mainDB인 것으로 한정해서 자동와이어링을 시도한다.
빈을 XML이 아니라 @Component를 이용해 자동등록했다면 `````` 태그 대신 @Qualifier 애노테이션을 클래스에 부여해주면 된다.
```
@Component
@Qualifier("mainDB") // 선언과 동일하다.
public class oracleDataSource {
}
```
> @Qualifier를 이용하면 이름에 의한 빈 선택도 가능하다. 하지만 권장되지 않는다. 이름을 이용해 빈을 지정하고 싶다면 @Resource를 사용해야 한다.
생성자나 파라미터가 여러 개인 설정용 메소드일 때
```
@Autowired
public void config(@Qualifier("mainDB") DataSource dataSource, Printer printer) {
}
```
> @Autowired는 @Resource와 마찬가지로 일단 지정하면 반드시 DI 할 후보 빈이 존재해야 한다. 그렇지 않으면 빈을 찾을 수 없다는 에러가 발생한다. 컬렉션으로 선언된 경우에도 최소한 한 개의 타입이 일치하는 빈이 존재해야만 한다.
> 타입에 의한 자동와이어링으로 빈을 찾을 수 없더라도 상관없다면, 즉 해당 프로퍼티의 DI를 선택적으로 가능하게 하려면 @Autowired의 required 엘리먼트를 false로 선언해주면 된다. ```@Autowired(required=false) Printer printer;```
JavaEE 6의 표준인 JSR-330(DIJ)에는 @Autowired 및 @Qualifier와 비슷한 기능을 가진 @Inject와 @Qualifier가 있다. 스프링의 @Qualifier와 이름은 같지만 패키지가 다르기 때문에 혼종하지 않도록 주의해야 한다.
* @javax.inject.Inject
* @Inject는 필드, 생성자, 수정자와 임의의 설정용 메소드에 사용돼서 타입에 의한 자동와이어링을 선언한다는 점에서 @Autowired와 매우 유사하다 하지만 @Autowired의 required 엘리먼트에 해당하는 선택 기능은 없다.
* @javax.inject.Qualifier
* 스프링의 @Qualifier와 이름이 같지만 패키지가 다르고, 사용 방법도 다르다. 스프링의 @Qualifier는 스트링 값을 가질 수 있고 그 자체로 한정자로 @Autowired와 함께 사용할 ㅅ 있다. 반면에 JSR-330의 @Qualifier는 그 자체로는 한정자로 사용해서 @Inject와 함께 쓸 수 없다. 단지 다른 한정자 애노테이션을 정의하는 용도로만 사용 가능하다.
```
@Retention(RetentionPolicy.RUNTIME)
@javax.inject.Qualifier
public @interface Main {
}
```
> @Qualifier를 이용해 애노테이션을 만들 때는 엘리먼트를 추가해서 사용할 수도 있다.
> JSR-330의 @Inject와 @Qualifier는 스프링 외의 표준 JavaEE 6 환경에서도 같은 의미로 사용될 수 있기 때문에 유용하다. 하지만 기능이 제한적이고 단순하기 때문에 스프링에 독립적인 DI 설정을 가진 클래스를 만들 생각이 아니라면 스프링의 @Autowired와 @Qualifier를 사용하는 편이 낫다.
##### 자바 코드에 의한 의존관계 설정
1. 애노테이션에 의한 설정 @Autowired, @Resource
* 빈은 자바 코드에 의해 생성되지만 의존관계는 빈 클래스의 애노테이션을 이용하게 할 수 있다.
* @Autowired 등을 사용했더라도 일부 프로퍼티는 코드에서 직접 의존관계를 지정해줄 수도 있다.
2. @Bean 메소드 호출
* @Configuration과 @Bean을 사용하는 자바 코드 설정 방식의 기본은 메소드로 정의된 다른 빈을 메소드 호출을 통해 참조하는 것이다. @Bean이 붙은 메소드 자체가 하나의 빈 이름처럼 사용된다.
* @Bean이 붙은 메소드는 기본적으로 스프링의 실글톤 방식을 따라야 하기 때문에 여러 번 호출해도 매번 새로운 오브젝트가 만들어지지 않고, 딱 한 개의 오브젝트가 반복적으로 돌아온다.
* @Configuration이 붙지 않은 클래스의 @Bean 메소드에서는 이 방식을 사용하면 안 된다.
3. @Bean과 메소드 자동와이어링
* 위의 두 번째 방법의 단점을 극복하기 위해 사용할 수 있다.
* 메소드로 정의된 다른 빈을 가져와 자바 코드로 의존정보를 생성할 때 직접 @Bean이 붙은 메소드를 호출하는 대신 그 빈의 레퍼런스를 파라미터로 주입받는 방식을 사용한다.
* @Bean이 붙은 메소드는 기본적으로 @Autowired가 붙은 메소드처럼 동작한다.
##### 빈 의존관계 설정 전략
* XML 단독
* 빈 등록은 물론이고 의존관계 설정까지 모두 XML만으로 구성하는 방법이다.
* XML 방식이라면 컨테이너 설정과 기술 서비스의 등록을 위해 네임스페이스와 전용 태그의 사용은 필수다. 그 외의 빈 등록은 ``````을 사용하면 된다.
* 한 가지 선택할 수 있는 것은 XML 자동와이어링을 적용할 것인지 아니면 의존관계 정보를 직접 ``````나 ``````로 직접 정의할 것인지인데, 가능한 한 XML 자동 와이어링을 선택하는 것이 좋다. 그리고 XML 자동와이어링은 가능한 한 이름에 의한 방식을 사용하는 편이 좋다.
* XML과 애노테이션 설정의 혼합
* 빈은 XML로 등록하지만 의존관계 정보는 @Autowired나 @Resource 같은 애노테이션을 이용하는 방법이다. 빈 스캐너로 자동등록할 경우 등록 대상을 정확히 파악하기 힘들다고 생각한다면 `````` 태그를 이용한 빈 등록을 선호할 수 있다. 하지만 ``````를 일일이 선언해서 의존관계를 설정해주기는 번거롭고, 그렇다고 XML의 일괄적인 자동와이어링은 불편하다고 느껴진다면 의존관계 설정은 애노테이션을 이용해 세밀하게 제어할 수 있게 만들면 좋다.
* 애노테이션 단독
* 빈의 등록도 @Component 애노테이션을 이용해 스캐너에게 맡기고 의존관계 역시 @Autowired와 같은 애노테이션을 이용해 자동으로 등록하는 방법이다. 애플리케이션 빈을 등록하는 데 XML이 필요 없기 때문에 생산성이 높고 수정이 편리하다는 장점이 있어서 점차 비중이 늘어가고 있는 방식이다.
* 물론 이때도 일부 기술 서비스 빈이나 컨테이너 설정용 빈은 XML을 이용할 수 있다. 스키마와 전용 태그를 사용하기 때문에 얻을 수 있는 장점이 많기 때문이다.
* XML이 하나도 없는 순수한 애노테이션만의 설정을 원한다면 일부 기술 서비스 빈은 @Configuration 자바 코드를 이용해 등록해줘야 한다.
#### 프로퍼티 값 설정 방법
DI를 통해 빈에 주입되는 것은 두 가지다. 하나는 다른 빈 오브젝트의 레퍼런스이고, 다른 하나는 단순 값이다.
##### 메타정보 종류에 따른 값 설정 방법
* XML: ``````와 전용 태그
* ``````는 ref 애트리뷰트를 이용해 다른 빈의 아이디를 지정한다. 만약 ref 대신 value 애트리뷰트를 사용하면 런타임 시에 주입할 값으로 인식한다.
* 애노테이션: @Value
* static final로 선언하는 상수 값 같은 용도라면 굳이 외부에서 별도로 주입할 이유가 없다.
* 빈 의존관계는 아니지만 어떤 값을 외부에서 주입해야 하는 용도는 두 가지가 있다.
* 하나는 환경에 따라 매번 달라질 수 있는 값이다.
* 두 번째 용도는 첫 번째와 거의 비슷하지만 초기값을 미리 갖고 있다는 점에서 조금 차이가 있다. 테스트나 특별한 이벤트 때는 초기값 대신 다른 값을 지정하고 싶을 경우다.
* 소스코드의 애노테이션을 이용해서 프로퍼티 값을 지정하는 방법
```
public Class Hello {
private String name;
@Value("Everyone")
public void setName(String name) {
this.name=name;
}
}
```
> @Value("Everyone")은 ```<property .. value="Everyone" />```과 동일하다.
> @Value 애노테이션은 스프링 컨테이너가 참조하는 정보이지 그 자체로 클래스의 필드에 값을 넣어주는 기능이 있는 것은 아니다. 따라서 테스트 코드와 같이 컨테이너 밖에서 사용된다면 @Value 애노테이션은 무시된다. @Value로 값을 설정해준다는 건 자바 코드와 컨테이너가 런타임 시에 주입하는 정보를 분리하겠다는 의미이고, 외부로부터의 주입을 통한 초기화가 반드시 필요하다고 이해할 수 있다. 어쨌든 XML과 같이 외부로 분리하는 방법에 비해 @Value에 직접 값을 지정하는 방법은 잘 사용되지 않는다.
> @Value 애노테이션의 주요 용도는 자바 코드 외부의 리소스나 환경정보에 담긴 값을 사용하도록 지정해주는 데 있다.
* 자바 코드: @Value
* 자바 코드에 의한 설정에서도 @Autowired를 활용했던 것처럼 @Value도 사용 가능하다. @Configuration이 붙은 클래스를 일반 빈이라고 생각하고 애노테이션에 의한 의존관계 설정 방식을 적용하면 된다.
* @Bean 메소드의 파라미터에 @Value를 직접 사용할 수도 있다.
```
@Configuration
public class config {
@Value("${database.username}")
private String name;
@Bean
public Hello hello() {
Hello hello = new Hello();
hello.setName(this.name);
return hello;
}
}
```
##### PropertyEditor와 ConversionService
XML의 value 애트리뷰트나 @Value의 엘리먼트는 모두 텍스트 문자로 작성된다. 값을 넣을 프로퍼티 타입이 스트링이라면 아무런 문제가 없겠지만, 그 외의 타입인 경우라면 타입을 변경하는 과정이 필요하다.
이를 위해 스프링은 두 가지 종류의 변환 서비스를 제공한다. 디폴트로 사용되는 타입 변환기는 PropertyEditor라는 java.beans의 인터페이스를 구현한 것이다. 프로퍼티 에디터라고 불리는 오브젝트는 원래 GUI 개발환경에서 자바빈 오브젝트의 프로퍼티 값을 적접 넣어주기 위해 만들었다. 스프링은 프로퍼티 에디터 개념을 XML 또는 @Value의 스트링 값에서 빈 오브젝트의 프로퍼티 타입으로 변경하는 데 활용한다.
* 스프링이 기본적으로 지원하는 변환 가능한 타입
* 기본 타입
* boolean, Boolean, byte, Byte, short, Short, int, Integer, long, Long, float, Float, double, Double, BigDecimal, BigInteger, char, Character, String
* 배열
* byte[], char[], short[], int[], long[]
* 값을 콤마로 구분해서 넣어주면 배열 형태로 변환된다. ```@Value("1,2,3,4") int intarr;```
* 기타
* Charset, Class, Currency, File, InputStream, Locale, Pattern, Resource, Timezone, URI, URL
스프링 3.0부터는 PropertyEditor 대신 사용할 수 있는 ConversionService를 지원하기 시작했다. ConversionService는 자바빈에서 차용해서 사용해오던 PropertyEditor와 달리 스프링이 직접 제공하는 타입 변환 API다. PropertyEditor보다 변환기의 작성이 간편하다. 또한 PropertyEditor와 달리 멀티스레드 환경에서 공유해 사용될 수 있다. 하지만 컨테이너가 스프링 빈의 값을 주입하는 작업에는 기본 변환기인 PropertyEditor로 충분하다.
* conversionService 빈 설정
```
// conversionService라는 이름의 빈으로 ConversionService 타입의 빈을 등록하면 컨테이너가 자동인식해서 PropertyEditor를 대신해서 사용한다.
// 직접 정의한 타입 변환기를 등록할 수 있다. 기본적으로 등록된 변환 타입에 추가돼서 value, @Value의 값을 변환하는 데 사용된다.
```
##### 컬렉션
스프링은 List, Set, Map, Properties와 같은 컬렉션 타입을 XML로 작성해서 프로퍼티에 주입하는 방법을 제공한다. 이때는 value 애트리뷰트를 통해 스트링 값을 넣는 대신 컬렉션 선언용 태그를 사용해야 한다. 이때는 ``````의 value 애트리뷰트가 생략된다.
* List, Set
* ``````와 ``````를 이용해 선언한다.
```
// 의 값을 각 원소로 갖는 List 타입의 오브젝트로 변환되어 names 프로퍼티에 주입된다.
Spring
IoC
DI
```
> 프로퍼티가 Set이라면 `````` 대신 ``````을 사용하면 된다.
* Map
* 맵은 ```