이 장은 제어의 역전(IoC) 개념의 Spring 프레임워크의 구현물을 다룬다. [1]IoC는 Spring 프레임워크가 제공하는 많은 주변 기능들의 전반적인 토대가 되며, 풍부하면서도 개념적으로는 매우 단순한 이 기술을 순서대로 철저하게 다룰 것이다.
org.springframework.beans과 org.springframework.context 패키지는 Spring 프레임워크의 IoC컨테이너를 위한 기초를 제공한다. BeanFactory 인터페이스는 어떠한 성질의 객체라도 관리할 수 있는 고급 설정 기법을 제공한다. ApplicationContext 인터페이스는 BeanFactory 위에 구축되었고(BeanFactory를 상속받음), Spring의 AOP기능, 메시지 자원 핸들링(국제화에 사용됨), 이벤트 위임, 웹 애플리케이션에서 사용하기 위한 WebApplicationContext와 같은 특정 애플리케이션 계층의 컨텍스트를 더 쉽게 통합할 수 있는 등의 다른 기능들을 가지고 있다.
다시 말해, BeanFactory는 설정 프레임워크와 기본 기능을 제공하는 반면에, ApplicationContext는 좀더 전사적 중심(enterprisse-centric)의 기능을 거기에 보태고 있다. ApplicationContext는 BeanFactory의 완벽한 수퍼세트(BeanFactory의 기능을 모두 포함하면서, 다른 기능을 더 추가한 상태)이다. BeanFactory의 모든 기능과 행태는 ApplicationContext에도 마찬가지로 적용된다고 보면 된다.
이 장은 BeanFactory 와 ApplicationContext 모두에 적용하는 기본 개념을 다루는 첫번째 부분 과 ApplicationContext 인터페이스에만 적용되는 기능을 다루는 두번째 부분의 두개의 부분으로 나뉘었다.
Spring에서, 애플리케이션의 뼈대를 형성하고 Spring IoC컨테이너에 의해 관리되는 객체를 beans라고 한다. bean은 간단히 객체라 할 수 있으며, Spring IoC container에 의해 일반적으로 인스턴스화되고 조합되고 관리된다. 그 외에는 bean에는 별반 특별할 것이 없다(이는 모든 다른 점에 있어서 여러분의 어플리케이션에 있는 아마도 많은 객체들중의 하나일 뿐이다). 이러한 bean들과 그것들간의 의존성은 컨테이너에 의해 사용되는 설정 메타데이터에 의해 반영된다.
org.springframework.beans.factory.BeanFactory는 앞서말한 bean을 포함하고 관리하는 책임을 지는 Spring IoC 컨테이너의 실제 표현이다.
BeanFactory은 Spring에서 핵심 IoC 컨테이너 인터페이스이다. 이것은 어플리케이션의 객체를 인스턴스화하거나 소싱(다른곳에서 생성된 객체를 가져오기)하고 해당 객체를 설정하고 각 객체간의 의존성을 조합하는 책임을 맡고 있다.
Spring과 함께 바로 제공되는 뛰어난 BeanFactory 인터페이스의 많은 구현(implementations)이 있다. 가장 공통적으로 사용되는 BeanFactory 구현은 XmlBeanFactory 클래스이다. 이 구현을 통해 XML로 애플리케이션을 구성하는 객체과 그 객체들간의 의심할 여지없이 풍부한 상호의존 관계를 표현할 수 있다. XmlBeanFactory는 이 XML 설정 메타데이터를 가지고 완전하게 설정된 시스템이나 애플리케이션을 생성한다.
Spring IoC 컨테이너
위 이미지에서 볼수 있는 것처럼, Spring IoC 컨테이너는 설정 메타데이터의 몇가지 형태를 지원한다; 이 설정 메타데이터는 Spring 컨테이너에 “[애플리케이션내 객체를]인스턴스화, 설정, 그리고 조합”하는 방법을 지정하는 방법에 지나지 않는다. 이 설정 메타데이터는 대개 간단하고 직관적인 XML형태로 제공된다. XML-기반의 설정 메타데이터를 사용할때, 당신은 Spring IoC 컨테이너가 관리해주길 원하는 bean을 위한 bean정의를 작성하고, 컨테이너가 할일을 하게 두면 된다.
![]() | Note |
---|---|
XML-기반의 메타데이터는 설정 메타데이터의 가장 일반적으로 사용되는 형태이다. 그렇다고 이것이 사용가능한 유일한 메타데이터의 형태는 아니다. Spring IoC 컨테이너 자체는 설정 메타데이터의 형태와는 전혀 결합되지 않는다.(decoupled) 개발시에는 XML, Java 프라퍼티 형태, 또는 프로그램으로 처리(Spring의 public API를 사용하여)하는 설정 메타데이터를 제공할수 있다. XML-기반 설정 메타데이터 형태는 매우 간단하다. 그래서 이 장의 나머지는 핵심 개념과 Spring IoC 컨테이너의 기능을 전달하기 위해 XML 형태를 사용할 것이다. |
광범위한 주요 애플리케이션 작성 시나리오에서는, Spring IoC 컨테이너의 하나 이상의 인스턴스를 생성하기 위해서 사용자가 명시적으로 코딩할 필요가 없음을 주의하기 바란다. 예를 들어, 웹 애플리케이션의 경우, 애플리케이션의 관련 web.xml 파일내에 간단히 8줄 정도의 반복적으로 사용되는 J2EE 웹 서술자 XML 구문으로 충분할것이다(Section 3.9.4, “웹 애플리케이션을 위한 편리한 ApplicationContext 인스턴스화”를 보라).
가장 기본적인 수준으로는, Spring IoC 컨테이너 설정은 컨테이너가 관리해야만 하는 적어도 하나의 bean 정의로 구성되지만, 보통은 1개 이상의 bean을 정의한다. XML-기반의 설정 메타데이터를 사용할때, 이러한 bean은 가장 상위 레벨의 <beans/>요소내부에 하나 혹은 그 이상의 <bean/> 요소로 설정된다.
이러한 bean 정의는 애플리케이션을 구성하는 실질적인 객체에 대응한다. 일반적으로 이것은 서비스 계층 객체, 데이터 접근 객체(DAO) 혹은 Struts Action과 같은 표현 객체, Hibernate SessionFactory와 같은 하부 기반 구조를 이루는 객체, JMS Queue 참조 객체 등을 위한 bean정의를 가질것이다. (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 http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="..." class="..."> <!-- 이 bean을 위한 도우미와 설정이 여기에 온다. --> </bean> <bean id="..." class="..."> <!-- 이 bean을 위한 도우미와 설정이 여기에 온다. --> </bean> <!--더 많은 bean 정의들이 있을 수 있다... --> </beans>
Spring IoC 컨테이너를 인스턴스화하는 것은 쉽다. 아래에서 그 방법을 보자.
Resource resource = new FileSystemResource("beans.xml"); BeanFactory factory = new XmlBeanFactory(resource);
... 또는...
ClassPathResource resource = new ClassPathResource("beans.xml"); BeanFactory factory = new XmlBeanFactory(resource);
... 또는...
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
//물론 ApplicationContext는
BeanFactory이기도 하다
BeanFactory factory = (BeanFactory) context;
컨테이너 정의를 여러 XML파일로 분리하는 것이 종종 도움이 될 수도 있다. 그리고서 이러한 XML 파일들로 설정된 애플리케이션 컨텍스트를 적재하는 한가지 방법은 여러 리소스 경로를 파라미터로 받는 애플리케이션 컨텍스트 생성자를 사용하는 것이다. bean factory를 사용하여, bean 정의 리더(reader)는 순서대로 각각의 파일로부터 bean 정의를 읽는데 여러번 사용될수 있다.
일반적으로, Spring팀은 위와 같은 접근 방식을 선호하는데, 그것은 컨테이너 설정 파일이 다른 파일들과 조합된다는 사실을 각 설정파일이 서로 알필요가 없어도 되기 때문이다. 다른 방법으로는 다른 파일(혹은 파일들)로 부터 bean 정의를 적재하기 위해 하나 혹은 그 이상의 <import/> 요소를 사용하는 것이다. <import/> 요소는 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>
이 예제에서, 외부 bean정의가 3개의 파일(services.xml, messageSource.xml과 themeSource.xml)로부터 적재된다. 모든 위치 경로는 import를 수행하는 정의파일에 대해 상대적이다. 그래서 이 경우에 messageSource.xml 과 themeSource.xml이 import 하는 파일의 위치 아래의 resources 에 있어야만 하는 반면에 services.xml은 import를 수행하는 파일과 같은 디렉토리나 클래스패스 경로내 두어야만 한다. 여기 보듯이 맨 앞의 슬래쉬(/)는 실제로는 무시된다. 하지만 상대경로를 나타내려 하기 때문에 비록 슬래쉬가 무시되더라도 슬래쉬 없이 사용하는 것이 더 나은 이다.
import될 파일의 내용은 <beans/>요소를 가장 상위 레벨에 포함하는 스키마나 DTD에 따라 완전히 유효한 XML bean정의 파일이어야만 한다.
이전에 언급된것처럼, Spring IoC 컨테이너는 하나 혹은 그 이상의 bean을 관리한다. 이러한 bean은 컨테이너에 제공된 bean을 가진 설정 메타데이터내 정의(대개 XML <bean/> 정의의 형태로)된 지시사항을 사용하여 생성된다.
컨테이너 내부에서는, 이러한 bean 정의는 다른 여러 정보들 중에서도 특히 다음의 메타데이터를 포함하는 BeanDefinition객체로 표현된다.
패키지명을 포함하는 클래스명 : 이것은 보통 정의된 bean의 실제 구현 클래스이다. 하지만 bean이 일반적인 생성자를 사용하지 않고 정적(static) factory 메소드를 호출하여 인스턴스화된다면, 이것은 실제로는 factory 클래스의 클래스명이 될것이다.
bean행위적 설정 요소는 bean이 컨테이너 내에서 어떻게 행위를 하는지를 지정한다(이를 테면 프로토타입 또는 싱글톤, autowiring 모드, 의존성 체크 모드, 초기화(initialization)와 제거(destruction)메소드).
새롭게 생성된 bean내에 셋팅할 생성자 인자와 프라퍼티 값. 예를 들면 커넥션 풀을 관리하는 빈 안에서 (생성자의 인자이거나 프라퍼티로 값으로 지정되는)커넥션의 갯수나 커넥션 풀 최대 크기와 같은 값들이 될 것이다.
작업을 하는 bean을 위해 필요한 다른 bean, 이를 테면 협력자(collaborators - 혹은 의존성dependency이라고 불리기도 한다).
위에서 나열된 개념들은 각각의 bean정의를 구성하는 프라퍼티의 묶음으로 직접적으로 바뀌게 된다. 그러한 프라퍼티 중의 몇몇은 각각에 대한 좀더 상세한 문서 링크를 포함해서 아래에 목록을 제시하였다.
Table 3.1. Bean 정의
이름 | 좀더 상세한 정보 |
---|---|
class | Section 3.2.3.2, “bean 인스턴스 생성하기” |
name | Section 3.2.3.1, “bean 명명하기” |
scope | |
생성자 인자들 | Section 3.3.1, “의존성 삽입하기” |
프라퍼티 | Section 3.3.1, “의존성 삽입하기” |
autowiring 모드 | Section 3.3.6, “Autowiring 협력자” |
의존성 체크 모드 | Section 3.3.7, “의존성을 위한 체크” |
게으른 초기화 모드 | |
초기화 메소드 | Section 3.6.1, “Lifecycle 인터페이스” |
제거(destruction) 메소드 | Section 3.6.1, “Lifecycle 인터페이스” |
지정된 bean을 생성하는 방법을 나타내는 bean정의 외에도, 어떤 BeanFactory 구현물은 factory외부에서 (사용자 코드에 의해)생성되어 존재하는 객체를 등록할 수 있도록 해준다. DefaultListableBeanFactory 클래스는 registerSingleton(..) 메소드를 통해 그런 기능을 지원한다. 일반적인 애플리케이션은 메타데이터 bean정의를 통해 정의된 bean들을 가지고 단독으로 작동한다.
모든 bean은 하나 또는 그 이상의 id(식별자 혹은 이름이라고 불리기도 하며, 이 용어들은 모두 같은 것을 나타낸다) 를 가진다. 이러한 id는 bean을 적재하고 있는 컨터이너 내에서 유일해야만 한다. bean은 대개 오직 하나의 id를 가지지만 하나 이상의 id를 가진다면, 추가적인 것들은 본질적으로는 별칭으로 간주할 수 있다.
XML-기반의 설정 메타데이터를 사용할때, bean의 식별자를 명시하기 위해 'id' 나 'name' 속성을 사용한다. 'id' 속성은 정확하게 하나의 id를 명시하도록 허용한다. 그리고 실제 XML요소의 ID 속성이고 XML파서는 다른 요소가 id를 참조할때 몇가지 추가적인 유효성 검사를 할수 있다. 이것은 bean id를 명시하기 위해 권장하는 방법이다. 하지만 XML 스펙에서는 XML ID에 적법한 문자가 제한돼 있다. 이것은 보통 큰 제약은 아니지만, 이러한 특수한 XML 문자중 하나를 사용할 필요가 있거나 bean에 대해 다른 별칭을 사용하길 원한다면, 하나 혹은 그 이상의 id를 사용하는 대신 'name' 속성에서 쉼표 (,), 세미콜론 (;), 또는 공백으로 구분해서 나타내면된다.
bean을 위해 name을 제공하는 것이 필수가 아나다. name이 명시적으로 제공되지 않는다면, 컨테이너는 bean을 위해 (유일한) name을 생성할것이다. bean을 위한 name을 제공하지 않는 까닭은 나중에 언급할 것이다(한가지 사용예로 내부(inner) beans이 있다).
bean 정의 내부에서는, id 속성을 이용해서 최대 한 개의 이름과 그리고 alias속성을 통해 여러개의 다른 이름들을 조합하여 bean에 한 개 이상의 이름을 지정할 수 있다. 이러한 모든 이름은 동일한 bean에 대응하는 별칭으로 간주되며, 한 애플리케이션 내에서 각각의 컴포넌트가 컴포넌트 자체에 명시된 bean이름을 사용하여 공통적인 의존성을 참조하도록 하는 것과 같은 몇가지 상황에서 유용하게 쓰일 수 있다.
하지만 bean을 정의 할 때 모든 별칭을 함께 명시하는 것 만으로는 부족할 때도 있다. 때로는 다른 곳에서 정의된 bean을 위한 별칭을 지정하는 것이 바람직한 경우도 있다. 이것은 XML-기반의 설정 메타데이터에서, 독립적인 <alias/> 요소를 사용하면 가능하다. 예를 들어:
<alias name="fromName" alias="toName"/>
이 경우, 동인한 컨테이너 내에 있는 'fromName'이라는 이름의 bean은 별칭을 정의한 뒤에는 'toName'으로 참조할 수 있다.
구체적인 예를들면, XML 일부분에서 컴포넌트 A가 componentA-dataSource라고 불리는 DataSource bean을 정의하는 경우에, 컴포넌트 B는 그 DataSource를 XML 설정의 다른 부분에서 componentB-dataSource와 같이 참조할 것이다. 주 애플리케이션인 MyApp는 자체적인 XML의 일부를 정의하고 3개의 부분 모두로 부터 최종적인 애플리케이션 컨텍스트 조합해낸다. 그리고 그 DataSource를 myApp-dataSource로 참조할것이다. 이 시나리오는 MyApp XML 일부에 다음의 독립적인 별칭을 추가하여 쉽게 다룰수 있다.
<alias name="componentA-dataSource" alias="componentB-dataSource"/> <alias name="componentA-dataSource" alias="myApp-dataSource" />
이제 각각의 컴포넌트와 주 애플리케이션은 유일하면서 다른 정의들과 충돌하지 않을 것이 보장된 이름(사실상 네임스페이스가 있는것이다)을 통해서 dataSource를 참조하면서도, 여전히 그것들은 같은 bean을 참조하는 것이다.
Spring IoC 컨테이너 관한한, bean 정의는 기본적으로 하나 혹은 그 이상의 실제 객체를 생성하기 위한 조리법 같은 것이다. 컨테이너는 요청이 들어오면 지정된 bean을 위한 조리법을 찾아보고, 해당 bean정의에 의해 캡슐화된 설정 메타데이터를 사용하며 계속 진행해서, 설정 사항들을 반영한 실물 객체를 생성한다. 이번 섹션은 어플리케이션 개발자가 Spring IoC 컨테이너에게 객체가 어떤 타입(혹은 클래스)으로 인스턴스화되는지, 그리고 최종적으로 생성될 객체를 어떻게 인스턴스화하는지 지정하는 방법에 대해 다룰 것이다.
XML 기반의 설정 메타데이터를 사용한다면, <bean/> 요소의 'class' 속성을 사용하여 인스턴스를 생성할 객체의 타입(혹은 클래스)을 명시할수 있다. 이 'class' 속성(내부적으로는 최종적으로 BeanDefinition 인스턴스의 Class프라퍼티가 된다)은 일반적으로 필수 (Section 3.2.3.2.3, “인스턴스 factory 메소드를 사용하여 인스턴스화 생성하기” 와 Section 3.7, “Bean정의 상속”에 두가지 예외가 있다)이고 두가지 목적 중 하나를 위해 사용된다. 컨테이너 자체가 생성자를 호출('new'를 사용하는 Java코드와 어떤 면에서는 동일한 방식으로)하여 bean을 직접 생성하는 대부분의 일반적인 경우에서는 class 속성에는 생성될 bean의 클래스를 기입한다. 컨테이너가 bean을 생성하기 위해 클래스의 정적(static), factory 메소드를 호출하는 다소 덜 일반적인 상황에서는 class 프라퍼티는 객체를 생성하기 위해 호출되는 static factory메소드를 포함하는 실제 클래스를 기입한다 (static factory메소드의 호출로부터 반환되는 객체의 타입은 class 속성에 지정한 것과 같은 클래스이거나 완전히 다른 클래스여도 상관없다).
생성자를 통한 접근방법으로 bean을 생성할때, 모든 일반적인 클래스는 Spring과 호환되며 사용하는데 문제가 없다. 다시말해, 생성할 클래스는 어떠한 특정 인터페이스를 구현하거나 특정 형태로 코딩할 필요가 없다. 단지 bean클래스를 명시하는 것만으로 충분하다. 하지만 지정된 bean을 위해 어떤 타입의 IoC를 사용할것이냐에 따라서 기본(아무것도 하지 않는) 생성자가 필요할 수도 있다.
추가적으로, Spring IoC 컨테이너는 진정한 JavaBeans만을 관리하는 것으로 제한 받지 않고, 사실상 당신이 원하는 그 어떤 클래스라도 관리할 수 있다. Spring을 사용하는 대부분의 사람들은 실제 JavaBean(기본 생성자와 프라퍼티에 따라서 적절한 setter와 getter를 가지는)을 컨테이너 내에 두길 선호하지만, 좀더 색다르고 bean 스타일이 아닌 클래스도 가능하다. 예를들어, 만약 JavaBean 스펙을 따르지 않는 예전에 만들어진 connection pool을 사용할 필요가 있다하도, Spring은 역시나 그것도 잘 관리한다.
XML-기반 설정 메타데이터를 사용할때 당신은 다음처럼 bean클래스를 명시할 수 있다. :
<bean id="exampleBean" class="examples.ExampleBean"/> <bean name="anotherExample" class="examples.ExampleBeanTwo"/>
생성자에 인자를 제공(필요할 경우에)하는 기법이나, 객체를 생성한 이후에 객체 인스턴스에 프라퍼티 값을 설정하는 방법은 곧 설명할 것이다
static factory 메소드를 사용해서 생성할 bean을 정의할 때는 클래스를 명시하는 class 속성에 static factory메소드를 포함하고 있는 클래스를 지정하고, factory-method라는 또 다른 속성에 factory 메소드의 이름을 명시할 필요가 있다. Spring은 그 메소드를 (나중에 알아볼 예정인 추가적인 인자를 함께 전달하는 방법과 함께) 호출할 것이고, 일반적으로 생성자를 통해 생성된 것과 다를 바 없이 사용할 수 있는 생생한 객체를 돌려 받게 될 것이다. 이러한 bean 정의 방식은 예전 방식의 코드에서 static factory를 호출할 때 사용할 수 있다.
다음 예제에서 factory 메소드를 호출하여 생성되는 bean을 정의하는 방법을 볼 수 있다. 이 bean 정의가 반활될 객체의 타입(클래스)를 명시하지 않고, 오직 factory 메소드를 가지고 있는 클래스만을 명시함을 주의해서 보라. 이 예제에서, createInstance() 메소드는 꼭 static 메소드이어야만 한다.
<bean id="exampleBean" class="examples.ExampleBean2" factory-method="createInstance"/>
factory메소드에 (추가적인)인자를 제공하는 기법이나 factory로부터 객체 인스턴스를 생성한 후에 그 인스턴스에 프라퍼티를 설정하는 것은 곧 설명할 것이다.
static factory method를 통해 인스턴스를 생성하는 것과 유사한 방법으로, 인스턴스 factory 메소드를 사용하여 인스턴스화를 생성한다는 것은 이미 컨테이너에 존재하는 bean의 factory 메소드를 호출하여 새로운 bean을 만들어 냄을 의미한다.
이 기법을 사용하기 위해, 'class' 속성은 설정하면 안되고 동일(혹은 부모/조상) 컨테이너에 있는 factory메소드를 가진 bean의 이름을 'factory-bean' 속성을 명시해야만 한다. factory메소드 자체는 여전히 'factory-method' 속성을 통해 설정하면 된다(아래의 예제에서 보이는것처럼).
<!-- createInstance()라는 factory 메소드를 포함하고 있는 factory bean--> <bean id="myFactoryBean" class="..."> ... </bean> <!-- factory bean을 통해 생성할 bean --> <bean id="exampleBean" factory-bean="myFactoryBean" factory-method="createInstance"/>
비록 여전히 bean프라퍼티를 설정하는 메카니즘을 논의할 예정이긴 하지만, 이러식의 접근 방법은 factory bean 자체를 DI를 통해 설정하고 관리할 수 있음을 알 수 있게 해준다.
BeanFactory는 기본적으로 다른 bean과 그것들의 의존성의 등록을 유지하는 향상된 factory능력을 위한 인터페이스에 지나지 않는다. BeanFactory는 당신에게 bean factory를 사용하여 bean정의를 읽고 그것들에 접근하는 것을 가능하게 한다. BeanFactory를 사용할때 당신은 다음처럼 하나를 생성하고 XML형태의 몇몇 bean정의내에서 읽을것이다.
InputStream is = new FileInputStream("beans.xml"); BeanFactory factory = new XmlBeanFactory(is);
getBean(String)를 사용하여 당신의 bean인스턴스를 가져올수 있다. BeanFactory의 클라이언트측 시각은 놀라울정도로 간단한다. BeanFactory 인터페이스는 호출할 클라이언트를 위해 오직 6개의 메소드만을 가진다.
boolean containsBean(String): BeanFactory가 bean정의나 주어진 이름에 대응되는 bean인스턴스를 포함한다면 true를 반환한다.
Object getBean(String): 주어진 이름하에 등록된 bean의 인스턴스를 반환한다. bean이 어떻게 설정되는지는 BeanFactory설정에 의존한다. 싱글톤과 공유 인스턴스나 새롭게 생성되는 bean은 반환될것이다. BeansException은 bean을 찾을수 없을때(이 경우 이것은 NoSuchBeanDefinitionException이 될것이다.)나 bean을 인스턴스화하거나 준비하는 동안 예외가 발생할때 던져질것이다.
Object getBean(String,Class): 주어진 이름하에 등록된 bean을 반환한다. 반환되는 bean은 주어진 클래스로 형변환될것이다. 만약 bean이 형변환될수 없다면 관련 예외(BeanNotOfRequiredTypeException)가 던져질것이다. 게다가 getBean(String) 메소드의 모든 규칙(위에서 본)을 적용한다.
Class getType(String name): 주어진 이름을 가진 bean의 Class를 반환한다. 주어진 이름을 가진 bean이 없다면, NoSuchBeanDefinitionException 가 던져질것이다.
boolean isSingleton(String): 주어진 이름하에 등록된 bean정의나 bean인스턴스가 싱글톤인지 아닌지 조사한다(싱글톤과 같은 bean범위는 later에서 설명된다.). 만약 주어진 이름에 관련된 bean이 발견되지 않는다면 NoSuchBeanDefinitionException가 던져질것이다.
String[] getAliases(String): 만약 bean정의내 어떠한 것도 명시되어 있다면 주어진 bean이름을 위한 별칭을 반환한다.
당신의 전형적인 기업용 애플리케이션은 한개의 객체(또는 Spring내 bean)로 만들어지지는 않는다. 가장 간단한 애플리케이션조차도 최종 사용자가 응집성있는 애플리케이션처럼 보는것을 표현하기 위해 함께 작동하는 소량의 객체를 가진다는것을 의심할 필요가 없을것이다. 다음 부분은 독립적인 많은 수의 bean정의를 정의하는 것으로부터 객체가 몇가지 목표(대개 최종사용자가 원하는 것을 수행하는 애플리케이션)를 달성하기 위해 함께 작동하는 전체적으로 실제화된 애플리케이션까지 수행하는 방법을 설명한다.
Dependency Injection (DI) 배후의 기본개념은 생성자의 인자, factory메소드에 대한 인자, 또는 factory메소드로부터 생성되거나 반환된후 객체 인스턴스에 셋팅되는 프라퍼티를 통해서만 의존성을 정의하는 객체이다. 이것은 bean을 생성할때 이러한 의존성을 실제로 삽입하기 위한 컨테이너의 작업이다. 이것은 본질적으로 반대이고 나아가 이름으로는 Inversion of Control (IoC) 이다. bean자체는 인스턴스화를 제어하거나 클래스의 직접적 생성을 사용하여 자체적으로 의존성을 위치시키거나 서비스 위치자 패턴와 같은 것이다.
DI개념을 적용될때 코드가 좀더 깔끔해지도록 사용법이 분명해진다. 그것들과 함께 제공되지만 bean이 의존성을 룩업할때 디커플링의 좀더 높은 단계에 도착하는 것이 좀더 쉽다.(그리고 추가적으로 의존성이 위치한 곳과 실제 클래스가 하는 것이 무엇인지 알지 못한다.)
이전 단락에서 보여준것처럼, DI는 두가지의 형태로 존재한다. 명명하여 Setter 삽입 과 생성자(Constructor) 삽입이다.
Setter-기반의 DI는 인자가 없는 생성자나 인자가 없는 static factory 메소드가 bean을 인스턴스화하기 위해 호출된후 bean의 setter메소드를 호출하여 실제화된다.
setter삽입을 사용하여 의존성이 삽입되는 클래스의 예제를 아래에서 보자. 이 클래스에 대해 특별한 것은 없다. 이것은 일반적인 Java이다.
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on the MovieFinder private MovieFinder movieFinder; // a setter method so that the Spring container can 'inject' a MovieFinder public void setMoveFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually 'uses' the injected MovieFinder is omitted... }
생성자-기반의 DI는 각각의 협력자는 표시하는 많은 수의 인자를 가진 생성자를 호출하여 실제화된다. 추가적으로, bean을 생성하기 위한 특정 인자를 가진 static factory메소드를 호출하는 것은 대부분 동등하게 간주될수 있고 이 텍스트의 나머지는 생성자를 위한 인자와 static factory메소드를 위한 인자를 검토할것이다.
생성자 삽입을 사용하여 의존성을 삽입할수 있는 클래스의 예제를 아래에서 보라. 다시, 이 클래스에 대한 특별한 것은 없다.
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on the MovieFinder private 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... }
BeanFactory는 의존성을 bean에 삽입하기 위한 이러한 모든 형태를 지원한다(사실 몇가지 의존성이 생성자 접근법을 통해 제공된 후 setter-기반의 의존성을 삽입하는 것을 지원한다). BeanDefinition의 형태인 의존성을 위한 설정은, 한 형태의 프라퍼티를 다른 형태로 변환하는 방법을 아는 PropertyEditor 인스턴스와 함께 사용된다. 어쨌든, 대부분의 Spring사용자는 직접 이러한 클래스를 다루지 않고 클래스의 인스턴스로 내부적으로 변환하기 위해 Spring IoC컨테이너 인스턴스를 로드하기 위해 사용되는 XML정의 파일을 사용할것이다.
bean의존성 해석은 다음처럼 발생한다.
BeanFactory는 모든 bean을 언급하는 설정으로 생성되고 초기화된다(대개의 Spring사용자는 XML형태의 설정파일을 지원하는 BeanFactory 나 ApplicationContext 구현물을 사용한다.)
각각의 bean은 프라퍼티, 생성자의 인자, 또는 대개의 생성자 대신에 사용되는 static-factory메소드의 인자의 형태로 표시되는 의존성을 가진다. 이러한 의존성은 bean이 실제로 생성될때 bean에 제공될것이다.
각각의 프라퍼티 또는 생성자의 인자는 실제 셋팅되기 위한 값의 정의이거나 컨테이너내 다른 bean에 대한 참조이다.
각각의 프라퍼티와 생성자의 인자는 이것이 명시하는 어떠한 타입에서 프라퍼티나 생성자의 인자의 실질적인 타입으로 변환될수 있어야만 하는 값이다. 디폴트에 의해 Spring은 문자열로 제공되는 값에서 int, long, String, boolean, 등등과 같은 모든 내장 타입으로 변환할수 있다.
Spring이 컨테이너가 생성될때 컨테이너내 각각의 bean의 설정의 유효성을 체크하는 것을 실제화하는 것이 중요하다. It is important to realize that Spring validates the configuration of each bean in a container as the container is created, including the validation that properties which are bean references are actually referring to valid beans (i.e. the beans being referred to are also defined in the container. However, the bean properties themselves are not set until the bean is actually created. For that which are singleton-scoped and set to be pre-instantiated (such as singleton beans in an ApplicationContext), creation happens at the time that the container is created, but otherwise this is only when the bean is requested. When a bean actually has to be created, this will potentially cause a graph of other beans to be created, as its dependencies and its dependencies' dependencies (and so on) are created and assigned.
You can generally trust Spring to do the right thing. It will detect mis-configuration issues, such as references to non-existent beans and circular dependencies, at container load-time. It will actually set properties and resolve dependencies (i.e. create those dependencies if needed) as late as possible, which is when the bean is actually created. This does mean that a Spring container which has loaded correctly, can later generate an exception when you request a bean, if there is a problem creating that bean or one of its dependencies. This could happen if the bean throws an exception as a result of a missing or invalid property, for example. This potentially delayed visibility of some configuration issues is why ApplicationContext implementations by default pre-instantiate singleton beans. At the cost of some upfront time and memory to create these beans before they are actually needed, you find out about configuration issues when the ApplicationContext is created, not later. If you wish, you can still override this default behavior and set any of these singleton beans to lazy-initialize (i.e. not be pre-instantiated).
Finally, if it is not immediately apparent, it is worth mentioning that when one or more collaborating beans are being injected into a dependent bean, each collaborating bean is totally configured prior to being passed (via one of the DI flavors) to the dependent bean. This means that if bean A has a dependency on bean B, the Spring IoC container will totally configure bean B prior to invoking (for example) the attendant setter method on bean A; you can read 'totally configure' to mean that the bean will be instantiated (if not a pre-instantiated singleton), all of its dependencies will be set, and the relevant lifecycle methods (such as a configured init method or the IntializingBean callback method will all be invoked.
먼저, setter-기반의 DI를 위한 XML-기반의 설정 메타데이터를 사용하는 예제이다. 아래에서 몇가지 bean정의를 명시하는 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"/>
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; } }
당신이 볼수 있는 것처럼 setter는 XML파일내 명시되는 프라퍼티에 대응하기 위해 선언된다.
이제는 생성자-기반의 DI를 사용하는 예제이다. 아래는 생성자의 인자와 관련 Java클래스를 명시하는 XML설정의 일부이다.
<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"/>
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; } }
당신이 볼수 있는것처럼 bean정의내 명시되는 생성자의 인자는 ExampleBean의 생성자를 위한 인자로 전달될것이다.
지금 생성자를 사용하는것 대신에 사용되는 것들의 다양한 종류를 검토해보자. Spring은 객체의 인스턴스를 반환하기 위해 정적 factory메소드를 호출하는것을 말한다.
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg><ref bean="anotherExampleBean"/></constructor-arg> <constructor-arg><ref bean="yetAnotherBean"/></constructor-arg> <constructor-arg><value>1</value></constructor-arg> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
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;
}
}
정적 factory메소드를 위한 인자는 constructor-arg요소를 통해 제공된다. 생성자가 실질적으로 사용되는 것처럼 정확하게 같다. 그 인자들은 선택사항이다. 물론 정적 factory메소드를 포함하는 클래스와 같은 타입이 되지않을 factory메소드에 의해 반환될 클래스의 타입을 구체화하는것은 중요하다. 앞에서 언급된 인스턴스(정적이 아닌) factory메소드는 기본적으로 동일한 형태(class속성 대신에 factory-bean속성의 사용을 제외하고)로 사용되기 때문에 여기서는 상세하기 다루지 않을것이다.
생성자의 인자 분석 대응(matching)은 인자타입을 사용할때 발생한다. bean정의의 생성자의 인자에서 잠재적인 모호함이 없다면, bean정의에 정의된 생성자의 인자의 순서는 초기화될때 적절한 생성자에 제공될 인자의 순서이다. 다음의 클래스를 검토하라.
package x.y; public class Foo { public Foo(Bar bar, Baz baz) { // ... } }
There is no potential for ambiguity here (assuming of course that Bar and Baz classes are not related in an inheritance hierarchy). Thus the following configuration will work just fine, and one does not need to specify the constructor argument indexes and / or types explicitly... it just plain works as one would expect it to.
<beans> <bean name="foo" class="x.y.Foo"> <constructor-arg> <bean class="x.y.Bar"/> </constructor-arg> <constructor-arg> <bean class="x.y.Baz"/> </constructor-arg> </bean> </beans>
When another bean is referenced, the type is known, and matching can occur (as was the case with the preceding example). When a simple type is used, such as <value>true<value>, Spring cannot determine the type of the value, and so cannot match by type without help. Consider the following class, which is used for the following two sections:
package examples; public class ExampleBean { // No. of years to the calculate the Ultimate Answer private int years; // The Answer to Life, the Universe, and Everything private 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</value></constructor-arg> <constructor-arg type="java.lang.String"><value>42</value></constructor-arg> </bean>
생성자의 인자는 index 속성을 사용하여 명확하게 명시된 인덱스를 가질수 있다. 예를 들면.
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean>
여러개의 간단한 값들의 모호한 문제를 푸는것에 더하여 인덱스를 명시하는 것은 생성자가 같은 타입의 두개의 인자를 가지는 모호함의 문제도 해결한다. 인덱스는 0 부터 시작된다는것에 주의하라.
![]() | Tip |
---|---|
생성자의 인자 인덱스를 명시하는것은 생성자 IoC를 수행하는 방법이 선호된다. |
앞 부분에서 언급된것처럼 bean프라퍼티와 생성자의 인자는 다른 관리빈(협력자), 또는 인라인으로 정의된 값의 참조처럼 정의될수 있다. Spring의 XML-기반의 설정 메타데이터는 이러한 목적을 위한 <property/> 와 <constructor-arg/>요소내에서 많은 수의 하위요소를 지원한다.
<value/> 요소는 사람이 읽을수 있는 문자열 표현처럼 프라퍼티나 생성자의 인자를 명시한다. 앞서 상세하게 언급된것처럼 자바빈 PropertyEditors는 java.lang.String로 부터 문자열값을 실질적인 프라퍼티나 인자타입으로 변환하기 위해 사용된다.
<bean id="myDataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/mydb</value>
</property>
<property name="username">
<value>root</value>
</property>
</bean>
idref 요소는 컨테이너내 다른 bean의 id를 (<constructor-arg/> 나 <property/>요소에)전달하기 위한 에러를 검사(error-proof)하는 방법이다.
<bean id="theTargetBean" class="..."/> <bean id="theClientBean" class="..."> <property name="targetName"> <idref bean="theTargetBean" /> </property> </bean>
이것은 수행시 다음의 조각들과 동일하다.
<bean id="theTargetBean" class="..."/> <bean id="client" class="..."> <property name="targetName"> <value>theTargetBean</value> </property> </bean>
첫번째 형태가 두번째를 선호하는 가장 중요한 이유는 idref 태그를 사용하는 것이 Spring이 다른 bean이 실질적으로 존재하는 배치시점에 유효하도록 허용한다는것이다. 두번째 에서, 'client' bean의 'targetName'프라퍼티에 전달되는 값에서 수행되는 유효성체크는 없다. 어느 활자는 'client' bean이 인스턴스화될때 발견된다. 'client' bean이 prototype bean이라면, 이 활자(와 결과 예제)는 컨테이너가 실제로 배치된후에 발견될것이다.
추가적으로 만약 참조되는 bean이 같은 XML파일내에 있고 bean이름이 bean id라면 사용될 local 속성은 XML문서를 파싱하는 시점에 좀더 일찍 XML파서가 bean이름을 자체적으로 체크하는것을 허용할것이다.
<property name="targetName">
<!-- a bean with an id of 'theTargetBean' must exist, else an XML exception will be thrown -->
<idref local="theTargetBean"/>
</property>
예제의 방법에 의해, 값을 가져오는 <idref/> 요소의 한가지 공통된 위치는 ProxyFactoryBean bean 정의내 AOP 인터셉터의 설정내이다. 인터셉터 이름을 명시할때 <idref/> 요소를 사용한다면, 무심코 인터셉터의 id를 잘못쓰는 경우는 없다.
ref요소는 <constructor-arg/> 또는 <property/> 정의 요소내부에 허용되는 마지막 요소이다. 이것은 컨테이너에 의해 관리되는 다른 bean에 대해 참조되기 위해 명시된 프라퍼티의 값을 셋팅하는데 사용된다. 앞 부분에서 언급했던것 처럼 참조되는 bean은 프라퍼티가 셋팅되는 bean의 의존성이 되는것이 검토되고 프라퍼티가 셋팅되기 전에 필요(만약 이것이 싱글톤 bean이라면 이것은 컨테이너에 의해 이미 초기화되었을것이다.)하다면 요구에 의해 초기화될것이다. 모든 참조는 궁극적으로 다른 객체에 대한 참조이지만 다른 객체의 id/name을 명시하는 방법은 3가지가 있다.
<ref/> 태그의 bean 속성을 사용하여 대상 bean을 명시하는것이 가장 일반적인 형태이고 같은 컨테이너(같은 XML파일이든 아니든)나 부모 컨테이너내에서 어떠한 bean에 대한 참조를 생성하는 것을 허용할것이다. 'bean' 속성의 값은 대상 bean의 'id' 속성이나 'name' 속성의 값중 하나처럼 같은것이 될것이다.
<ref bean="someBean"/>
local 속성을 사용하여 대상 bean을 명시하는것은 같은 파일내 타당한 XML id 참조를 위한 XML파서의 기능에 영향을 미친다. local 속성의 값은 대상 bean의 id 속성과 같아야만 한다. XML파서는 대응되는 요소가 같은 파일내에 발견되지 않는다면 에러를 발생시킬것이다. 그런것처럼 만약 대상 bean이 같은 XML파일내 있다면 local 형태를 사용하는 것이 가장 좋은 선택(가능한한 빨리 에러에 대해 알기 위해)이다.
<ref local="someBean"/>
'parent' 속성을 사용하여 대상 bean을 명시하는 것은 현재 컨테이너의 부모 컨테이너내 있을 bean을 위해 생성될 참조를 허용한다. 'parent' 속성의 값은 아마 대상 bean의 'id' 속성이나 'name' 속성내 값중에 하나와 같을것이고 대상 bean은 최근것을 위해 부모 컨테이너내 있어야만 한다. bean참조 형태의 가장 중요한 사용은 몇몇 프록시의 순서대로 부모 컨텍스트내 존재하는 bean을 포장하기 위해 필요할때이고 초기의 객체는 그것을 포장하기 위해 필요하다.
<!-- in the parent context --> <bean id="accountService" class="com.foo.SimpleAccountService"> <!-- insert dependencies as required as here --> </bean>
<!-- in the child (descendant) context --> <bean id="accountService" <-- notice that the name of this bean is the same as the name of the 'parent' bean class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref parent="accountService"/> <-- notice how we refer to the parent bean </property> <!-- insert other configuration and dependencies as required as here --> </bean>
('parent' 속성의 사용은 전혀 공통적이지 않다.)
<property/> 나 <constructor-arg/> 요소내부의 <bean/> 요소는 내부 bean이라 불리는 것을 정의하기 위해 사용된다. 내부 bean정의는 정의된 id나 name을 필요로 하지 않고 언급된 id나 name값은 컨테이너에 의해 무시되기 때문에 id나 name값을 명시하지 않는 것이 가장 좋다.
내부 bean의 예제를 아래에서 보라.
<bean id="outer" class="..."> <!-- instead of using a reference to a target bean, simply define the target inline --> <property name="target"> <bean class="com.mycompany.Person"> <!-- this is the inner bean --> <property name="name" value="Fiona Apple"/> <property name="age" value="25"/> </bean> </property> </bean>
내부 bean의 특별한 경우, 'singleton' 플래그와 'id' 또는 'name'속성은 사실상 무시된다. 내부 bean은 언제나 익명이고 언제나 prototypes과 같이 범위화된다. 둘러싸인 bean보다는 협력하는 bean으로 내부 bean을 삽입하는 것은 가능하지 않다는 것을 노트하라.
<list/>, <set/>, <map/>, 과 <props/>요소는 정의되고 셋팅되는 Java Collection List, Set, Map, and Properties의 프라퍼티와 인자를 허용한다.
<bean id="moreComplexObject" class="example.ComplexObject"> <!-- results in a setAdminEmails(java.util.Properties) call --> <property name="adminEmails"> <props> <prop key="administrator">administrator@somecompany.org</prop> <prop key="support">support@somecompany.org</prop> <prop key="development">development@somecompany.org</prop> </props> </property> <!-- results in a setSomeList(java.util.List) call --> <property name="someList"> <list> <value>a list element followed by a reference</value> <ref bean="myDataSource" /> </list> </property> <!-- results in a setSomeMap(java.util.Map) call --> <property name="someMap"> <map> <entry> <key> <value>yup an entry</value> </key> <value>just some string</value> </entry> <entry> <key> <value>yup a ref</value> </key> <ref bean="myDataSource" /> </entry> </map> </property> <!-- results in a setSomeSet(java.util.Set) call --> <property name="someSet"> <set> <value>just some string</value> <ref bean="myDataSource" /> </set> </property> </bean>
map key의 value나 value, 또는 set value는 다음의 어떤 요소도 될수 있다는 것을 노트하라.
bean | ref | idref | list | set | map | props | value | null
Spring 2.0에서, 컨테이너는 collection의 병합또한 지원한다. 이것은 애플리케이션 개발자에게 부모 스타일의 <list/>, <map/>, <set/> 또는 <props/>요소를 정의하고 부모 collection으로 부터 상속하거나 값을 덮는 자식 스타일의 <list/>, <map/>, <set/> 또는 <props/>요소를 가지는 것을 허용한다. 예를 들면, 자식 collection의 값은 자식 collection요소가 부모 collection내 명시된 값을 덮는 부모와 자식 collection 요소의 병합으로 부터 얻어지는 결과가 될것이다.
병합에 대한 이 섹션은 부모-자식 bean기법의 사용하게 한다. 이 개념은 아직 소개되지 않아서, 부모와 자식 bean정의의 개념에 친숙하지 않은 독자는 지속되기 전에 관련 섹션을 읽기를 바란다(Section 3.7, “Bean정의 상속” 를 보라).
예제는 이 기능을 보여주기 위한 가장 좋은 소스를 제공한다.
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">administrator@somecompany.com</prop>
<prop key="support">support@somecompany.com</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the *child* collection definition -->
<props merge="true">
<prop key="sales">sales@somecompany.com</prop>
<prop key="support">support@somecompany.co.uk</prop>
</props>
</property>
</bean>
<beans>
child bean정의의 adminEmails 프라퍼티의 <props/>요소에서 merge=true속성을 사용하자. child bean이 컨테이너에 의해 실질적으로 분석되고 인스턴스화될때, 결과 인스턴스는 부모의 adminEmails collection을 가진 자식의 adminEmails collection의 병합의 결과를 포함하는 adminEmails Properties collection을 가질것이다.
administrator=administrator@somecompany.com sales=sales@somecompany.com support=support@somecompany.co.uk
자식 Properties collection의 값 세트가 부모 <props/>로부터 모든 프라퍼티 요소를 상속하는 방법에 주의하라. support 값을 위한 자식 값이 부모 collection내 부수적인 값을 덮는 방법에 주의하라.
이 병합행위는 <list/>, <map/>, 그리고 <set/> collection타입에 유사하게 적용한다. <list/>요소의 특정경우에, 이 의미는 List collection타입과 관련된다. 이를테면, value의 ordered collection의 개념은 유지관리된다. 부모값은 모든 자식 목록의 값에 선행한다. Map, Set, 그리고 Properties collection타입의 경우, 컨테이너에 의해 내부적으로 사용되는 Map, Set 그리고 Properties구현물 타입에 관련된 collection타입을 위한 영향을 끼친다.
마지막으로, 병합 지원에 대한 몇몇 사소한 노트는 순서대로이다. 첫째 다른 collection타입간(이를 테면, Map 과 List)의 병합을 할수 없다. 그리고 하나가 그렇게 시도한다면 Exception을 던질것이다. 그리고 이 경우에는, 알기쉽다. 'merge' 속성은 더 낮은 레벨, 상속된, 자식 정의..에서 명시되어야만 한다. 부모 collection정의의 'merge'속성을 명시하는 것은 예측가능하고 바람직한 병합의 결과를 만들지 않을것이다. 그리고 마지막으로, 이 병합 기능은 오직 Spring 2.0(과 그 이후 버전)에서만 사용가능하다.
If you are one of the lucky few to be using Java5 (Tiger), you will be aware that it is possible (and I daresay recommended) to have strongly typed collections. That is, it is possible to declare a Collection type such that it can only contain String elements (for example).
If you are using Spring to dependency inject a strongly-typed Collection into a bean, you can take advantage of Spring's type-conversion support such that the elements of your strongly-typed Collection instances will be converted to the appropriate type prior to being added to the Collection.
An example will make this clear; consider the following class definition, and it's attendant (XML) configuration...
public class Foo { private Map<String, Float> accounts; public void setAccounts(Map<String, Float> accounts) { this.accounts = accounts; } }
<beans> <bean id="foo" class="x.y.Foo"> <property name="accounts"> <map> <entry key="one" value="9.99"/> <entry key="two" value="2.75"/> <entry key="six" value="3.99"/> </map> </property> </bean> </beans>
When the 'accounts' property of the 'foo' bean is being prepared for injection, the generics information about the element type of the strongly-typed Map<String, Float> is actually available via reflection, and so Spring's type conversion infrastructure will actually recognize the various value elements as being of type Float and so the string values '9.99', '2.75', and '3.99' will be converted into an actual Float type.
<null/> 요소는 null 값을 다루기 위해 사용된다. Spring은 프라퍼티를 위한 빈 인자를 빈 문자열처럼 처리한다. 다음의 XML-기반의 설정 메타데이터 조각은 email프라퍼티를 빈 String값으로 셋팅되도록 한다.
<bean class="ExampleBean"> <property name="email"><value></value></property> </bean>
이것은 다음의 Java코드인 exampleBean.setEmail("")과 동등하다. 특별한 <null> 요소는 아마도 null값을 표시하는데 사용될것이다.
<bean class="ExampleBean"> <property name="email"><null/></property> </bean>
위 설정은 다음의 자바코드인 exampleBean.setEmail(null) 와 동등하다.
이것은 value이나 bean참조를 설정하기 위해 필요한 공통사항이다. 완전한 형태의 <value/> 와 <ref/>요소를 사용하는것보다 다소 덜 장황하게 간략화한 몇가지 형태가 존재한다. <property/>, <constructor-arg/>, 그리고 <entry/>요소 모두 완전한 형태의 <value/> 요소 대신에 사용된 'value' 속성을 지원한다. 그러므로 다음은
<property name="myProperty"> <value>hello</value> </property
<constructor-arg> <value>hello</value> </constructor-arg>
<entry key="myKey"> <value>hello</value> </entry>
동일하다.
<property name="myProperty" value="hello"/>
<constructor-arg value="hello"/>
<entry key="myKey" value="hello"/>
대개 손으로 정의를 작성할때 당신은 다소 덜 장황한 간략화된 형태를 사용하는것을 선호할것이다.
<property/> 와 <constructor-arg/>요소는 완전한 형태의 내포된 <ref/> 요소 대신에 사용될 유사하게 간략화된 'ref' 속성을 지원한다. 그러므로 다음은
<property name="myProperty"> <ref bean="myBean"> </property>
<constructor-arg> <ref bean="myBean"> </constructor-arg>
는 다음과 동일하다.
<property name="myProperty" ref="myBean"/>
<constructor-arg value="myBean"/>
어쨌든 간략화된 형태는 <ref bean="xxx">요소와 동등하다. <ref local="xxx"> 를 위한 간략화된 형태는 없다. 엄격한 local ref를 강요하기 위해 당신은 긴 형태를 사용해야만 한다.
마지막으로 entry 요소는 'key'/'key-ref' 와 'value'/'value-ref'속성의 형태로 map의 키 그리고/또는 값을 명시하기 위한 간략화된 형태를 허용한다. 그러므로 다음은
<entry> <key> <ref bean="myKeyBean" /> </key> <ref bean="myValueBean" /> </entry>
는 다음과 동일하다.
<entry key-ref="myKeyBean" value-ref="myValueBean"/>
다시 간략화된 형태는 <ref bean="xxx">요소와 동일하다. <ref local="xxx">를 위한 간략화된 형태는 없다.
복합적이거나 내포된 프라퍼티명은 bean프라퍼티를 셋팅할때 완전히 규칙에 따른다(legal)는 것을 알아두라. 마지막 프라퍼티명을 제외한 경로의 모든 컴포넌트는 null이 아니다. 예를 들면, 이 bean정의내에서:
<bean id="foo" class="foo.Bar"> <property name="fred.bob.sammy" value="123" /> </bean>
foo bean은 bob 프라퍼티를 가지는 fred프라퍼티를 가진다. 그리고 bob 프라퍼티는 sammy프라퍼티를 가지고 마지막 sammy 프라퍼티는 123값으로 셋팅된다. 이 작업을 위해, foo의 fred프라퍼티, 그리고 fred의 bob프라퍼티는 bean이 생성된 후에 null이 아니어야만 한다. 그렇지 않으면 NullPointerException예외가 던져질것이다.
대부분의 상황을 위해 bean이 다른 것들의 의존성이라는 사실은 하나의 bean이 다른것의 프라퍼티처럼 셋팅한다는 사실에 의해 간단하게 표현된다. 이것은 XML-기반의 설정 메타데이터내 <ref/>요소를 가지고 수행한다. 이것의 다양한 종류에서 때때로 컨테이너를 인식하는 bean은 간단하게 주어진 의존성(문자열 값이나 문자열 값과 같은것을 평가하는 <idref/>요소의 대안을 사용하여)의 id이다. 첫번째 bean은 이것의 의존성을 위해 컨테이너에 프로그램마다 다른 방식으로 요청한다. 어느 경우에나 의존성은 의존적인 bean이전에 초기화된다.
다소 덜 직접적인(예를 들면, 데이터베이스 드라이버 등록과 같은 클래스내 정적인 초기자가 트리거 될 필요가 있을때) bean들 사이의 의존성이 있는 비교적 드물게 발생하는 상황을 위해 'depends-on' 속성은 이 초기화된 요소를 사용하는 bean이전에 초기화되기 위한 하나 이상의 bean을 명시적으로 강제하기 위해 사용된다. 하나의 bean에 의존성을 표시하기 위한 'depends-on' 속성을 사용하는 예제를 아래에서 보라.
<bean id="beanOne" class="ExampleBean" depends-on="manager"/> <bean id="manager" class="ManagerBean" />
다중 bean에 의존성을 표시할 필요가 있다면, 콤마, 공백 그리고 세미콜론과 같은 모든 유효한 구분자를 사용하여 'depends-on'속성의 값으로 bean이름의 구분된 목록을 제공할수 있다. 많은 수의 bean에 의존성을 표시하기 위해 'depends-on'를 사용하는 예제를 아래에서 보라.
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao"> <property name="manager" ref="manager" /> </bean> <bean id="manager" class="ManagerBean" /> <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
The default behavior for ApplicationContext implementations is to eagerly pre-instantiate all singleton beans at startup. Pre-instantiation means that an ApplicationContext implementation instance will eagerly create and configure all of it's singleton beans as part of its initialization process. This is generally a good thing, because it means that any errors in the configuration or in the attendant environment will be discovered immediately (as opposed to possibly hours or even days down the line).
However, there are times when this behavior is not what is wanted. If you do not want a singleton bean to be pre-instantiated when using an ApplicationContext implementation, you can (on a bean-definition by bean-definition basis) selectively control this by marking a bean definition as lazy-initialized. A lazily-initialized bean indicates to the IoC container whether or not a bean instance should be created at startup or when it is first requested.
When configuring beans via XML, this lazy loading is controlled by the 'lazy-init' attribute on the <bean/> element; to wit:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"> <!-- various properties here... --> </bean> <bean name="not.lazy" class="com.foo.AnotherBean"> <!-- various properties here... --> </bean>
When the above configuration is consumed by an ApplicationContext implementation, the bean named 'lazy' will not be eagerly pre-instantiated when the ApplicationContext is starting up, whereas the 'not.lazy' bean will be eagerly pre-instantiated.
One thing to understand about lazy-initialization is that even though a bean definition may be marked up as being lazy-initialized, if the lazy-initialized bean is the dependency of a singleton bean that is not lazy-initialized, when the ApplicationContext is eagerly pre-instantiating the singleton, it will (of course) have to satisfy all of said singletons dependencies, one of which will be the lazy-initialized bean! So don't be confused if the IoC container creates one of the beans that you have explicitly configured as lazy-initialized at startup; all that means is that the lazy-initialized bean probably is being injected into a non-lazy-initialized singleton bean elsewhere in your configuration.
It is also possible to control lazy-initialization at the container level by using the 'default-lazy-init' attribute on the <beans/> element; to wit:
<beans default-lazy-init="true">
<!-- no beans will be eagerly pre-instantiated... -->
</beans>
Spring IoC컨테이너는 협력자 bean들 사이의 관계를 autowire 할수 있다. 이것은 BeanFactory의 내용을 조사함으로써 당신의 bean을 위해 Spring이 자동적으로 협력자(다른 bean)를 분석하는것이 가능하다는 것을 의미한다. autowiring 기능은 5개의 모드를 가진다. Autowiring은 다른 bean이 autowire되지 않는 동안 bean마다 명시되고 몇몇 bean을 위해 가능하게 될수 있다. autowiring을 사용하면 명백하게 많은 양의 타이핑을 줄이고 프라퍼티나 생성자의인자를 명시할 필요를 줄이거나 제거하는것이 가능하다. [2] XML-기반의 설정 메타데이터를 사용할때, bean정의를 위한 autowire모드는 <bean/> 요소의 autowire 속성을 사용하여 명시된다. 다음의 값이 허용된다.
Table 3.2. Autowiring 모드
모드 | 설명 |
---|---|
no | autowiring이 전혀없다. bean참조는 ref 요소를 통해 정의되어야만 한다. 이 값은 디폴트이고 좀더 큰 제어와 명백함을 주는 협력자를 명시하기 때문에 좀더 큰 배치를 위해 이것이 억제되도록 변경한다. 몇몇 확장을 위해 이것은 시스템의 구조에 대한 문서의 형태이다. |
byName | 프라퍼티 이름에 의한 Autowiring. 이 옵션은 컨테이너를 조사하고 autowire될 필요가 있는 프라퍼티와 같은 이름의 bean을 찾는다. 예를 들면 당신이 만약 이름에 의해 autowire하기 위해 셋팅하는 bean정의를 가지고 이것이 master 프라퍼티(이것은 setMaster(...) 메소드를 가진다.)를 포함한다면 Spring은 master라는 이름의 bean정의를 찾을것이고 프라퍼티를 셋팅하기 위해 이것을 사용한다. |
byType | 컨테이너내 프라퍼티 타입의 bean이 정확하게 하나 있다면 프라퍼티를 autowire가 되도록 허용한다. 만약 하나 이상이 있다면 치명적인 예외가 던져지고 이것은 bean을 위한 byType autowiring을 사용하지 않는것을 나타낸다. 만약 대응되는 bean이 없다면 아무것도 발생하지 않는다. 프라퍼티가 셋팅되지 않는다. 만약 이 기능을 바라지 않는다면 이 경우에 던져질 에러를 명시하는 dependency-check="objects" 속성값을 셋팅한다. |
constructor | 이것은 byType와 유사하지만 생성자의 인자에 적용한다. bean factory내 생성자의 인자타입의 bean이 정확하게 하나가 아닐경우 치명적인 에러가 발생한다. |
autodetect | bean클래스의 내성을 통해 constructor 나 byType를 선택하라. 만약 디폴트 생성자가 발견된다면 byType는 적용된다. |
property 와 constructor-arg 셋팅내 명시적인 의존성이 언제나 autowiring을 오버라이드한다는것에 주의하라. 소위 간단하다고 불리는 원시(primitives), Strings, 그리고Classes(그리고 이러한 간단한 프라퍼티의 배열.)(이것은 디자인되고 기능에 추가될것이다.)와 같은 프라퍼티를 자동으로 묶는(autowire)것이 현재 가능하지 않다는 것을 알라. Autowire 행위는 모든 autowiring가 완성된후 수행될 의존성 체크와 조합될수 있다.
다양한 장점과 autowiring의 단점을 이해하는 것은 중요하다. 몇가지 장점은 다음을 포함한다.
Autowiring은 요구되는 설정의 양을 명백하게 감소시킨다. 어쨌든 bean 템플릿(이 장 도처에서 언급된)의 사용과 같은 기법은 이점에서 가치있다.
Autowiring은 객체가 나타나는 것처럼 그것 자체를 최신으로 유지하는 설정을 야기한다. 예를 들면 만약 당신이 클래스에 추가적으로 의존성을 추가할 필요가 있다면 그 의존성은 설정을 변경할 필요없이 자동적으로 만족될수 있다. 게다가 배치하는 동안 코드기초가 좀더 안정화가 될때 명시적으로 wiring하기 위한 교체의 옵션이 없이 autowiring을 위해 견고한 경우가 된다.
autowiring 의 몇몇 단점
Autowiring은 명시적인 wiring보다는 좀더 마법과같다. 비록 위 테이블에서 언급된것처럼 Spring은 기대되지 않는 결과를 가지는 모호함과 같은 경우에 추측을 피하기 위해 주의한다. 당신의 Spring관리 객체들 간의 관계는 더 이상 명시적으로 문서화되지 않는다.
wiring정보는 아마도 Spring컨테이너로부터 문서를 생성하는 툴을 위해 사용가능하지는 않을것이다.
type에 의한 autowiring은 setter메소드나 생성자의 인자에 의해 명시되는 타입의 하나의 bean정의가 있을때만 작동할것이다. 당신은 어떠한 잠재적인 모호함이 있을경우 명시적인 wiring을 사용할 필요가 있다.
모든 경우에 "틀리다(wrong)" 나 "맞다(right)"가 답은 아니다. 우리는 프로젝트를 통한 일관성(consistency)의 정도(degree)를 추천한다. 예를 들면 autowiring이 대개 사용되지 않을때 이것은 개발자에게 하나또는 두개의 bean정의를 사용하는것에 혼동을 줄지도 모른다.
You can also (on a per bean basis) totally exclude a bean from being an autowire candidate. When configuring beans using Spring's XML format, the 'autowire-candidate' attribute of the <bean/> element can be set to 'false'; this has the effect of making the container totally exclude that specific bean definition from being available to the autowiring infrastructure.
This can be useful when you have a bean that you absolutely never ever want to have injected into other beans via autowiring. It does not mean that the excluded bean cannot itself be configured using autowiring... it can, it is rather that it itself will not be considered as a candidate for autowiring other beans.
Spring IoC컨테이너는 컨테이너로 배치되는 bean의 분석되지 않은 의존성의 존재를 체크하도록 시도하는 능력을 가진다. 그것들은 bean정의내 그것들을 위한 실제값을 셋팅하지 않거나 autowiring기능에 의해 자동적으로 제공되는 bean의 자바빈 프라퍼티이다.
이 기능은 모든 프라퍼티(또는 특정 타입의 모든 프라퍼티)가 bean에 셋팅되는지 확인하기를 원할때 때때로 유용하다. 물론 많은 경우에 bean클래스는 많은 프라퍼티 또는 모든 사용시나리오를 위해 적용하지 않는 몇몇 프라퍼티를 위한 디폴트 값을 가질것이다. 그래서 이 기능은 제한적으로 사용가능하다. 의존성체크는 autowiring기능처럼 bean단위로 사용가능하거나 사용불가능하다. 디폴트는 의존성을 체크하지 않는 것이다. 의존성체크는 다양한 모드로 다루어질수 있다. XmlBeanFactory에서 이것은 bean정의내 'dependency-check'속성을 통해 명시되고 다음의 값을 가진다.
Table 3.3. 의존성체크 모드
모드 | 설명 |
---|---|
none | 의존성 체크가 없다. 그것들을 위해 명시되는 값이 없는 bean의 프라퍼티가 간단하게 셋팅하지 않는다. |
simple | 원시타입과 collection(다른 beanㄷ처럼 협력자를 제외한 모든 것)을 위해 수행되는 의존성 체크. |
object | 협력자를 위해 수행되는 의존성 체크. |
all | 협력자, 원시타입 그리고 collection을 위해 수행되는 의존성 체크. |
Java 5를 사용하고 소스레벨의 어노테이션을 사용한다면, 관심을 가지고 Section 25.3.1, “@Required” 부분을 보게 될것이다.
대부분의 사용자를 위해 컨테이너내 대부분의 bean은 싱글톤일것이다. 싱글톤 bean이 다른 싱글톤 bean과 협력할 필요가 있거나 비-싱글톤 bean이 다른 비-싱글톤 bean과 협력할 필요가 있을때 다른 것의 프라퍼티가 되기 위한 하나의 bean을 명시하여 이 의존성을 다루는 전형적이고 공통적인 접근법은 꽤 충분하다. 어쨌든 bean생명주기가 다를때 문제가 있다. 비-싱글톤(프로토타입) bean B를 사용할 필요가 있는 싱글톤 bean A가 A의 각각의 메소드 호출을 한다고 해보자. 컨테이너는 싱글톤 bean A를 단지 한번만 생성할것이고 그것의 프러퍼티를 셋팅하기 위한 기회를 오직 한번만 가진다. 그것이 필요할때마다 bean B의 새로운 인스턴스를 가진 bean A를 제공하기 위한 컨테이너를 위한 기회는 없다.
이 문제를 해결하기 위한 하나의 해결법은 몇몇 Inversion of Control을 버리는 것이다. bean A는 BeanFactoryAware를 구현해서 컨테이너(여기에서 언급된것처럼)를 인식할수 있고 이것이 필요할때마다 (새로운) bean B를 위한 getBean("B") 호출을 통해 컨테이너에게 요청하기 위한 프로그램마다 다른 방법을 사용한다.
// a class that uses a stateful inner class to do some data processing package fiona.apple; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; public class CommandProcessor implements BeanFactoryAware { // instances of this class are stateful, and hence best deployed as prototypes public static final class ThreadedCommandHelper implements CommandHelper, Runnable { private Object command; public Object process(Object command) { this.command = command; new Thread(this).start(); // harvest and return result of processing... return ...; } public void run() { // do the processing of the command... } } private BeanFactory beanFactory; public Object process(Object command) { // grab a new instance of the appropriate CommandHelper return createHelper().process(command); } protected CommandHelper createHelper() { return (CommandHelper) this.beanFactory.getBean("command.helper"); } public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } }
이것은 bean코드가 인식을 하고 Spring에 커플링이 되기 때문에 대개 바람직한 해결법은 아니다. Spring IoC컨테이너의 향상된 기능인 메소드 삽입은 몇몇 다른 시나리오에 따라 깔끔한 형태로 다루어지는 사용 상황을 허용한다.
룩업 메소드 삽입은 컨테이너내 다른 명명된 bean를 룩업하는 결과를 반환하는 컨테이너내 관리빈의 메소드를 오버라이드하기 위해 컨테이너의 기능을 적용한다. 룩업은 위에서 언급(비록 싱글톤이 될수 있더라도)된 시나리오처럼 프로토타입(prototype) bean이 될것이다. Spring은 CGLIB 라이브러리를 통해 바이트코드 생성을 사용하여 동적으로 생성된 하위클래스가 메소드를 오버라이드하는것을 통해 이 삽입을 구현한다.
당신이 이전이 코드 조각(CommandProcessor 클래스)으로부터 코드를 본다면, 이것은 createHelper() 메소드의 구현물을 동적으로 제공하는 Spring컨테이너이다. 당신의 CommandProcessor 클래스는 아래에서 볼수 있는것처럼, Spring의존성을 가지지 않는다.
package fiona.apple; // no more Spring imports! public class CommandProcessor { // instances of this class are stateful, and hence best deployed as prototypes public static final class ThreadedCommandHelper implements CommandHelper, Runnable { private Object command; public Object process(Object command) { this.command = command; new Thread(this).start(); // harvest and return result of processing... return ...; } public void run() { // do the processing of the command... } } public Object process(Object command) { // grab a new instance of the appropriate CommandHelper return createHelper().process(command); } // mmm, but where is the implementation of this method? protected abstract CommandHelper createHelper(); }
(위 코드조각내 CommandProcessor)삽입된 메소드를 포함하는 클라이언트 클래스에서, '삽입된' 메소드는 다음 형태의 시그너처를 가져야만 한다.
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
만약 메소드가 추상적이라면, Spring이 생성한 하위클래스는 메소드를 구현할것이다. 그렇지 않다면, Spring이 생성한 하위클래스가 원래의 클래스내 정의된 구현된 메소드를 오버라이드할것이다. 예제를 보자.
<!-- a stateful bean deployed as a prototype (non-singleton) --> <bean id="statefulCommandHelper" class="fiona.apple.CommandProcessor$ThreadedCommandHelper" scope="prototype"/> <!-- commandProcessor uses statefulCommandHelper --> <bean id="commandProcessor" class="fiona.apple.CommandProcessor"> <lookup-method name="createHelper" bean="statefulCommandHelper"/> </bean>
commandProcessor로 확인된 bean은 statefulCommandHelper bean의 새로운 인스턴스가 필요할때마다 자체적인 createHelper메소드를 호출할것이다. bean을 디플로이하는 사람은 프로토타입으로 statefulCommandHelper bean을 디플로이하는 것을 주의해야만 한다는 것을 노트하는 것이 중요하다. 싱글톤으로 디플로이된다면(명시적이거나, 이 플래그에 디폴트로 true로 셋팅되는 것에 의존하여), statefulCommandHelper의 같은 인스턴스는 매번 반환될것이다.
룩업 메소드 삽입은 생성자와 setter삽입모두 조합될수 있다.
Please be aware that in order for this dynamic subclassing to work, you will need to have the CGLIB jar(s) on your classpath. Additionally, the class that the Spring container is going to subclass cannot be final, and the method that is being overridden cannot be final either. Also, testing a class that has an abstract method can be somewhat odd in that you will have to subclass the class yourself and supply a stub implementation of the abstract method. Finally, beans that have been the target of method injection cannot be serialized.
The interested reader may also find the ServiceLocatorFactoryBean (in the org.springframework.beans.factory.config package) to be of use... the approach is similar to that of the ObjectFactoryCreatingFactoryBean, but it allows you to specify your own lookup interface as opposed to having to use a Spring-specific lookup interface such as the ObjectFactory. Consult the (copious) Javadocs for the ServiceLocatorFactoryBean for a full treatment of this alternative approach (that does reduce the coupling to Spring).
룩업 메소드 삽입보다 메소드 삽입의 다소 덜 공통적으로 유용한 형태는 다른 메소드 구현물을 가진 관리빈내에 임의의 메소드를 교체하는 기능이다. 사용자는 이 기능이 실질적으로 필요할때까지 이 부분의 나머지(향상된 기능에 대해 언급하는)를 생략할수 있다.
XML-기반의 설정 메타데이터를 사용할때, replaced-method 요소는 배치된 bean위해 다른것을 가진 존재하는 메소드 구현물을 교체하기 위해 사용된다. 우리가 오버라이드하길 원하는 메소드 computeValue를 가진 다음의 클래스를 검토하라.
public class MyValueCalculator { public String computeValue(String input) { // some real code... } // some other methods... }
org.springframework.beans.factory.support.MethodReplacer인터페이스를 구현하는 클래스는 새로운 메소드 정의를 제공할 필요가 있다.
<!-- /** meant to be used to override the existing computeValue implementation in MyValueCalculator */ --> public class ReplacementComputeValue implements MethodReplacer { public Object reimplement(Object o, Method m, Object[] args) throws Throwable { <!-- // get the input value, work with it, and return a computed result --> String input = (String) args[0]; ... return ...; }
원래의 클래스를 배치하고 오버라이드할 메소드를 명시하기 위한 BeanFactory배치 정의는 다음처럼 보일것이다.
<bean id="myValueCalculator class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
replaced-method 요소내 하나 이상이 포함된 <arg-type/>요소는 오버라이드된 메소드의 메소드 시그너처를 표시하기 위해 사용된다. 인자를 위한 시그너처는 메소드가 실질적으로 오버로드되고 클래스내 몇가지 종류가 되는 경우에만 필요하다. 편의상 인자를 위한 문자열 타입은 완전한 형태의 타입명의 일부가 된다. 예를 들면 다음은 java.lang.String과 대응된다.
java.lang.String String Str
많은 수의 인자가 종종 각각의 가능한 선택들 사이에 구별하기 충분하기 때문에 이 간략화된 형태는 인자에 대응될 가장 짧은 문자열을 사용하여 많은 타입을 저장할수 있다.
When you create a bean definition (typically in an XML configuration file) what you are actually creating is (loosely speaking) a recipe or template for creating actual instances of the objects defined by that bean definition. The fact that a bean definition is a recipe is important, because it means that, just like a class, you can potentially have many object instances created from a single recipe.
You can control not only the various dependencies and configuration values that are to be plugged into an object that is created from a particular bean definition, but also the scope of the objects created from a particular bean definition. This approach is very powerful and gives you the flexibility to choose the scope of the objects you create through configuration instead of having to 'bake in' the scope of an object at the Java class level. Beans can be defined to be deployed in one of a number of scopes: out of the box, the Spring Framework supports exactly five scopes (of which three are available only if you are using a web-aware Spring ApplicationContext).
The scopes supported out of the box are listed below:
Table 3.4. Bean scopes
Scope | Description |
---|---|
Scopes a single bean definition to a single object instance per Spring IoC container. | |
Scopes a single bean definition to any number of object instances. | |
Scopes a single bean definition to the lifecycle of a single HTTP request; i.e. each and every HTTP request will have its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext. | |
Scopes a single bean definition to the lifecycle of a HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext. | |
Scopes a single bean definition to the lifecycle of a global HTTP Session. Typically only valid when used in a portlet context. Only valid in the context of a web-aware Spring ApplicationContext. |
When a bean is a singleton, only one shared instance of the bean will be managed and all requests for beans with an id or ids matching that bean definition will result in that one specific bean instance being returned by the Spring container.
To put it another way, when you define a bean definition and it is scoped as a singleton, then the Spring IoC container will create exactly one instance of the object defined by that bean definition (or recipe). This single instance will be stored in a singleton cache, and all subsequent requests and references for that named bean will result in the cached object instance being returned.
The following diagram illustrates the Spring singleton scope.
Please be aware that Spring's concept of a singleton bean is quite different from the Singleton pattern as defined in the seminal Gang of Four (GoF) patterns book. The classic GoF Singleton hardcodes the scope of an object such that one and only one instance of a particular class will ever be created per ClassLoader. The scope of the Spring singleton is best described as per container and per bean. This means that if you define one bean for a particular class in a single Spring container, then the Spring container will create one and only one instance of the class defined by that bean definition.
The singleton scope is the default scope in Spring. To define a bean as a singleton in XML, you would write configuration like so:
<bean id="accountService" class="com.foo.DefaultAccountService"/> <!-- the following is equivalent, though redundant (singleton scope is the default) --> <bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/> <!-- the following is equivalent, though redundant (and preserved for backward compatibility) --> <bean id="accountService" class="com.foo.DefaultAccountService" singleton="true"/>
The non-singleton, prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made (that is, it is injected into another bean or it is requested via a programmatic getBean() method call on the container). As a rule of thumb, you should use the prototype scope for all beans that are stateful, while the singleton scope should be used for stateless beans.
The following diagram illustrates the Spring prototype scope. Please note that a DAO would not typically be configured as a prototype, since a typical DAO would not hold any conversational state; it was just easier for this author to reuse the core of the singleton diagram.
To define a bean as a prototype in XML, you would write configuration like so:
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
<!-- the following is equivalent too (and preserved for backward compatibility) -->
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="false"/>
There is one quite important thing to be aware of when deploying a bean in the prototype scope, in that the lifecycle of the bean changes slightly. Spring cannot (and hence does not) manage the complete lifecycle of a prototype bean: the container instantiates, configures, decorates and otherwise assembles a prototype object, hands it to the client and then has no further knowledge of that prototype instance. This means that while initialization lifecycle callback methods will be (and are) called on all objects regardless of scope, in the case of prototypes, any configured destruction lifecycle callbacks will not be called. It is the responsibility of the client code to clean up prototype scoped objects and release any expensive resources that the prototype bean(s) are holding onto. (One possible way to get the Spring container to release resources used by singleton-scoped beans is through the use of a bean post processor which would hold a reference to the beans that need to be cleaned up.)
In some respects, you can think of the Spring container's role when talking about a prototype-scoped bean as somewhat of a replacement for the Java 'new' operator. Any lifecycle aspects past that point have to be handled by the client. The lifecycle of a bean in a Spring IoC container is further described in the section entitled Section 3.6.1, “Lifecycle 인터페이스”.
![]() | Backwards compatibility note: specifying the lifecycle scope in XML |
---|---|
If you are referencing the 'spring-beans.dtd' DTD in a bean definition file(s), and you are being explicit about the lifecycle scope of your bean(s) you must use the "singleton" attribute to express the lifecycle scope (remembering that the singleton lifecycle scope is the default). If you are referencing the 'spring-beans-2.0.dtd' DTD or the Spring 2.0 XSD schema, then you will need to use the "scope" attribute (because the "singleton" attribute was removed from the definition of the new DTD and XSD files in favour of the "scope" attribute). To be totally clear about this, this means that if you use the "singleton" attribute in an XML bean definition then you must be referencing the 'spring-beans.dtd' DTD in that file. If you are using the "scope" attribute then you must be referencing either the 'spring-beans-2.0.dtd' DTD or the 'spring-beans-2.0.xsd' XSD in that file. |
The other scopes, namely request, session, and global session are for use only in web-based applications (and can be used irrespective of which particular web application framework you are using, if indeed any). In the interest of keeping related concepts together in one place in the reference documentation, these scopes are described here.
![]() | Note |
---|---|
The scopes that are described in the following paragraphs are only available if you are using a web-aware Spring ApplicationContext implementation (such as XmlWebApplicationContext). If you try using these next scopes with regular Spring IoC containers such as the XmlBeanFactory or ClassPathXmlApplicationContext, you will get an IllegalStateException complaining about an unknown bean scope. |
In order to effect the scoping of beans at the request, session, and global session level (i.e. web-scoped beans), some minor initial configuration is required before you can set about defining your bean definitions. Please note that this extra setup is not required if you just want to use the 'standard' scopes; i.e. singleton and prototype.
Now as things stand, there are a couple of ways to effect this initial setup depending on your particular servlet environment. If you are using a Servlet 2.4+ web container, then you need only add the following ContextListener to the XML declarations in your web applications 'web.xml' file.
<web-app> ... <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> ... </web-app>
If you are using an older web container (before Servlet 2.4), you will need to use a (provided) javax.servlet.Filter implementation. Find below a snippet of XML configuration that has to be included in the 'web.xml' file of your web application if you want to have access to web-scoped beans (the filter settings depend on the surrounding web application configuration and so you will have to change them as appropriate).
<web-app> .. <filter> <filter-name>requestContextFilter</filter-name> <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> </filter> <filter-mapping> <filter-name>requestContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ... </web-app>
That's it. The RequestContextListener and RequestContextFilter classes both do exactly the same thing, namely bind the HTTP request object to the Thread that is servicing that request. This makes beans that are request- and session-scoped available further down the call chain.
Consider the following bean definition:
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
With the above bean definition in place, the Spring container will create a brand new instance of the LoginAction bean using the 'loginAction' bean definition for each and every HTTP request. That is, the 'loginAction' bean will be effectively scoped at the HTTP request level. You can change or dirty the internal state of the instance that is created as much as you want, safe in the knowledge that other requests that are also using instances created off the back of the same 'loginAction' bean definition will not be seeing these changes in state since they are particular to an individual request. When the request is finished processing, the bean that is scoped to the request will be discarded.
Consider the following bean definition:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
With the above bean definition in place, the Spring container will create a brand new instance of the UserPreferences bean using the 'userPreferences' bean definition for the lifetime of a single HTTP Session. In other words, the 'userPreferences' bean will be effectively scoped at the HTTP Session level. Just like request-scoped beans, you can change the internal state of the instance that is created as much as you want, safe in the knowledge that other HTTP Session instances that are also using instances created off the back of the same 'userPreferences' bean definition will not be seeing these changes in state since they are particular to an individual HTTP Session. When the HTTP Session is eventually discarded, the bean that is scoped to that particular HTTP Session will also be discarded.
Consider the following bean definition:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>
The global session scope is similar to the standard HTTP Session scope (described immediately above), and really only makes sense in the context of portlet-based web applications. The portlet specification defines the notion of a global Session that is shared amongst all of the various portlets that make up a single portlet web application. Beans defined at the global session scope are scoped (or bound) to the lifetime of the global portlet Session.
Please note that if you are writing a standard Servlet-based web application and you define one or more beans as having global session scope, the standard HTTP Session scope will be used, and no error will be raised.
Being able to define a bean scoped to a HTTP request or Session (or indeed a custom scope of your own devising) is all very well, but one of the main value-adds of the Spring IoC container is that it manages not only the instantiation of your objects (beans), but also the wiring up of collaborators (or dependencies). If you want to inject a bean that, for the sake of argument is scoped at the HTTP request scope, into another bean, you will need to inject an AOP proxy in place of the scoped bean. That is to say, you need to inject a proxy object that exposes the same public interface as the scoped object, but that is smart enough to be able to retrieve the real, target object from the relevant scope (for example a HTTP request) and delegate method calls onto the real object.
![]() | Note |
---|---|
You do not need to use the <aop:scoped-proxy/> in conjunction with beans that are scoped as singletons or prototypes. It is an error to try to create a scoped proxy for a singleton bean (and the resulting BeanCreationException will certainly set you straight in this regard). |
Let's look at the configuration that is required to effect this; the configuration is not hugely complex (it takes just one line), but it is important to understand the “why” as well as the “how” behind it.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <!-- a HTTP Session-scoped bean exposed as a proxy --> <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"> <!-- this next element effects the proxying of the surrounding bean --> <aop:scoped-proxy/> </bean> <!-- a singleton-scoped bean injected with a proxy to the above bean --> <bean id="userService" class="com.foo.SimpleUserService"> <!-- a reference to the proxied 'userPreferences' bean --> <property name="userPreferences" ref="userPreferences"/> </bean> </beans>
To create a proxy to a scoped bean using XML-based configuration, you need only to insert a child <aop:scoped-proxy/> element into a scoped bean definition (you may also need the CGLIB library on your classpath so that the container can effect class-based proxying; you will also need to be using XSD based configuration). The above XML configuration demonstrated the “how”; now for the “why”. So, just why do you need this <aop:scoped-proxy/> element in the definition of beans scoped at the request, session, and globalSession level? The reason is best explained by picking apart the following bean definition (please note that the following 'userPreferences' bean definition as it stands is incomplete):
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/> <bean id="userManager" class="com.foo.UserManager"> <property name="userPreferences" ref="userPreferences"/> </bean>
From the above configuration it is evident that the singleton bean 'userManager' is being injected with a reference to the HTTP Session-scoped bean 'userPreferences'. The salient point here is that the 'userManager' bean is a singleton... it will be instantiated exactly once per container, and its dependencies (in this case only one, the 'userPreferences' bean) will also only be injected once. This means that the 'userManager' will (conceptually) only ever operate on the exact same 'userPreferences' object, i.e. the one that it was originally injected with. This is not what you want when you inject a HTTP Session-scoped bean as a dependency into a collaborating object. What we do want is a single 'userManager' object, and then, for the lifetime of a HTTP Session, we want to see and use a 'userPreferences' object that is specific to said HTTP Session.
Rather what you need then is to inject some sort of object that exposes the exact same public interface as the UserPreferences class (ideally an object that is a UserPreferences instance) and that is smart enough to be able to go off and fetch the real UserPreferences object from whatever underlying scoping mechanism we have chosen (HTTP request, Session, etc.). We can then safely inject this proxy object into the 'userManager' bean, which will be blissfully unaware that the UserPreferences reference that it is holding onto is a proxy. In the case of this example, when a UserManager instance invokes a method on the dependency-injected UserPreferences object, it is really invoking a method on the proxy... the proxy will then go off and fetch the real UserPreferences object from (in this case) the HTTP Session, and delegate the method invocation onto the retrieved real UserPreferences object.
That is why you need the following, correct and complete, configuration when injecting request-, session-, and globalSession-scoped beans into collaborating objects:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
As of Spring 2.0, the bean scoping mechanism in Spring is extensible. This means that you are not limited to just the bean scopes that Spring provides out of the box; you can define your own scopes, or even redefine the existing scopes (although that last one would probably be considered bad practice - please note that you cannot override the built-in singleton and prototype scopes).
Scopes are defined by the org.springframework.beans.factory.config.Scope interface. This is the interface that you will need to implement in order to integrate your own custom scope(s) into the Spring container. The interface itself is quite simple, with two methods to get and remove an object from/to an underlying storage mechanism respectively. Possible custom scopes are beyond the scope of this reference manual. You may wish to look at the Scope implementations that are supplied with Spring for an idea of how to go about implementing your own.
The remainder of this section details how, after you have written and tested one or more custom Scope implementations, you then go about making the Spring container aware of your new scope. The central method to register a new Scope with the Spring container is declared on the ConfigurableBeanFactory interface (implemented by most of the concrete BeanFactory implementations that ship with Spring); this central method is displayed below:
void registerScope(String scopeName, Scope scope);
The first argument to the registerScope(..) method is the unique name associated with a scope; examples of such names in the Spring container itself are 'singleton' and 'prototype'. The second argument to the registerScope(..) method is an actual instance of the custom Scope implementation that you wish to register and use.
Let's assume that you have written your own custom Scope implementation, and you have registered it like so:
// note: the ThreadScope class does not exist; I made it up for the sake of this example Scope customScope = new ThreadScope(); beanFactory.registerScope("thread", scope);
You can then create bean definitions that adhere to the scoping rules of your custom Scope like so:
<bean id="..." class="..." scope="thread"/>
If you have your own custom Scope implementation(s), you are not just limited to only programmatic registration of said custom scope(s). You can also do the Scope registration declaratively, using a custom BeanFactoryPostProcessor implementation, the CustomScopeConfigurer class. The BeanFactoryPostProcessor interface is one of the primary means of extending the Spring IoC container, and is described in a later section of this very chapter.
The declarative registration of custom Scope implementations using the CustomScopeConfigurer class is shown below:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="thread" value="com.foo.ThreadScope"/> </map> </property> </bean> <bean id="bar" class="x.y.Bar" scope="thread"> <property name="name" value="Rick"/> <aop:scoped-proxy/> </bean> <bean id="foo" class="x.y.Foo"> <property name="bar" ref="bar"/> </bean> </beans>
The CustomScopeConfigurer also allows you to specify actual Class instances as entry values, as well as actual Scope implementation instances; see the Javadocs for the CustomScopeConfigurer class for details.
Spring은 컨테이너내 당신의 bean 행위를 변경하기 위한 다양한 표시자(marker)인터페이스를 제공한다. 그것들은 InitializingBean 과 DisposableBean을 포함한다. 이 인터페이스를 구현하는 것은 bean에게 초기화와 파괴화(destruction)의 작업을 수행하도록 허용하는 전자를 위한 afterPropertiesSet()을 후자를 위해 destroy()를 호출함으로써 컨테이너내 결과를 생성한다.
내부적으로 Spring은 이것이 적당한 메소드를 찾고 호출할수 있는 어떠한 표시자(marker) 인터페이스를 처리하기 위해 BeanPostProcessors를 사용한다. 만약 Spring이 특별히 제공하지 않는 사용자 지정 기능이나 다른 생명주기 행위가 필요하다면 당신은 BeanPostProcessor를 구현할수 있다. 이것에 대한 좀더 상세한 정보는 Section 3.8, “컨테이너 확장 지점”에서 찾을수 있다.
모든 다른 종류의 생명주기 표시자(marker)인터페이스는 아래에서 언급된다. 추가물중 하나에서 당신은 Spring이 bean을 어떻게 관리하고 그러한 생명주기 기능들이 당신의 bean의 성질을 어떻게 변경하고 그들이 어떻게 관리되는지 보여주는 다이어그램을 찾을수 있다.
org.springframework.beans.factory.InitializingBean을 구현하는것은 bean의 필요한 모든 프라퍼티가 컨테이너에 의해 셋팅된 후 bean에게 초기화작업을 수행하는것을 허용한다. InitializingBean인터페이스는 정확하게 하나의 메소드만 명시한다.
void afterPropertiesSet() throws Exception;
대개 InitializingBean 인터페이스의 사용은 제거될수 있다. (그리고 Spring에 코드를 불필요하게 결합한 후 억제된다.). bean정의는 명시되는 일반적인 초기화 메소드를 위한 지원을 제공한다. XML-기반의 설정 메타데이터의 경우, 이것은 'init-method' 속성을 통해 수행된다. 예를 들면, 다음의 정의처럼.
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}
Is exactly the same as...
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
하지만 Spring에 코드를 결합하지는 않는다.
org.springframework.beans.factory.DisposableBean인터페이스를 구현하는것은 컨테이너가 파괴된(destroyed)것을 포함할때 bean에게 콜백을 얻는 것을 허용한다. DisposableBean인터페이스는 하나의 메소드를 명시한다.
void destroy() throws Exception;
DisposableBean 표시자(marker) 인터페이스의 사용은 제거될수 있다. (그리고 Spring에 코드를 불필요하게 결합한 후 억제된다.). bean정의는 명시되기 위한 일반적인 파괴(destroy) 메소드를 위한 지원을 제공한다. XML-기반의 설정 메타데이터의 경우에, <bean/>의 destroy-method 속성을 통해 수행된다. 예를 들면, 다음의 정의처럼.
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
Is exactly the same as...
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
하지만 Spring에 코드를 결합하지는 않는다.
When you are writing initialization and destroy method callbacks that do not use the Spring-specific InitializingBean and DisposableBean callback interfaces, one (in the experience of this author) typically finds oneself writing methods with names such as init(), initialize(), dispose(), etc. The names of such lifecycle callback methods are (hopefully!) standardized across a project so that developers on a team all use the same method names and thus ensure some level of consistency.
The Spring container can now be configured to 'look' for named initialization and destroy callback method names on every bean. This means that you as an application developer can simply write your application classes, use a convention of having an initialization callback called init(), and then (without having to configure each and every bean with, in the case of XML-based configuration, an 'init-method="init"' attribute) be safe in the knowledge that the Spring IoC container will call that method when the bean is being created (and in accordance with the standard lifecycle callback contract described previously).
Let's look at an example to make the use of this feature completely clear. For the sake of the example, let us say that one of the coding conventions on a project is that all initialization callback methods are to be named init() and that destroy callback methods are to be called destroy(). This leads to classes like so...
public class DefaultBlogService implements BlogService {
private BlogDao blogDao;
public void setBlogDao(BlogDao blogDao) {
this.blogDao = blogDao;
}
// this is (unsurprisingly) the initialization callback method
public void init() {
if (this.blogDao == null) {
throw new IllegalStateException("The [blogDao] property must be set.");
}
}
}
The attendant XML configuration for the above class, and making use of the by-convention initialization callback method configuration, would look like so:
<beans default-init-method="init">
<bean id="blogService" class="com.foo.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
Notice the use of the 'default-init-method' attribute on the top-level <beans/> element. The presence of this attribute means that the Spring IoC container will recognize a method called 'init' on beans as being the initialization method callback, and when a bean is being created and assembled, if the bean's class has such a method, it will be invoked at the appropriate time.
Destroy method callbacks are configured similarly (in XML that is) using the 'default-destroy-method' attribute on the top-level <beans/> element.
The use of this feature can save you the (small) housekeeping chore of specifying an initialization and destroy method callback on each and every bean, and it is great for enforcing a consistent naming convention for initialization and destroy method callbacks (and consistency is something that should always be aimed for).
One final word... let's say you want to use this feature, but you have some existing beans where the underlying classes already have for example initialization callback methods that are named at variance with the convention. You can always override the default by specifying (in XML that is) the method name using the 'init-method' and 'destroy-method' attributes on the <bean/> element itself.
![]() | Note |
---|---|
This next section does not apply to web applications (in case the title of this section did not make that abundantly clear). Spring's web-based ApplicationContext implementations already have code in place to handle shutting down the Spring IoC container gracefully when the relevant web application is being shutdown. |
If you are using Spring's IoC container in a non-web application environment, for example in a rich client desktop environment, and you want the container to shutdown gracefully and call the relevant destroy callbacks on your singleton beans, you will need to register a shutdown hook with the JVM. This is quite easy to do (see below), and will ensure that your Spring IoC container shuts down gracefully and that all resources held by your singletons are released (of course it is still up to you to both configure the destroy callbacks for your singletons and implement such destroy callbacks correctly).
So to register a shutdown hook that enables the graceful shutdown of the relevant Spring IoC container, you simply need to call the registerShutdownHook() method that is declared on the AbstractApplicationContext class. To wit...
import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public final class Boot { public static void main(final String[] args) throws Exception { AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(new String []{"beans.xml"}); // add a shutdown hook for the above context... ctx.registerShutdownHook(); // app runs here... // main method exits, hook is called prior to the app shutting down... } }
org.springframework.beans.factory.BeanFactoryAware인터페이스를 구현하는 클래스는 BeanFactory에 의해 생성되었을때 이것을 생성하는 BeanFactory에 대한 참조를 제공한다.
public interface BeanFactoryAware { void setBeanFactory(BeanFactory beanFactory) throws BeansException; }
이것은 bean에게 BeanFactory인터페이스를 통하거나 추가적인 기능을 드러내는 이것의 알려진 하위클래스에 대한 참조를 형변환함으로써 프로그램마다 다르게 그것들을 생성한 BeanFactory를 변경하는것을 허용한다. 원래 이것은 다른 bean의 프로그램마다 다른 검색으로 구성된다. 이 기능이 유용할때 이것이 Spring에 코드를 결합하고 Inversion of Control스타일을 따르지 않는 이 후 프라퍼티처럼 bean에 제공되는 협력자가 위치한 곳에 이것이 대개 제거될수 있다.
BeanFactoryAware 기반 접근법에 동등하게 영향을 끼치는 대체가능한 선택사항은 org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean를 사용하는 것이다.(이것은 여전히 Spring에 대한 커플링을 제거하기 않는다. 하지만 BeanFactoryAware기반 접근법만큼 IoC의 핵심개념에는 위배되지 않는다는 것을 기억하라.)
ObjectFactoryCreatingFactoryBean은 순서대로 bean룩업에 영향을 끼치기 위해 사용될수 있는 객체(factory)에 대한 참조를 반환하는 FactoryBean 구현물이다. ObjectFactoryCreatingFactoryBean 클래스는 자체적으로 실질적으로 삽입되는 클라이언트 bean이 ObjectFactory 인터페이스의 인스턴스인 BeanFactoryAware인터페이스를 구현한다. 이것은 Spring의 종속적인 인터페이스(그리고 나아가 Spring으로부터 완전히 디커플링되지는 않는)이지만, 클라이언트는 bean룩업에 영향을 주는 ObjectFactory의 getObject()메소드를 사용할수 있다(반환되는 ObjectFactory구현물아래에서 이름(name)으로 bean을 실질적을 룩업하는 BeanFactory로 위임한다.). 애플리케이션 개발자가 할 필요가 있는것은 룩업되는 bean의 이름을 가지는 ObjectFactoryCreatingFactoryBean를 제공하는 것이다. 예제를 보자.
package x.y; public class NewsFeed { private String news; public void setNews(String news) { this.news = news; } public String getNews() { return this.toString() + ": '" + news + "'"; } }
package x.y; import org.springframework.beans.factory.ObjectFactory; public class NewsFeedManager { private ObjectFactory factory; public void setFactory(ObjectFactory factory) { this.factory = factory; } public void printNews() { // here is where the lookup is performed; note that there is no // need to hardcode the name of the bean that is being looked up... NewsFeed news = (NewsFeed) factory.getObject(); System.out.println(news.getNews()); } }
ObjectFactoryCreatingFactoryBean 접근법을 사용하여 위 클래스를 묶는 XML설정을 아래에서 보자.
<beans> <bean id="newsFeedManager" class="x.y.NewsFeedManager"> <property name="factory"> <bean class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName"> <idref local="newsFeed" /> </property> </bean> </property> </bean> <bean id="newsFeed" class="x.y.NewsFeed" scope="prototype"> <property name="news" value="... that's fit to print!" /> </bean> </beans>
그리고 이것은 newsFeed bean의 새로운 인스턴스(prototype)가 NewsFeedManager의 printNews() 메소드내부 삽입된 ObjectFactory에 대한 각각의 호출을 위해 실질적으로 반환되는 것을 테스트하기 위한 작은 프로그램이다.
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import x.y.NewsFeedManager; public class Main { public static void main(String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); NewsFeedManager manager = (NewsFeedManager) ctx.getBean("newsFeedManager"); manager.printNews(); manager.printNews(); } }
위 실행중인 프로그램으로부터의 결과는 다음과 같은 것을 보여줄것이다.(물론 결과는 당신의 머신에 따라 다양할것이다.)
x.y.NewsFeed@1292d26: '... that's fit to print!' x.y.NewsFeed@5329c5: '... that's fit to print!'
bean정의는 잠재적으로 컨테이너 특정 정보(이를 테면, 초기화 메소드, 정적 factory 메소드명, 등등)와 생성자의 인자와 프라퍼티 값을 포함하는 많은 양의 설정정보를 포함한다. 자식 bean정의는 부모 정의로부터 설정정보를 상속하는 bean정의이다. 이것은 필요하다면 몇몇값을 오버라이드하거나 다른것을 추가할수 있다. 부모와 자식 bean정의를 사용하는것은 잠재적으로 많은 양의 타이핑을 줄일수 있다. 효과적으로 이것은 템플릿형태이다.
프로그램마다 다르게 BeanFactory로 작업을 수행할때 자식 bean정의는 ChildBeanDefinition 클래스에 의해 표현된다. 대부분의 사용자는 XmlBeanFactory와 같은 몇가지내에서 선언적인 설정 bean정의대신에 이 수준에서 그것들과 함께 작동하지는 않을것이다. XML-기반의 설정 메타데이터를 사용할때, bean정의에서 자식 bean정의는 이 속성의 값처럼 부모 bean을 명시하는 'parent' 속성을 사용하여 간단하게 표시될수 있다.
<bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean> <bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBean" init-method="initialize"> <property name="name" value="override"/> <!-- the age property value of 1 will be inherited from parent --> </bean>
자식 bean정의는 아무것도 명시가 되어 있지 않지만 이것을 오버라이드할수 있다면 부모 정의의 bean클래스를 사용할것이다. 후자의 경우 자식 bean클래스는 부모 bean클래스와 호환되어야만 한다. 이를 테면 부모의 프라퍼티 값을 받을수 있어야 한다.
자식 bean정의는 생성자의 인자값, 프라퍼티값과 부모로 부터 상속된 메소드를 새로운 값을 추가하는 선택사항과 함께 상속할것이다. 만약 init, destroy메소드와/또는 정적 factory메소드가 명시된다면 그것들은 관련된 부모 셋팅을 오버라이드할것이다.
남은 셋팅들은 언제나 자식 정의로부터 가져올것이다.: depends on, autowire 모드, 의존성 체크, 싱글톤, scope, lazy init.
위 예제에서 우리는 abstract 속성을 사용하여 추상적으로 부모 bean정의를 명시적으로 표시했다는 것을 알라. 이 경우 부모 정의는 클래스를 명시하지 않는다.
<bean id="inheritedTestBeanWithoutClass" abstract="true">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBeanWithoutClass" init-method="initialize">
<property name="name" value="override"/>
<!-- age will inherit the value of 1 from the parent bean definition-->
</bean>
부모 bean은 불완전하고 또한 추상적이라고 생각된 이후에는 인스턴스화될수 없다. 정의가 이것처럼(명시적이거나 함축적인) 추상적이라고 생각될 때 이것은 자식 정의를 위한 부모 정의처럼 제공될 순수한 템플릿이나 추상 bean정의처럼 사용가능하다. 그것 자체(다른 bean의 ref프라퍼티를 참조하거나 부모 bean id를 가진 명시적인 getBean()호출을 하여)의 추상적인 부모 bean들을 사용하는것을 시도하면 에러를 보게될것이다. 유사하게도 컨테이너의 내부적인 preInstantiateSingletons() 메소드는 추상적이라고 생각되는 bean정의를 완벽하게 무시할것이다.
ApplicationContexts(BeanFactories 아닌)는 디폴트에 의해 모든 싱글톤으로 미리 인스턴스화될것이다. 그러므로 이것은 만약 당신이 템플릿처럼만 오직 사용되는 경향이 있는 (부모) bean정의를 가지고 이 정의가 클래스를 명시한다면 당신은 'abstract'속성값을 true로 셋팅해야만 하는 반면에 애플리케이션 컨텍스트는 이것을 실질적으로 미리 인스턴스화할것이라는 것은 중요(적어도 싱글톤 bean을 위해서)하다.
Spring 프레임워크의 IoC컴포넌트는 확장을 위해 디자인되었다. 애플리케이션 개발자를 위해 다양한 BeanFactory 나 ApplicationContext 구현 클래스의 하위클래스를 만들필요가 없다. Spring IoC컨테이너는 특별한 통합 인터페이스의 구현물에 삽입하여 크게 확장될수 있다. 다음의 몇가지 부분은 이러한 다양한 통합 인터페이스를 상세하게 다룬다.
우리가 볼 첫번째 확장지점은 BeanPostProcessor 인터페이스이다. The first extension point that we will look at is the BeanPostProcessor interface. This interface defines a number of callback methods that you as an application developer can implement in order to provide your own (or override the containers default) instantiation logic, dependency-resolution logic, and so forth. If you want to do some custom logic after the Spring container has finished instantiating, configuring and otherwise initializing a bean, you can plug in one or more BeanPostProcessor implementations.
You can configure multiple BeanPostProcessors if you wish. You can control the order in which these BeanPostProcessors execute by setting the 'order' property (you can only set this property if the BeanPostProcessor implements the Ordered interface; if you write your own BeanPostProcessor you should consider implementing the Ordered interface too); consult the Javadocs for the BeanPostProcessor and Ordered interfaces for more details.
![]() | Note |
---|---|
BeanPostProcessors operate on bean (or object) instances; that is to say, the Spring IoC container will have instantiated a bean instance for you, and then BeanPostProcessors get a chance to do their stuff. If you want to change the actual bean definition (i.e. the recipe that defines the bean), then you rather need to use a BeanFactoryPostProcessor (described below in the section entitled Section 3.8.2, “Customizing configuration metadata with BeanFactoryPostProcessors”. Also, BeanPostProcessors are scoped per-container. This is only relevant if you are using container hierarchies. If you define a BeanPostProcessor in one container, it will only do its stuff on the beans in that container. Beans that are defined in another container will not be post-processed by BeanPostProcessors in another container, even if both containers are part of the same hierarchy. |
The org.springframework.beans.factory.config.BeanPostProcessor interface consists of exactly two callback methods. When such a class is registered as a post-processor with the container (see below for how this registration is effected), for each bean instance that is created by the container, the post-processor will get a callback from the container both before any container initialization methods (such as afterPropertiesSet and any declared init method) are called, and also afterwards. The post-processor is free to do what it wishes with the bean instance, including ignoring the callback completely. A bean post-processor will typically check for marker interfaces, or do something such as wrap a bean with a proxy; some of the Spring AOP infrastructure classes are implemented as bean post-processors and they do this proxy-wrapping logic.
It is important to know that a BeanFactory treats bean post-processors slightly differently than an ApplicationContext. An ApplicationContext will automatically detect any beans which are defined in the configuration metadata which is supplied to it that implement the BeanPostProcessor interface, and register them as post-processors, to be then called appropriately by the container on bean creation. Nothing else needs to be done other than deploying the post-processor in a similar fashion to any other bean. On the other hand, when using a BeanFactory implementation, bean post-processors explicitly have to be registered, with code like this:
ConfigurableBeanFactory factory = new XmlBeanFactory(...); // now register any needed BeanPostProcessor instances MyBeanPostProcessor postProcessor = new MyBeanPostProcessor(); factory.addBeanPostProcessor(postProcessor); // now start using the factory
This explicit registration step is not convenient, and this is one of the reasons why the various ApplicationContext implementations are preferred above plain BeanFactory implementations in the vast majority of Spring-backed applications, especially when using BeanPostProcessors.
![]() | Note |
---|---|
You typically don't want to have BeanPostProcessors marked as being lazily-initialized. If they are marked as such, then the Spring container will never instantiate them, and thus they won't get a chance to apply their custom logic. If you are using the 'default-lazy-init' attribute on the declaration of your <beans/> element, be sure to mark your various BeanPostProcessor bean definitions with 'lazy-init="false"'. |
Find below some examples of how to write, register, and use BeanPostProcessors in the context of an ApplicationContext.
This first example is hardly compelling, but serves to illustrate basic usage. All we are going to do is code a custom BeanPostProcessor implementation that simply invokes the toString() method of each bean as it is created by the container and prints the resulting string to the system console. Yes, it is not hugely useful, but serves to get the basic concepts across before we move into the second example which is actually useful.
Find below the custom BeanPostProcessor implementation class definition:
package scripting; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.BeansException; public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor { // simply return the instantiated bean as-is public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; // we could potentially return any object reference here... } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("Bean '" + beanName + "' created : " + bean.toString()); return bean; } }
Here is the attendant XML-based configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">
<lang:groovy id="messenger"
script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
</lang:groovy>
<!--
when the above bean ('messenger') is instantiated, this custom
BeanPostProcessor implementation will output the fact to the system console
-->
<bean class="scripting.InstantiationTracingBeanPostProcessor"/>
</beans>
Notice how the InstantiationTracingBeanPostProcessor is simply defined; it doesn't even have a name, and because it is a bean it can be dependency injected just like any other bean. (The above configuration also just so happens to define a bean that is backed by a Groovy script. The Spring 2.0 dynamic language support is detailed in the chapter entitled Chapter 24, 동적 언어 지원.)
Find below a small driver script to exercise the above code and configuration;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.scripting.Messenger; public final class Boot { public static void main(final String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml"); Messenger messenger = (Messenger) ctx.getBean("messenger"); System.out.println(messenger); } }
The output of executing the above program will be (something like) this:
Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961 org.springframework.scripting.groovy.GroovyMessenger@272961
Using marker interfaces or annotations in conjunction with a custom BeanPostProcessor implementation is a common means of extending the Spring IoC container. This next example is a bit of a cop-out, in that you are directed to the section entitled Section 25.3.1, “@Required” which demonstrates the usage of a custom BeanPostProcessor implementation that that ships with the Spring distribution which ensures that JavaBean properties on beans that are marked with an (arbitrary) annotation are actually (configured to be) dependency-injected with a value.
The next extension point that we will look at is the org.springframework.beans.factory.config.BeanFactoryPostProcessor. This semantics of this interface are similar to the BeanPostProcessor, with one major difference. BeanFactoryPostProcessors operate on bean definitions (i.e. the configuration metadata that is supplied to a container); that is to say, the Spring IoC container will allow BeanFactoryPostProcessors to read the configuration metadata and potentially change it before the container has actually instantied any other beans.
You can configure multiple BeanFactoryPostProcessors if you wish. You can control the order in which these BeanFactoryPostProcessors execute by setting the 'order' property (you can only set this property if the BeanFactoryPostProcessor implements the Ordered interface; if you write your own BeanFactoryPostProcessor you should consider implementing the Ordered interface too); consult the Javadocs for the BeanFactoryPostProcessor and Ordered interfaces for more details.
![]() | Note |
---|---|
If you want to change the actual bean instances (i.e. the objects that are created from the configuration metadata), then you rather need to use a BeanPostProcessor (described above in the section entitled Section 3.8.1, “BeanPostProcessors로 bean 커스터마이징하기”. Also, BeanFactoryPostProcessors are scoped per-container. This is only relevant if you are using container hierarchies. If you define a BeanFactoryPostProcessor in one container, it will only do its stuff on the bean definitions in that container. Bean definitions in another container will not be post-processed by BeanFactoryPostProcessors in another container, even if both containers are part of the same hierarchy. |
A bean factory post-processor is executed manually (in the case of a BeanFactory) or automatically (in the case of a ApplicationContext) to apply changes of some sort to the configuration metadata that defines a container. Spring includes a number of pre-existing bean factory post-processors, such as PropertyResourceConfigurer and PropertyPlaceHolderConfigurer, both described below, and BeanNameAutoProxyCreator, which is very useful for wrapping other beans transactionally or with any other kind of proxy, as described later in this manual. The BeanFactoryPostProcessor can be used to add custom property editors.
In a BeanFactory, the process of applying a BeanFactoryPostProcessor is manual, and will be similar to this:
XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml")); // bring in some property values from a Properties file PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); cfg.setLocation(new FileSystemResource("jdbc.properties")); // now actually do the replacement cfg.postProcessBeanFactory(factory);
This explicit registration step is not convenient, and this is one of the reasons why the various ApplicationContext implementations are preferred above plain BeanFactory implementations in the vast majority of Spring-backed applications, especially when using BeanFactoryPostProcessors.
An ApplicationContext will detect any beans which are deployed into it which implement the BeanFactoryPostProcessor interface, and automatically use them as bean factory post-processors, at the appropriate time. Nothing else needs to be done other than deploying these post-processor in a similar fashion to any other bean.
![]() | Note |
---|---|
Just as in the case of BeanPostProcessors, you typically don't want to have BeanFactoryPostProcessors marked as being lazily-initialized. If they are marked as such, then the Spring container will never instantiate them, and thus they won't get a chance to apply their custom logic. If you are using the 'default-lazy-init' attribute on the declaration of your <beans/> element, be sure to mark your various BeanFactoryPostProcessor bean definitions with 'lazy-init="false"'. |
bean factory 후-처리자처럼 구현된 PropertyPlaceholderConfigurer는 BeanFactory정의로부터 표준적인 자바 Properties 형태의 다른 분리된 파일로 몇몇 프라퍼티값들을 구체화하기 위해 사용된다. 이것은 몇몇 key 프라퍼티(예를 들면 데이터베이스 URL, 사용자명, 비밀번호)를 복잡하거나 핵심이 되는 XML정의파일이나 컨테이너를 위한 파일을 변경하는 위험없이 커스터마이징하기 위한 애플리케이션을 배치하는것을 사람에게 허용하는데 유용하다.
위치유지자(placeholder)값과 함께 DataSource가 정의된 다음의 XML-기반의 설정 메타데이터 일부를 검토하라. 우리는 외부 Properties파일로부터 몇몇 프라퍼티를 설정할것이다. 실행시 우리는 데이터소스의 몇몇 프라퍼티를 대체할 메타데이터를 위한 PropertyPlaceholderConfigurer을 적용할것이다.
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="jdbc.username"/> <property name="password" value="${jdbc.password}"/> </bean>
실질적인 값들은 표준적인 Java Properties형태로 다른 파일로부터 나온다.
jdbc.driverClassName=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb:hsql://production:9002 jdbc.username=sa jdbc.password=root
PropertyPlaceHolderConfigurer는 명시하는 Properties 파일내 프라퍼티를 찾지 않는다. 하지만 사용하고자 하는 프라퍼티를 찾지 못한다면 Java System 프라퍼티를 체크한다. 이 행위는 설정자의 systemPropertiesMode 프라퍼티를 셋팅하여 사용자정의 될수 있다. 이것은 3개의 값을 가진다. 하나는 언제나 오버라이드하는 설정자를 말하고, 하나는 결코 오버라이드하지 않도록하고, 하나는 프라퍼티가 명시된 프라퍼티 파일내에서 찾을수 없을때만 오버라이드하도록 한다. 좀더 많은 정보를 위해서는 PropertiesPlaceholderConfigurer를 위한 Javadoc을 보라.
PropertyOverrideConfigurer, 다른 bean factory 후-처리자는 PropertyPlaceholderConfigurer와 비슷하지만 후자와는 대조적으로 원래의 정의는 디폴트 값을 가지거나 bean프라퍼티를 위한 값을 가질수 없다. 만약 오버라이딩된 Properties 파일이 어떤 bean프라퍼티를 위한 항목을 가지지 않는다면 디폴트 컨텍스트 정의가 사용된다.
bean factory정의는 오버라이드된것을 인식하지 않는다. 그래서 이것은 사용될 설정자를 오버라이드한 XML정의 파일을 찾을 때 즉시 명확하지 않다는것에 주의하라. 다중 PropertyOverrideConfigurer가 같은 bean프라퍼티를 위해 다른 값을 정의하는 경우에 가장 마지막의 값이 사용될것이다.(오버라이드기법에 따라.)
프라퍼티 파일 설정 라인은 다음과 같은 형태로 될것이다.
beanName.property=value
예제 프라퍼티 파일은 다음처럼 보일것이다.
dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb
예제 파일은 driver 와 url 프라퍼티를 가진 dataSource라고 불리는 것안에서 bean을 포함하는 BeanFactory정의에 대해 사용가능할것이다.
마지막 프라퍼티를 제외한 경로의 모든 컴포넌트가 이미 null이 아닌 만큼 복합 프라퍼티명또한 지원된다(추측컨데 생성자에 의해 초기화된). 이 예제에서:
foo.fred.bob.sammy=123
foo bean의 fred프라퍼티의 bob프라퍼티의 sammy프라퍼티는 123으로 셋팅되어 있다..
org.springframework.beans.factory.FactoryBean인터페이스는 자체적으로 factory인 객체에 의해 구현되는 것이다.
FactoryBean 인터페이스는 Spring IoC 컨테이너 인스턴스화 로직에 대한 접속가능한 지점이다. 많은 XML에 반대로 Java로 좀더 잘 표현되는 몇가지 복잡한 초기화 코드를 가진다면, 당신은 클래스내부에서 복잡한 초기화를 작성하고 컨테이너로 사용자정의 FactoryBean를 삽입하는 자체적인 FactoryBean을 생성할수 있다.
FactoryBean 인터페이스는 3개의 메소드를 제공한다.
Object getObject(): 이 factory가 생성하는 객체의 인스턴스를 반환한다. 인스턴스는 공유될수(이 factory가 싱글톤이나 프로토타입을 반환하는지에 대한 여부에 의존하여) 있다.
boolean isSingleton(): 만약 이 FactoryBean이 싱글톤을 반환한다면 true를 반환하고 다른경우라면 false를 반환한다.
Class getObjectType(): getObject() 메소드에 의해 반환되는 객체 타입이나 타입이 미리 알려지지 않았다면 null을 반환한다.
FactoryBean 개념과 인터페이스는 Spring프레임워크내 많은 부분에서 사용된다. 이 글이 작성되는 시점에 Spring자체에 포함된 FactoryBean 인터페이스의 구현물이 50개 이상이 있다.
마지막으로, 때때로 bean이 아닌 실제 FactoryBean 인스턴스 자체를 위한 컨테이너를 요청할 필요가 있다. BeanFactory(ApplicationContext를 포함해서)의 getBean 메소드를 호출할때 '&'를 가진 bean id를 앞에 붙여서 수행될것이다. 그래서 myBean id를 가진 FactoryBean을 위해, 컨테이너의 getBean("myBean")를 호출하는 것은 FactoryBean의 산출물을 반환할것이다. 하지만 getBean("&myBean")을 호출하는 것은 FactoryBean 인스턴스 자체를 반환할것이다.
beans 패키지는 종종 프로그램마다 다른 방식으로 관리와 bean을 변경하기 위한 기초적인 기능을 제공하는 동안 context 패키지는 좀 더 프레임워크 기반 형태로 BeanFactory기능을 강화시키는 ApplicationContext를 추가한다. 많은 사용자는 이것을 수동으로 생성하지 않을뿐 아니라 J2EE 웹 애플리케이션의 일반적인 시작 프로세스의 일부처럼 자동적으로 ApplicationContext를 시작하기 위한 ContextLoader처럼 지원 클래스에 의존하는 대신에 완벽한 선언적인 형태로 ApplicationContext를 사용할것이다. 물론 이것은 ApplicationContext을 프로그램마다 다르게 생성하는것이 가능하다.
context 패키지는 위한 기초는 org.springframework.context 패키지에 위치한 ApplicationContext인터페이스이다. BeanFactory인터페이스에서의 파생물은 BeanFactory의 모든 기능을 제공한다. 좀더 프레임워크 기반의 형태로 작업하는것을 허용하기 위해 레이어와 구조적인 컨텍스트를 사용하라. context 패키지는 다음을 제공한다.
MessageSource, i18n 스타일로 메시지에 대한 접근을 제공한다.
자원에 대한 접근, URL이나 파일과 같은 형태.
이벤트 전달(propagation) ApplicationListener인터페이스를 구현하는 bean을 위한
다중(구조적인) 컨텍스트의 로딩, 예를 들어 애플리케이션의 웹 레이어처럼, 각각을 하나의 특정 레이어에 집중될수 있도록 허용하는
ApplicationContext가 BeanFactory의 모든 기능을 포함하기 때문에 , 이것은 메소리 소비가 치명적이고 몇몇 추가적인 킬로바이트가 다른 아마도 애플릿과 같은 몇몇 제한된 상황을 위하는 것을 제외하고 BeanFactory에 우선하여 사용되는 것이 추천된다. 다음 부분은 ApplicationContext가 기본적인 BeanFactory기능에 추가한 기능을 언급한다.
ApplicationContext인터페이스는 MessageSource라고 불리는 인터페이스를 확장해서 메시징(i18n또는 국제화)기능을 제공한다. HierarchicalMessageSource와 함께 구조적인 메시지를 분석하는 능력을 가진다. 여기엔 Spring이 메시지 분석을 제공하는 기초적인 인터페이스가 있다. 여기에 정의된 메소드를 빨리 알아보자.
String getMessage(String code, Object[] args, String default, Locale loc): MessageSource로 부터 메시지를 받기 위해 사용되는 기초적인 메소드. 특정 로케일을 위해 발견되는 메시지가 없을 때 디폴트 메시지가 사용된다. 전달된 인자는 표준적인 라이브러리에 의해 제공되는 MessageFormat 기능을 사용해서 대체값처럼 사용된다.
String getMessage(String code, Object[] args, Locale loc): 이전 메소드와 기본적으로는 같다. 하지만 한가지가 다르다. 디폴트 메시지가 선언될수 없다. 만약 메시지가 발견될수 없다면, NoSuchMessageException가 던져진다.
String getMessage(MessageSourceResolvable resolvable, Locale locale): 위 메소드에서 사용된 모든 프라퍼티는 이 메소드를 통해 사용할수 있는 MessageSourceResolvable 라는 이름의 클래스에 포장된다.
ApplicationContext가 로드될때 이것은 컨텍스트내 정의된 MessageSource bean을 위해 자동적으로 찾는다. bean은 messageSource을 가진다. 만약 그러한 bean이 발견된다면 위에서 언급된 메소드에 대한 모든 호출은 발견된 메시지소스에 위임될것이다. 만약 발견되는 메시지소스가 없다면 같은 이름을 가진 bean을 포함하는 부모를 가진다면 ApplicationContext가 보기를 시도한다. 만약 그렇다면 이것은 MessageSource처럼 그 bean을 사용한다. 만약 메시지를 위한 어떤 소스를 발견할수 없다면 빈 StaticMessageSource는 위에서 정의된 메소드에 호출을 받을수 있기 위해 인스턴스화될것이다.
Spring은 현재 두개의 MessageSource 구현물을 제공한다. 여기엔 ResourceBundleMessageSource 와 StaticMessageSource가 있다. 둘다 메시지를 내포하기 위해 NestingMessageSource을 구현한다. StaticMessageSource는 소스에 메시지를 추가하기 위한 프로그램마다 다른 방법을 제공하지만 거의 사용되지 않는다. ResourceBundleMessageSource는 좀더 흥미롭고 우리가 제공할 예제이다.
<beans> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>format</value> <value>exceptions</value> <value>windows</value> </list> </property> </bean> </beans>
이것은 당신이 format, exceptions 과 windows라고 불리는 당신의 클래스패스내 정의된 3개의 resource bundle을 가진다고 가정한다. ResourceBundles을 통한 메시지를 분석하는 JDK표준적인 방법을 사용하여 메시지를 분석하기 위한 요청이 다루어질것이다. 이 예제의 목적을 위해, 위 resource bundle파일의 두개의 내용을 가정해보자.
# in 'format.properties'
message=Alligators rock!
# in 'exceptions.properties'
argument.required=The '{0}' argument is required.
MessageSource 기능을 수행하기 위한 몇몇 (명백히 자명한) 드라이버 코드는 아래와 같을수 있다. 모든 ApplicationContext구현물이 MessageSource이고 그래서 MessageSource인터페이스로 변환할수 있다는 것을 기억하라.
public static void main(String[] args) { MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); String message = resources.getMessage("message", null, "Default", null); System.out.println(message); }
위 프로그램의 수행으로 부터 결과 출력물은 다음과 같을 것이다..
Alligators rock!
목록화하기 위해, MessageSource는 'beans.xml'라고 명명된 파일내 정의되었다(이 파일은 classpath의 가장 상위에 존재한다.). 'messageSource' bean정의는 basenames프라퍼티를 통해 많은 resource bundle을 참조한다. 목록내에서 basenames프라퍼티로 전달되는 3개의 파일은 당신의 classpath의 가장 상위에 존재한다. (각각 format.properties, exceptions.properties, 그리고 windows.properties 라는 파일명을 가진다).
다른 예제를 보자. 그리고 이 시점에 우리는 메시지 검색을 위한 인자를 전달할 것이다. 이 인자들은 문자열로 변환되고 검색 메시지내 placeholder로 삽입된다. 이것은 예제와 함께 설명되는게 가장 좋다.
<beans> <!-- this MessageSource is being used in a web application --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="baseName" value="WEB-INF/test-messages"/> </bean> <!-- let's inject the above MessageSource into this POJO --> <bean id="example" class="com.foo.Example"> <property name="messages" ref="messageSource"/> </bean> </beans>
public class Example { private MessageSource messages; public void setMessages(MessageSource messages) { this.messages = messages; } public void execute() { String message = this.messages.getMessage("argument.required", new Object [] {"userDao"}, "Required", null); System.out.println(message); } }
execute() 메소드의 호출의 결과는 다음과 같을것이다.
The 'userDao' argument is required.
국제화(i18N)를 고려해볼때, Spring의 다양한 MessageResource구현물은 같은 로케일 분석과 표준적인 JDK ResourceBundle처럼 되돌리기(fallback)규칙을 따른다. 짧게 말해, 이미 정의된 예제 'messageSource'로 계속할것이다. 만약 당신이 영국(en-GB) 로케일에 대해 메시지를 해석하길 원한다면, 각각 format_en_GB.properties, exceptions_en_GB.properties, 그리고 windows_en_GB.properties라고 불리는 파일을 생성할 것이다.
로케일 분석은 애플리케이션의 주위환경에 의해 대개 관리된다. 비록 이 예제의 목적을 위해, 우리는 영국 메시지를 분석하기를 원하는 로케일을 직접 명시할것이다.
# in 'exceptions_en_GB.properties'
argument.required=Ebagum lad, the '{0}' argument is required, I say, required.
public static void main(final String[] args) { MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); String message = resources.getMessage("argument.required", new Object [] {"userDao"}, "Required", Locale.UK); System.out.println(message); }
위 프로그램으의 수행으로 부터 결과 출력물은 다음과 같을 것이다..
Ebagum lad, the 'userDao' argument is required, I say, required.
MessageSourceAware인터페이스는 정의된 MessageSource에 대한 참조를 얻기 위해 사용될수 있다. MessageSourceAware인터페이스를 구현하는 ApplicationContext로 정의된 어느 bean은 생성되고 설정될때 애플리케이션 컨텍스트의 MessageSource를 주입할것이다.
ApplicationContext내 이벤트 핸들링은 ApplicationEvent 클래스와 ApplicationListener인터페이스를 통해 제공된다. 만약 ApplicationListener 인터페이스를 구현하는 bean이 컨텍스트로 배치된다면 매번 ApplicationEvent는 통지될 bean인 ApplicationContext에 배포된다. 기본적으로 이것은 표준적인 Observer 디자인 패턴이다. Spring은 3가지 표준적인 이벤트를 제공한다.
Table 3.5. Built-in Events
이벤트 | 설명 |
---|---|
ContextRefreshedEvent | ApplicationContext가 초기화되거나 재생(refresh)될때 배포되는 이벤트. 여기서 초기화는 모든 bean이 로드되고 싱글톤은 미리 인스턴스화되며 ApplicationContext는 사용할 준비가 된다는 것을 의미한다. |
ContextClosedEvent | ApplicationContext의 close()메소드를 사용하여 ApplicationContext가 닫힐때 배포되는 이벤트. 여기서 닫히는 것은 싱글톤이 없어지는(destroy)되는것을 의미한다. |
RequestHandledEvent | 웹 특정 이벤트는 HTTP요청이 서비스(이를 테면 요청이 종료될때 after가 배포될것이다.)되는 모든 bean을 말한다. 이 이벤트는 Spring의 DispatcherServlet을 사용하는 웹 애플리케이션에서만 적용가능하다. |
사용자정의 이벤트를 구현하는것은 잘 작동될수 있다. ApplicationContext의 publishEvent() 메소드를 간단히 호출하는 것은 당신의 사용자정의 이벤트 클래스가 ApplicationEvent을 구현하는 파라미터를 명시하는 것이다. 이벤트 리스너(listener)는 이벤트를 동시에 받아들인다. 이것은 publishEvent() 메소드는 모든 리스너가 이벤트 처리를 종료할때 까지 블럭된다는것을 의미한다(ApplicationEventMulticaster 구현물을 통한 대안의 이벤트 배포 전략을 제공하는 것이 가능하다.). 게다가 리스너가 이벤트를 받을때 이것은 배포자(publisher)의 만약 트랜잭션 컨텍스트가 사용가능하다면 트랜잭션 컨텍스트내 작동한다.
예제를 보자. 첫번째 ApplicationContext이다:
<bean id="emailer" class="example.EmailBean"> <property name="blackList"> <list> <value>black@list.org</value> <value>white@list.org</value> <value>john@doe.org</value> </list> </property> </bean> <bean id="blackListListener" class="example.BlackListNotifier"> <property name="notificationAddress" value="spam@list.org"/> </bean>
Now, let's look at the actual classes:
public class EmailBean implements ApplicationContextAware {
private List blackList;
private ApplicationContext ctx;
public void setBlackList(List blackList) {
this.blackList = blackList;
}
public void setApplicationContext(ApplicationContext ctx) {
this.ctx = ctx;
}
public void sendEmail(String address, String text) {
if (blackList.contains(address)) {
BlackListEvent evt = new BlackListEvent(address, text);
ctx.publishEvent(evt);
return;
}
// send email...
}
}
public class BlackListNotifier implement ApplicationListener {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(ApplicationEvent evt) {
if (evt instanceof BlackListEvent) {
// notify appropriate person...
}
}
}
물론 이 특별한 예제는 기본적인 이벤트 기법을 설명하기에는 충분하지만 좀더 나은 방법(아마도 AOP기능을 사용하여)으로 구현될수 있을것이다.
애플리케이션 컨텍스트의 최적화된 사용법과 이해를 위해, 사용자는 Chapter 4, 자원에서 언급된것처럼 Spring의 Resource추상화 자체에 친숙해야만 한다.
애플리케이션 컨텍스트는 Resource를 로드하기 위해 사용될수 있는 ResourceLoader이다. Resource는 본질적으로는 classpath, 파일시스템 위치, 표준적인 URL로 언급할수 있는 어떠한 지점, 그리고 다양한 변형형태를 포함하는 투명한 형태의 위치로부터 하위 레벨의 resource를 얻기 위해 사용될수 있는 java.net.URL이다(사실, 이것은 적절한 위치의 URL을 포장하고 사용한다.). resource위치 문자열이 어떤 특별한 접두사없는 간단한 경로라면, resource이 유래된 지점은 실질적 애플리케이션 컨텍스트 타입에 특성화되고 적절하게된다.
애플리케이션 컨텍스트로 배치된 bean은 애플리케이션 컨텍스트 자체가 ResourceLoader로 전달되는 초기화시점에 자동적으로 콜백되기 위한 특별한 표시자(marker) 인터페이스인 ResourceLoaderAware를 구현한다.
bean은 정적 resource에 접근하기 위해 사용되는 Resource타입의 프라퍼티를 나타내고 프라퍼티는 다른 프라퍼티처럼 주입될것이다. bean을 배치하는 사람은 간단한 문자열 경로처럼 Resource 프라퍼티를 명시하고 텍스트 문자열을 실질적인 Resource객체로 변환하기 위해 컨텍스트에 의해 자동으로 등록된 특별한 자바빈 PropertyEditor에 의존한다.
위치 경로나 ApplicationContext생성자에 제공되는는 경로는 실질적으로는 resource문자열이고 특정 컨텍스트 구현물에 적절하게 처리된 간단한 형태이다(이를테면 ClassPathXmlApplicationContext는 classpath위치로 간단한 위치경로를 처리한다.). 하지만 실질적 컨텍스트 타입에 따라 classpath나 URL로부터 정의의 로딩을 강요하기 위한 특정 접두사와 함께 사용될것이다.
프로그램으로 처리해서 종종 생성될 BeanFactory와 반대로, ApplicationContext 인스턴스는 예를 들어 ContextLoader를 사용하여 선언적으로 생성될수 있다. 물론 당신은 ApplicationContext구현물중 하나를 사용하여 프로그램으로 처리하여 ApplicationContext인스턴스를 생성할수 있다. 먼저 ContextLoader 인터페이스와 그것의 구현물을 시험해보자.
ContextLoader는 ContextLoaderListener 와 ContextLoaderServlet의 두가지의 구현물을 가진다. 그것들 모두 같은 기능을 가지지만 리스너(listener)는 서블릿 2.2 호환 컨테이너내에서는 사용될수 없다는 것이 다르다. 서블릿 2.4 스펙이후로 리스너(listener)는 웹 애플리케이션의 시작 후 초기화를 요구한다. 많은 2.3 호환 컨테이너는 이미 이 기능을 구현한다. 이것은 당신이 사용하는 것에 따르지만 모든것은 당신이 아마도 ContextLoaderListener를 선호하는것과 동일하다. 호환성에 대한 좀더 상세한 정보를 위해서는 ContextLoaderServlet을 위한 JavaDoc를 보라.
당신은 다음처럼 ContextLoaderListener을 사용하여 ApplicationContext을 등록할수 있다.
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- or use the ContextLoaderServlet instead of the above listener <servlet> <servlet-name>context</servlet-name> <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> -->
리스너(listener)는 contextConfigLocation 파라미터를 조사한다. 만약 이것이 존재하지 않는다면 이것은 디폴트로 /WEB-INF/applicationContext.xml을 사용할것이다. 이것이 존재할때, 이것은 미리 정의된 분리자(delimiter-콤마, 세미콜론 그리고 공백)를 사용하여 문자열을 분리하고 애플리케이션 컨텍스트가 검색되는 위치처럼 값을 사용할것이다. ContextLoaderServlet는 말하는것처럼 ContextLoaderListener대신에 사용될수 있다. 서블릿은 리스너(listener)가 하는것처럼 'contextConfigLocation' 파라미터를 사용할것이다.
애플리케이션내 코드의 대부분은 이것이 생성될때 컨테이너에 의해 자신만의 의존성이 제공되고 컨테이너를 완벽하게 자각하지 못하는 Spring IoC 컨테이너에 코드가 제공되는 DI스타일로 작성되는 것이 가장 좋다. 어쨌든 다른 코드와 함께 묶기 위한 때때로 필요한 코드의 작은 glue레이어를 위해 Spring IoC컨테이너에 접근하는 싱글톤(또는 준(quasi)-싱글톤 스타일을 위해 때때로 필요하다. 예를 들어 써드파티(third party) 코드가 BeanFactory의 객체를 얻기 위해 이것을 강제로 하게 할수 있는 능력없이 새로운 객체를 직접적으로 생성(Class.forName() 스타일)하도록 시도할수 있다. 만약 써드파티(third party)모드에 의해 생성된 객체가 작은 스텁(stub)나 프록시라면 위임하는 실제 객체를 얻기 위해 Spring IoC컨테이너에 접근하는 싱글톤 스타일을 사용한다. inversion of control은 여전히 코드의 대부분을 위해 달성된다.(객체는 컨테이너로 부터 나온다.). 게다가 대부분의 코드는 컨테이너나 이것이 접근되는 방법을 자각하지 않는다. 그리고 모든 이익을 가지는 다른 코드로 부터 커플링되지 않은체로 남게된다. EJB는 Spring IoC컨테이너로 부터 나오는 명확한 자바 구현물 객체를 위해 위임하는 스텁/프록시 접근법을 사용할수 있다. Spring IoC컨테이너 자체가 이론상 싱글톤이 되지 않는 동안 이것은 비-싱글톤 Spring IoC컨테이너를 사용하는 각각의 bean을 위해 메모리 사용이나 초기화 시점(Hibernate SessionFactory처럼 Spring IoC컨테이너내 bean을 사용할 때)의 개념에서 비현실적일수 있다.
다른 예제처럼, 다중 레이어(이를테면, 다양한 JAR파일들, EJB들, 그리고 EAR처럼 패키지된 WAR파일)의 복잡한 J2EE애플리케이션에서 이것 자체의 Spring IoC 컨테이너정의(구조를 효과적으로 형상화하는)를 가진 각각의 레이어와 함께 가장 상위 구조내 단 하나의 웹 애플리케이션(WAR)이 존재할때 각각의 레이어의 다중 XML정의 파일에서 하나의 복잡한 Spring IoC 컨테이너를 간단하게 생성하기 위한 접근법이 선호된다. 모든 Spring IoC 컨테이너 구현물은 이 형태로 다중 정의 파일로 부터 생성될수 있다. 어쨌든 구조의 가장 상위의 다중의 구성원(sibling) 웹 애플리케이션을 가진다면 이것은 아래쪽의 레이어로부터 대부분 일치하는 bean정의를 구성하는 각각의 웹 애플리케이션을 위한 Spring IoC 컨테이너를 생성하는것이 메모리 사용을 증가시키거나 오랜 시간동안 초기화(이를테면, Hibernate SessionFactory)하는 그리고 부작용(side-effects)과 같은 여러개의 bean을 생성하는 문제의 소지가 있다. 대안으로 ContextSingletonBeanFactoryLocator 나 SingletonBeanFactoryLocator와 같은 클래스는 웹 애플리케이션 Spring IoC 컨테이너의 부모처럼 사용될수 있는 효과적인 싱글톤형태로 다중 구조적 Spring IoC 컨테이너 로드를 요구하기 위해 사용될수 있다. 그 결과는 아래쪽의 레이어를 위한 bean정의가 필요할때만 오직 한번 로드되는것이다.
당신은 각각의 JavaDoc를 봐서 SingletonBeanFactoryLocator 와 ContextSingletonBeanFactoryLocator 를 사용하는 상세화된 예제를 볼수 있을것이다.
EJB장에서 언급되는것처럼, EJB를 위한 Spring의 편리한 기본 클래스는 필요하다면 SingletonBeanFactoryLocator과 ContextSingletonBeanFactoryLocator 의 사용으로 쉽게 대체되는 비-싱글톤 BeanFactoryLocator을 대개 사용한다.