Chapter 3. Beans, BeanFactory 그리고 ApplicationContext

3.1. 소개

Spring내에서 가장 기초적이고 가장 중요한 두개의 패키지는 org.springframework.beansorg.springframework.context패키지이다. 이 패키지내 코드는 Spring의 Inversion of Control(대안으로 Dependency Injection으로 불리는)기능의 기초를 제공한다. BeanFactory는 잠재적으로 어떤 종류의 저장 기능을 사용하여 어떤 성질의 bean을 관리하는 향상된 설정 기법을 제공한다. ApplicationContext는 BeanFactory(또는 하위클래스)의 가장 상위에 빌드하고 향상된 점 중에서도 Spring AOP기능의 좀더 쉬운 통합, 메시지 자원 핸들링(국제화내에서 사용하기 위한), 이벤트 위임, ApplicationContext와 옵션적으로 부모 컨텍스트를 생성하기 위한 선언적인 기법, WebApplicationContext와 같은 애플리케이션 레이어 특정 컨텍스트를 사용하는 것과 같은 다른 기능을 추가한다.

짧게 말하면 ApplicationContext가 그것들중 몇몇 좀더 J2EE이고 기업중심인 것을 위한 향상된 기능을 추가하는 반면에 BeanFactory는 설정 프레임워크와 기본적인 기능을 제공한다. 일반적으로 ApplicationContext는 BeanFactory의 완벽한 수퍼셋(superset)이고 BeanFactory기능과 행위의 어떤 설명은 ApplicationContexts에 잘 적용이 되도록 검토되어야 한다.

사용자들은 때때로 BeanFactory나 ApplicationContext중 어느것이 특정 상황에서 사용하기 위해 가장 적합한지 확신하지 못한다. 대개 J2EE환경내 대부분의 애플리케이션을 빌드할때 ApplicationContext는 일반적으로 선호하는 몇몇 기능의 사용을 위한 좀더 선언적인 접근법을 허용하는 동안 BeanFactory의 모든 기능을 제공하고 기능적인 면에서 이것을 추가하였기 때문에 가장 좋은 선택은 ApplicationContext를 사용하는 것이다.. 당신이 BeanFactory를 사용하기 위해 선택할 중요한 사용 시나리오는 메모리 사용이 가장 큰 관심사항일때(가장 최근 킬로바이트가 계산하는 애플릿과 같은 경우)이고 당신이 ApplicationContext의 모든 기능을 필요로 하지 않을때이다.

이 장은 BeanFactory와 ApplicationContext에 둘다 관련되는 사항들을 다룬다. BeanFactory를 언급할때 당신은 언제나 ApplicaitonContext에도 적용이 된다고 가정할것이다. 기능이 ApplicaitonContext에만 적용될때는 명시적으로 이것만 언급한다.

3.2. BeanFactory 와 BeanDefinitions - 기초

3.2.1. BeanFactory

BeanFactory 는 인스턴스를 생성하고 설정하고 많은 수의 bean을 관리하는 실질적인 컨테이너이다. 그 bean들은 일반적으로 서로 협력하고 그들 사이의 의존성을 가진다. 그 의존성들은 BeanFactory에 의해 사용된 설정 데이타에 반영된다(비록 몇몇 의존성은 설정 데이타에서 보이지 않을수도 있지만 실행시 bean사이의 프로그램마다 다른 상호작용의 기능이 될것이다.).

BeanFactory는 다중 구현물을 위한 org.springframework.beans.factory.BeanFactory인터페이스에 의해 나타난다. 가장 공통적으로 사용되는 간단한 BeanFactory구현물은 org.springframework.beans.factory.xml.XmlBeanFactory이다. (이것은 ApplicationContexts가 BeanFactory의 하위 클래스임을 알리는 자격을 갖추어야 한다. 그리고 대부분의 사용자는 결국에 ApplicationContext의 각각 다른 형태의 XML을 사용해야만 한다. )

비록 대부분의 시나리오를 위해 BeanFactory에 의해 관리되는 대부분의 모든 사용자 코드가 BeanFactory를 인식하지 못하더라도 BeanFactory는 어떻게 해서든지 인스턴스화되어야 한다. 이것은

InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);

또는

ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);

또는

ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
        new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
// of course, an ApplicationContext is just a BeanFactory
BeanFactory factory = (BeanFactory) appContext;

과 같은 명시적인 사용자 코드를 통해 발생할수 있다.

많은 사용 시나리오를 위한 사용자 코드는 Spring프레임워크가 인스턴스화 한 이후 BeanFactory를 인스턴스화 하지 않을것이다. 예를 들면 웹 레이어는 J2EE웹 애플리케이션의 일반적인 시작 프로세스의 일부처럼 Spring ApplicationContext을 자동적으로 로드하기 위한 지원 코드를 제공한다. 이 선언적인 프로세스는 here에서 언급된다.

BeanFactory의 프로그램마다 다른 조작이 이후에 언급될때까지 다음 부분은 BeanFactory의 설정을 언급하는데 집중할것이다.

BeanFactory설정은 가장 기초적인 레벨, BeanFactory가 관리해야만 하는 하나 이상의 bean의 정의로 구성된다. XmlBeanFactory에서 가장 상위레벨의 beans 요소내 하나 이상의 beans요소로 설정된다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

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

  ...

</beans>

3.2.2. BeanDefinition

각각의 다른 형태(XmlBeanFactory처럼)의 DefaultListableBeanFactory내 Bean정의는 다음의 상세사항을 포함하는 BeanDefinition객체처럼 표현된다.

  • 클래스명 : 이것은 bean정의내 서술되는 bean의 실질적인 구현 클래스이다. 어쨌든 bean이 일반적인 생성자를 사용하는 대신에 정적인 factory메소드를 호출함으로써 생성된다면, 이것은 factory클래스의 클래스명이 될것이다.

  • bean행위적 설정 요소, bean이 컨테이너내에서 어떻게 행위를 하는지의 상태(이를 테면 프로터타입 또는 싱글톤, autowiring모드, 의존성 체크 모드, 초기화(initialization)와 제거(destruction)메소드.)

  • 새롭게 생성되는 bean내에 셋팅할 생성자 인자와 프라퍼티 값. 예제는 Connection pool(프라퍼티나 생성자 인자처럼 명시되는)이나 pool크기 제한을 관리하는 bean내에서 사용하는 Connection의 수가 될것이다.

  • 이것의 작업을 하기 위해 필요한 bean. 이를 테면 collaborators(프라퍼티나 생성자 인자처럼 명시되는). 의존성에 의해 호출될수 있다.

위에서 나열된 개념은 bean정의를 구성하는 요소의 집합을 직접적으로 번역한다. 그러한 요소그룹의 몇몇은 각각에 대한 좀더 상세한 문서 링크를 포함해서 밑에 나열했다.

bean정의는 실질적인 org.springframework.beans.factory.config.BeanDefinition인터페이스와 다양한 하위 인터페이스 그리고 구현물들에 의해 표현된다. 어쨌든 이것은 대부분의 사용자 코드가 BeanDefinition와 함께 작동할 것 같지는 않다.

bean을 생성하는 방법에 대한 정보를 포함하느 bean정의 외에도 bean factory는 존재하는 bean인스턴스를 등록하는것을 허용할수 있다. DefaultListableBeanFactory는 org.springframework.beans.factory.config.ConfigurableBeanFactory인터페이스에 의해 정의된 registerSingleton메소드를 통해 이것을 지원한다. 전형적인 애플리케이션은 단순히 bean 정의를 가지고 작동한다.

3.2.3. bean 클래스

class 속성은 일반적으로 필수(두 예외를 위한 Section 3.2.3.3, “인스턴스 factory메소드를 통한 Bean 생성”Section 3.5, “추상 그리고 자식 bean정의”을 보라.)이고 두가지 목적중 하나를 위해 사용된다. BeanFactory자체가 생성자를 호출함(new를 호출하는 자바코드와 같은)으로써 bean을 직접적으로 생성하는 많은 공통적인 상황에서 class속성은 생성될 bean의 클래스를 명시한다. BeanFactory가 bean을 생성하기 위한 클래스의 정적인 factory 메소드를 호출하는 다소 적은 공통적인 상황에서는 class속성은 정적 factory메소드를 포함하는 실질적인 클래스를 명시한다. (정적 factory메소드로 부터 반환되는 bean의 타입은 같은 클래스이거나 완전히 다른 클래스일것이다. 이것은 문제가 아니다.)

3.2.3.1. 생성자를 통한 Bean 생성

생성자를 이용한 접근법을 사용하여 bean을 생성할때 모든 일반적인 클래스는 Spring과 Spring에 호환되는 것에 의해 사용가능하다. 생성된 클래스는 어떤 특정 인터페이스를 구현하거나 특정 형태로 작성될 필요가 없다. bean클래스를 명시하는 것만으로도 충분하다.어쨌든 특정 bean을 위해 사용하기 위한 어떤 타입의 IoC에 의존한다. 당신은 아마도 디폴트(빈) 생성자가 필요할것이다.

추가적으로 BeanFactory는 자바빈을 관리하는데 제한을 두지 않는다. 이것은 또한 당신이 관리하고자 하는 사실상의 어떤 클래스를 관리하는것이 가능하다. Spring을 사용하는 대부분의 사람들은 BeanFactory내 실질적인 자바빈(프라퍼티뒤 디폴트 생성자와 선호하는 ssetter와 getter메소드를 가지는)을 가지는 것을 선호한다. 하지만 이것은 BeanFactory내 색다른 bean스타일이 아닌 클래스를 가지는것이 가능하다. 만약 예를 들어, 당신이 자바빈 애플리케이션을 따르지 않는 예전(legacy) connection pool을 사용할 필요가 있다면 걱정하지 말라. Spring은 이것을 잘 관리할수 있다.

XmlBeanFactory을 사용하면 당신은 다음처럼 당신의 bean클래스를 명시할수 있다.

<bean id="exampleBean"
      class="examples.ExampleBean"/>
<bean name="anotherExample"
      class="examples.ExampleBeanTwo"/> 

생성자를 위한 인자, 또는 객체 인스턴스가 생성된후 객체 인스턴스의 프라퍼티를 셋팅하기 위한 기법은 짧게 언급될것이다.

3.2.3.2. 정적 factory메소드를 통한 Bean 생성

정적인 factory메소드를 포함하는 클래스를 명시하는 class 속성에 따라 정적인 factory메소드를 사용하여 생성되기 위한 bean을 정의할때 factory-method라는 이름의 다른 속성은 factory메소드 자체의 이름을 명시할 필요가 있다. Spring은 만약 생성자를 통해 일반적으로 생성되는 것처럼 처리되는 시점으로 부터 이 메소드(나중에 언급되는 것처럼 인자의 목록을 가지는)를 호출하고 시스템에서 살아있는(live)객체를 돌려받는 것이 가능하도록 기대한다. bean정의와 같은 것을 위해 사용하는것은 예전(legacy)코드내 정적 factory를 호출하는것이다.

다음은 factory메소드를 호출함으로써 생성되기 위한 bean을 명시하는 bean정의의 예제이다. 정의는 반환되는 객체의 타입(클래스)를 명시하지 않을뿐 아니라 클래스는 factory메소드를 포함한다는것을 알라. 예를 들어 createInstance정적 메소드가 되어야만 한다.

<bean id="exampleBean"
      class="examples.ExampleBean2"
      factory-method="createInstance"/>

factory메소드를 위한 인자나 factory로 부터 반환된 뒤 객체 인스턴스의 프라퍼티를 셋팅하는 것을 지원하기 위한 기법은 짧게 언급될것이다.

3.2.3.3. 인스턴스 factory메소드를 통한 Bean 생성

bean을 생성하기 위한 정적 factory메소드를 사용하는 것과 흡사한것은 factory로 부터 존재하는 bean의 factory메소드가 새로운 bean을 생성하기 위해 호출되는 인스턴스 (정적이 아닌) factory메소드 사용이다.

이 기법을 사용하는것은 class 속성이 빈값으로 남아야만 하고 factory-bean 속성은 최근 bean의 이름이나 factory메소드를 포함하는 조상(ancestor) bean factory를 명시해야만 한다. factory메소드 자체는 factory-method 속성을 통해 셋팅될것이다.

다음은 예제이다.

<!-- The factory bean, which contains a method called
     createInstance -->
<bean id="myFactoryBean"
      class="...">
  ...
</bean>
<!-- The bean to be created via the factory bean -->
<bean id="exampleBean"
      factory-bean="myFactoryBean"
      factory-method="createInstance"/>

비록 bean프라퍼티를 셋팅하기 위한 기법이 여전히 논의되어도 이 접근법의 하나의 함축은 factory bean자체가 컨테이너에 의해 의존성 삽입(Dependency Injection)을 통해 관리되고 설정될수 있다.

3.2.4. bean 구분자 (idname)

모든 bean은 하나 이상의 id(구분자, 이름이라고 불리는:그 용어들은 같은것을 지칭한다.)를 가진다. 그 id들은 bean을 생성한 BeanFactory 나 ApplicationContext내에서 유일해야만 한다. bean은 거의 대부분 하나의 id만을 가진다. 하지만 bean이 한개의 id보다 많은수를 가진다면 나머지 것들은 기본적으로 별칭으로 간주될수 있다..

XmlBeanFactory(여러가지의 ApplicationContext를 포함해서)에서 당신은 bean id를 명시하기 위해 idname속성을 사용하고 적어도 하나의 id는 그 속성중 하나나 둘다에 명시해야만 한다. id 속성은 당신에게 하나의 id를 명시하는것을 허용하고 실제 XML요소의 ID속성처럼 XML DTD(정의문서)내 표시된다. 파서는 다른 요소점이 이 요소로 돌아갈때 몇몇 별개의 유효성체크를 하는것이 가능하다. 그러한 것처럼 bean id를 명시하기 위한 선호되는 방법이 있다. 어쨌든 XML스펙은 XML ID들내 지역적인(legal) 문자들을 제한한다. 이것은 언제나 제약인것은 아니지만 만약 당신이 그러한 문사들중 하나를 사용할 필요가 있거나 bean에 대한 다른 별칭을 소개하길(introduce) 원한다면 당신은 그렇게 하거나 name 속성을 통해 하나이상의 bean id(,으로 구분되는) 또는 세미콜론(;)을 명시하는것으로 대신한다.

3.2.5. 싱글톤이나 비-싱글톤(non-singleton)

bean은 두가지 모드(싱글톤또는 비-싱글톤)중 하나로 배치되기 위해 정의된다. 후자(비-싱글톤)는 프로토타입이라도도 불린다. 비록 용어가 정확하게 적합한것이 아닌 느슨하게 대충 사용이 되더라도). bean이 싱글톤일때 bean의 오직 하나의 공유인스턴스만이 관리될것이고 id를 가진 bean을 위한 모든 요청이나 bean정의에 되는 id가 반환되는 하나의 특정 bean인스턴스를 야기한다.

비-싱글톤, bean배치의 프로토타입 모드는 특정 bean이 수행하기 위한 요청에 대해 매번 새로운 bean인스턴스의 생성이라는 결과를 만든다. 이것은 예를 들어 각각의 사용자가 비의존적인 사용자 객체나 유사한 어떤것이 필요한 상황에 이상적이다.

bean은 당신이 디폴트로 싱글톤모드로 배치된다. 타입을 비-싱글톤으로 변경함으로써 그것을 잊지말라. bean을 위한 각각의 요청은 새롭게 생성된 bean을 초래하고 이것은 당신이 실질적으로 원하는것이 되지는 않을것이다. 그래서 오직 절대적으로 필요할때 모드를 프로터타입으로 변경하라.

아래의 예제에서, 두개의 bean은 하나는 싱글톤으로 정의되고 다른 하나는 비-싱글톤(프로토타입)으로 선언되었다. yetAnotherExample이 오직 한번만 생성되는 동안 exampleBean은 각각 생성되고 매번 클라이언트는 이 bean을 위해 BeanFactory를 요청한다. 정확히 같은 인스턴스를 위한 참조는 이 bean을 위한 각각의 요청에 반환된다.

<bean id="exampleBean"
      class="examples.ExampleBean" singleton="false"/>
<bean name="yetAnotherExample"
      class="examples.ExampleBeanTwo" singleton="true"/>

메모: bean을 프로토타입모드내에서 배치할때 bean의 생명주기는 미세하게 변경한다. 정의에 의해 Spring은 이것이 생성된 후에는 비-싱글톤/프로토타입 bean의 완벽한 생명주기를 관리할수 없다. 이것은 더 이상 놓치지 않는 클라이언트와 컨테이너에게 주어진다.당신은 'new' 연산(operator)을 위한 교체품처럼 비-싱글톤/프로토타입 bean에 대하여 얘기할때 Spring의 역활에 대해 생각할수있다. 어떠한 생명주기 양상은 클라이언트에 의해 다루어질 시점을 지난다. BeanFactory내 bean의 생명주기는 Section 3.4.1, “Lifecycle 인터페이스”에서 좀더 상세하게 언급된다.

3.3. 프라퍼티, 협력자(collaborators), autowiring 과 의존성 체크

3.3.1. bean프라퍼티와 협력자(collaborators) 셋팅하기

IoC(Inversion of Control)는 이미 Dependency Injection처럼 간주되고 있다. 기본적인 원칙은 오직 생성자 인자, factory메소드를 위한 인자 또는 factory메소드로부터 생성되거나 반환된 후 객체 인스턴스에 셋팅하는 프라퍼티를 통해 bean이 그것들의 의존성(이를 테면, 그것들이 함께 작동하는 다른 객체들)을 명시한다는 것이다. 그 다음, 이것이 bean을 생성할때 그러한 의존성을 실질적으로 삽입하기 위한 컨테이너의 책임이다. 이것은 근본적으로 bean인스턴스화의 역전(inverse - 나아가 Inversion of Control)이거나 클래스의 직접적인 생성을 사용하여 그것의 의존성을 위치시키는 것이다. 또는 서비스 위치자(Locator) 패턴처럼 어떤것이다. 우리가 의존성 삽입의 장점에 정성을 들이지 않을동안 이것은 코드가 좀더 깔끔하게 되고 좀더 높은 디커플링 등급에 도달하는 것이 bean이 그것들의 의존성을 보지 않을때 좀더 쉽게 되도록 하는 명백한 사용법이 된다. 하지만 그것들과 함께 제공되고 추가적으로 의존성이 위치하는 곳과 실질적인 타입이 무엇인지 알지 않는다.

앞 단락에서 알아본것처럼 Inversion of Control/의존성 삽입은 두가지 큰 종류가 존재한다.

  • setter-기반 의존성 삽입은 당신의 bean을 인스턴스화 하기 위한 인자 없는 생성자나 인자 없는 정적 factory메소드를 호출한 후에 당신의 bean의 setter를 호출함으로써 구체화된다. BeanFactory내 명시된 bean은 setter-기반 의존성 삽입을 사용하는 것이 신뢰할수 있는(true) 자바빈이다. Spring은 많은 수의 생성자 인자가 다루기 어렵고, 몇몇 프라퍼티가 선택사항일때 유별나기 때문에 대개 setter-기반 의존성 삽입의 사용을 지지한다.

  • 생성자-기반 의존성 삽입은 각각 협력자(collaborator)나 프라퍼티를 표현하는 많은 수의 인자를 가진 생성자를 호출함으로써 구체화된다. 추가적으로 bean을 생성하기 위해 특정 인자를 가진 정적 factory메소드를 호출하는 것은 대부분 동일하게 간주될수 있고 이 글의 나머지는 생성자를 위한 인자와 정적 factory메소드를 위한 인자를 눈여겨 볼것이다. 비록 Spring이 대부분의 경우를 위해 setter-기반 의존성 삽입의 사용을 지지한다고 하더라도 당신이 setter없이 다중 인자를 가진 생성자만 제공하는 이미 존재하는 bean을 사용하길 바랄지도 모르기 때문에 생성자-기반 접근법또한 완벽하게 지원한다. 추가적으로 좀더 간단한 bean을 위해 몇몇 사람들은 bean이 유효하지 않은 상태에서 생성이 될수 없다는 것을 확신하는 의미처럼 생성자 접근법을 선호한다.

BeanFactory는 이것이 관리하는 bean으로 의존성을 삽입하기 위한 이러한 형태 둘다 지원한다(이것은 사실 몇몇 의존성이 생성자 접근법을 통해 이미 제공된 후 setter-기반으로 의존성을 삽입하는 것들 지원한다.). BeanDefinition의 형태로 들어오는 의존성을 위한 설정은 프라퍼티를 하나의 포맷에서 다른 포맷으로 변환하는 방법을 알기 위한 자바빈 PropertyEditors와 같이 사용된다. 여기저기 전달되는 실제 값들은 PropertyValue객체의 형태로 수행된다. 어쨌든 Spring의 대부분의 사용자는 직접적으로 이 클래스를 다루지는 않을것이지만(이를테면 프로그램에 따라 다르게) XML정의 파일은 내부적으로 그러한 클래스의 인스턴스로 변환될것이고 전체 BeanFactory 나 ApplicationContext를 로드하기 위해 사용된다.

Bean의존성 해석은 일반적으로 다음처럼 발생한다.

  1. BeanFactory는 모든 bean을 서술하는 설정으로 생성되고 초기화된다. 대부분의 Spring사용자는 XML형태의 설정 파일을 지원하는 BeanFactory 나 ApplicationContext 종류를 사용한다.

  2. 각각의 bean은 프라퍼티, 생성자 인자, 또는 보통의 생성자 대신에 사용되는 정적 factory메소드를 위한 인자의 형태로 표현되는 의존성을 가진다. 이러한 의존성은 bean이 실질적으로 생성되었을때 bean에 제공될것이다.

  3. 각각의 프라퍼티또는 생성자의 인자는 셋팅하기 위한 값의 실질적인 정의이거나 BeanFactory내에서 다른 bean에 대한 참조이다. ApplicationContext의 경우 참조는 부모 ApplicationContext내 bean에 대한 것이 될수 있다.

  4. 각각의 프라퍼티와 생성자의 인자는 이것이 명시하는 어떠한 타입에서 프라퍼티나 생성자의 인자의 실질적인 타입으로 변환될수 있어야만 하는 값이다. 디폴트에 의해 Spring은 문자열로 제공되는 값에서 int, long, String, boolean, 등등과 같은 모든 내장 타입으로 변환할수 있다. 추가적으로 XML기반 BeanFactory 형태에 대해서 이야기 할때 그것들은 List, Map, Set 그리고 프라퍼티 collection타입들을 정의하기 위한 내장 지원을 가진다. 추가적으로 Spring은 문자열 값에서 다른, 임의의 타입으로 변환될수 있는 자바빈 PropertyEditor정의를 사용한다.(당신은 당신 자신의 사용자 지정 타입으로 변환할수 있도록 하는 당신 자신의 PropertyEditor정의를 가진 BeanFactory를 제공할수 있다. PropertyEditors에 대한 좀더 상세한 정보와 사용자 지정 PropertyEditors를 직접 추가하는 방법은 Section 3.9, “추가적인 사용자지정 PropertyEditors 등록하기”에서 찾을수 있다.). bean프라퍼티 타입이 자바클래스 타입일때 Spring은 당신에게 클래스명인 문자열값 같은 프라퍼티를 위한 값을 명시하도록 허용하고 내장된 ClassEditor PropertyEditor는 실질적인 클래스 인스턴스를 위한 클래스명을 변환하는것을 다룰것이다.

  5. Spring이 BeanFactory가 생성될때 bean참조가 유효한 bean에 실질적으로 참조(이를 테면 참조될 bean은 BeanFactory내 명시되거나 ApplicationContext의 경우 부모 컨텍스트)하는 프라퍼티를 체크하는것을 포함해서 BeanFactory내 각각의 bean의 설정을 체크하는 것을 구체화하는것은 중요하다. 어쨌든 bean프라퍼티 자체는 bean이 실질적으로 생성이 될때까지 셋팅되지 않는다. 싱글톤이고 미리 인스턴스화(ApplicationContext내 싱글톤 bean과 같은)되기 위한 bean을 위해 생성은 BeanFactory가 생성될때마다 발생하지만 반면에 이것은 bean이 요청될때만 발생한다. bean이 실질적으로 생성되었다면 이것은 의존성과 의존성의 의존성이 생성되고 할당되는것처럼 생성되기 위한 다른 bean의 그래프(graph)를 잠재적으로 야기할 것이다.

  6. 당신은 일반적으로 적절한것을 하기 위해 Spring을 신뢰할수 있다. 이것은 BeanFactory가 로드되는 시점에 존재하지 않는 bean과 순환적인 의존성에 대한 참조를 포함해서 설정사항을 다룰것이다. 이것은 bean이 실질적으로 생성될때 가능한 늦게 실질적으로 프라퍼티를 셋팅하고 의존성을 해석(이를 테면 필요하다면 그러한 의존적인것을 생성한다.)한다. 이것은 정확하게 로드되는 BeanFactory가 bean이나 이것의 의존성중 하나를 생성할때 문제가 발생한다면 당신이 bean을 요청할때 나중에 예외를 생성할수 있다는 것을 의미한다. 이것은 예를 들어 bean이 잃어버리거나 유효하지 않은 프라퍼티의 결과처럼 예외를 던진다면 발생할수 있다. 몇몇 설정사항의 잠재적으로 늦은(delayed) 가시성(visibility)은 디폴트에 의해 ApplicationContext이 싱글톤 bean을 미리 인스턴스화하기 때문이다. 그것들이 실질적으로 필요하기 전에 그 bean을 생성하기 위한 몇몇 선행 시간과 메모리의 비용에서 당신은 나중이 아닌 ApplicationContext이 생성될때 설정사항에 대해 찾는다. 만약 당신이 바란다면 당신은 이 디폴트 행위를 오버라이드 할수 있고 늦은 로드(미리 인스턴스화되는것이 아닌)를 위한 싱글톤 bean을 셋팅할수 있다.

몇몇 예제들

첫번째 setter-기반 의존성 삽입을 위해 BeanFactory를 사용하는 예제. 아래는 몇몇 bean정의를 명시하는 XmlBeanFactory설정 파일의 작은 일부이다. 다음은 실질적으로 핵심적인 bean자체를 위한 코드이다. 선호하는 setter가 선언된것을 보자.

<bean id="exampleBean" class="examples.ExampleBean">
    <property name="beanOne"><ref bean="anotherExampleBean"/></property>
    <property name="beanTwo"><ref bean="yetAnotherBean"/></property>
    <property name="integerProperty"><value>1</value></property>
</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파일내 명시되는 프라퍼티에 대응하기 위해 선언된다. (XML파일의 프라퍼티는 RootBeanDefinition로부터 PropertyValues객체와 직접적으로 관련된다.)

IoC타입 3(생성자-기반 의존성 삽입)을 위해 BeanFactory를 사용하는 예제. 아래는 생성자의 인자와 실질적인 bean코드를 명시하는 XML설정의 작은 조각이다. 생성자를 보자.

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg><ref bean="anotherExampleBean"/></constructor-arg>
    <constructor-arg><ref bean="yetAnotherBean"/></constructor-arg>
    <constructor-arg type="int"><value>1</value></constructor-arg>
</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속성의 사용을 제외하고)로 사용되기 때문에 여기서는 상세하기 다루지 않을것이다.

3.3.2. 생성자의 인자 분석

생성자의 인자 분석 대응(matching)은 인자타입을 사용할때 발생한다. 다른 bean이 참조될때 타입은 알려지고 대응은 발생한다. <value>true<value>와 같은 간단한 타입이 사용될때 Spring은 값의 타입을 결정할수 없고 도움 없이는 타입에 의해 대응(match)할수 없다. 두개의 부분을 위해 사용된 다음의 클래스를 검토하라.

package examples;

public class ExampleBean {

    private int years;             //No. of years to the calculate the Ultimate Answer
    private String ultimateAnswer; //The Answer to Life, the Universe, and Everything

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

3.3.2.1. 생성자의 인자 타입 대응(match)

위 시나리오는 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> 

3.3.2.2. 생성자의 인자 인덱스

생성자의 인자는 index 속성을 사용하여 명확하게 명시된 인덱스를 가질수 있다. 예를 들면.

<bean id="exampleBean" class="examples.ExampleBean">
     <constructor-arg index="0"><value>7500000</value></constructor-arg>
     <constructor-arg index="1"><value>42</value></constructor-arg>
</bean> 

여러개의 간단한 값들의 모호한 문제를 푸는것에 더하여 인덱스를 명시하는 것은 생성자가 같은 타입의 두개의 인자를 가지는 모호함의 문제도 해결한다. 인덱스는 0 부터 시작된다는것에 주의하라.

생성자의 인자 인덱스를 명시하는것은 생성자 IoC를 수행하는 방법이 선호된다.

3.3.3. bean프라퍼티와 상세화된 생성자의 인자

앞 부분에서 언급된것처럼 bean프라퍼티와 생성자의 인자는 다른 관리빈(협력자), 또는 인라인으로 명시된 값의 참조처럼 명시될수 있다. XmlBeanFactory는 이러한 목적을 위해 propertyconstructor-arg내 많은 수의 하위요소타입을 지원한다.

value 요소는 사람이 읽을수 있는 문자열 표현처럼 프라퍼티나 생성자의 인자를 명시한다. 앞서 상세하게 언급된것처럼 자바빈 PropertyEditors는 java.lang.String로 부터 문자열값을 실질적인 프라퍼티나 인자타입으로 변환하기 위해 사용된다.

<beans>
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <!-- 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>
</beans> 

null 요소는 null값을 다루기 위해 사용된다. Spring은 프라퍼티를 위한 빈 인자를 빈 문자열처럼 처리한다. 다음은 XmlBeanFactory설정이다.

<bean class="ExampleBean">
    <property name="email"><value></value></property>
</bean>        

email프라퍼티내 결과는 ""으로 셋팅되고 자바코드 exampleBean.setEmail("") 와 동일하다. 특별한 <null> 요소는 아마도 null값을 표시하는데 사용될것이다.

<bean class="ExampleBean">
    <property name="email"><null/></property>
</bean>        

이것은 자바코드 exampleBean.setEmail(null) 와 동일하다.

list, set, map, 그리고 props 요소는 명시하고 셋팅되기 위한 프라퍼티와 자바타입 List, Set, Map, 그리고 Properties 의 인자를 허용한다.

<beans>
    ...
    <bean id="moreComplexObject" class="example.ComplexObject">
        <!-- results in a setPeople(java.util.Properties) call -->
        <property name="people">
            <props>
                <prop key="HarryPotter">The magic property</prop>
                <prop key="JerrySeinfeld">The funny property</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="yup an entry">
                    <value>just some string</value>
                </entry>
                <entry key="yup a ref">
                    <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>
</beans>

map의 값이나 set값은 어느 요소도 될수 있다는 것을 알라.

(bean | ref | idref | list | set | map | props | value | null)

property 요소내 bean 요소는 BeanFactory내 명시되는 bean을 위한 참조대신에 bean값을 인라인으로 명시하기 위해 사용된다. 인라인 bean정의는 명시되는 어떠한 id로 필요로 하지 않는다.

<bean id="outer" class="...">
    <!-- Instead of using a reference to target, just use an inner bean -->
    <property name="target">
        <bean class="com.mycompany.PersonImpl">
            <property name="name"><value>Tony</value></property>
            <property name="age"><value>51</value></property>
        </bean>
   </property>
</bean>

idref 요소는 컨테이너내 다른 bean의 문자열 idname으로 프라퍼티를 셋팅하기 위한 짧고(shorthand) 에러를 검사(error-proof)하는 방법이다.

<bean id="theTargetBean" class="...">
</bean>
<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>

이것은 수행시 다음의 조각들과 동일하다.

<bean id="theTargetBean" class="...">
</bean>
<bean id="theClientBean" class="...">
    <property name="targetName">
        <value>theTargetBean</value>
    </property>
</bean>

첫번째 형태가 두번째를 선호하는 가장 중요한 이유는 idref 태그를 사용하는 것이 Spring이 다른 bean이 실질적으로 존재하는 배치시점에 유효하도록 허용한다는것이다. 두번째 에서 targetName프라퍼티의 클래스는 자기 자신이 유효하도록 강제하고 클래스가 Spring에 의해 가능하면 컨테이너가 실질적으로 배치한 후에 실질적으로 인스턴스화될때 발생할것이다.

추가적으로 만약 참조되는 bean이 같은 XML파일내에 있고 bean이름이 bean id라면 사용될 local 속성은 XML문서를 파싱하는 시점에 좀더 일찍 XML파서가 bean이름을 자체적으로 체크하는것을 허용할것이다.

    <property name="targetName">
        <idref local="theTargetBean"/>
    </property>

ref 요소는 property정의 요소내에서 허용되는 마지막 요소이다. 이것은 컨테이너나 협력자에 의해 관리되는 다른 bean을 위해 참조되기 위한 명시된 프라퍼티의 값을 셋팅하는데 사용된다. 앞 부분에서 언급했던것 처럼 참조되는 bean은 프라퍼티가 셋팅되는 bean의 의존성이 되는것이 검토되고 프라퍼티가 셋팅되기 전에 필요(만약 이것이 싱글톤 bean이라면 이것은 컨테이너에 의해 이미 초기화되었을것이다.)하다면 요구에 의해 초기화될것이다. 모든 참조는 궁극적으로 다른 객체에 대한 참조이지만 다른 객체의 id/name을 명시하는 방법은 3가지가 있다.

ref 태그의 bean 속성을 사용하여 대상 bean을 명시하는것이 가장 일반적인 형태이고 같은 BeanFactory/ApplicationContext(같은 XML파일이든 아니든)나 부모 BeanFactory/ApplicationContext내에서 어떠한 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을 명시하는 것은 현재 BeanFactory(나 ApplicationContext)의 부모 BeanFactory(나 ApplicationContext)내 있을 bean을 위해 생성될 참조를 허용한다. parent 속성의 값은 아마 대상 bean의 id 속성이나 name 속성내 값중에 하나와 같을것이고 대상 bean은 최근것을 위해 부모 BeanFactory 나 ApplicationContext내 있어야만 한다. bean참조 형태의 가장 중요한 사용은 몇몇 프록시의 순서대로 부모 컨텍스트내 존재하는 bean을 포장하기 위해 필요할때이고 초기의 객체는 그것을 포장하기 위해 필요하다.

    <ref parent="someBean"/>

3.3.3.1. value와 ref 간략화한(shortcut) 폼

이것은 value이나 bean참조를 설정하기 위해 필요한 공통사항이다. 완전한 형태의 valueref를 사용하는것보다 다소 덜 장황하게 간략화한 몇가지 형태가 존재한다. 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"/>

대개 손으로 정의를 작성할때 당신은 다소 덜 장황한 간략화된 형태를 사용하는것을 선호할것이다.

propertyconstructor-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-refvalue-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"> 를 위한 간략화된 형태는 없다.

3.3.4. 메소드 삽입

대부분의 사용자를 위해 컨테이너내 대부분의 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") 호출을 통해 컨테이너에게 요청하기 위한 프로그램마다 다른 방법을 사용한다. 이것은 bean코드가 인식을 하고 Spring에 커플링이 되기 때문에 대개 바람직한 해결법은 아니다.

BeanFactory의 향상된 기능인 메소드 삽입은 몇몇 다른 시나리오에 따라 깔끔한 형태로 다루어지는 사용 상황을 허용한다.

3.3.4.1. 룩업(Lookup) 메소드 삽입

룩업 메소드 삽입은 컨테이너내 다른 명명된 bean를 룩업하는 결과를 반환하는 컨테이너내 관리빈의 추상및 구현된 메소드를 오버라이드하기 위해 컨테이너의 기능을 적용한다. 룩업은 위에서 언급(비록 싱글톤이 될수 있더라도)된 시나리오마다 비-싱글톤 bean이 될것이다. Spring은 CGLIB 라이브러리를 통해 바이트코드 생성을 사용하여 동적으로 생성된 하위클래스가 메소드를 오버라이드하는것을 통해 이것을 구현한다.

삽입되어야 할 메소드를 포함하는 클라이언트 클래스내에서 메소드 정의는 이 형태로 추상(또는 구현된) 정의가 되어야만 한다.

protected abstract SingleShotHelper createSingleShotHelper();

만약 메소드가 추상적이지 않다면 Spring은 존재하는 구현물을 간단하게 오버라이드 할것이다. XmlBeanFactory의 경우 당신은 bean정의내 lookup-method요소를 사용해서 컨테이너로부터 특정 bean을 반환하기 위한 이 메소드를 삽입/오버라이드 하도록 Spring에게 지시한다. 예를 들면

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="singleShotHelper class="..." singleton="false">
</bean>

<!-- myBean uses singleShotHelper -->
<bean id="myBean" class="...">
  <lookup-method name="createSingleShotHelper"
                 bean="singleShotHelper"/>
  <property>
    ...
  </property>
</bean>

myBean처럼 구분되는 bean은 이것이 singleShotHelper bean의 새로운 인스턴스를 필요로 할때마다 그것 자신의 메소드인 createSingleShotHelper을 호출할것이다. bean을 배치하는 사람은 비-싱글톤(그것이 실질적으로 필요한 것이라면)처럼 singleShotHelper를 배치하기 위해 주의해야 한다고 알리는것이 중요하다. 만약 싱글톤(명시적이거나 이 플래그를 위해 디폴트로 true셋팅한 것에 의존하여)처럼 배치되었다면 singleShotHelper의 같은 인스턴스는 매번 반환될것이다 !

룩업 메소드 삽입은 생성자 삽입(생성되는 bean을 위한 선택사항인 생성자의 인자를 제공하는)과 함께 조합될수 있고 또한 setter삽입(생성되는 bean의 프라퍼티를 셋팅하는)과도 조합될수 있다.

3.3.4.2. 임의의 메소드 교체

룩업 메소드 삽입보다 메소드 삽입의 다소 덜 공통적으로 유용한 형태는 다른 메소드 구현물을 가진 관리빈내에 임의의 메소드를 교체하는 기능이다. 사용자는 이 기능이 실질적으로 필요할때까지 이 부분의 나머지(향상된 기능에 대해 언급하는)를 생략할수 있다.

XmlBeanFactory에서 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">
</bean>

replaced-method 요소내 하나 이상이 포함된 arg-type 요소는 오버라이드된 메소드의 메소드 시그너처를 표시하기 위해 사용된다. 인자를 위한 시그너처는 메소드가 실질적으로 오버로드되고 클래스내 몇가지 종류가 되는 경우에만 필요하다. 편의상 인자를 위한 문자열 타입은 완전한 형태의 타입명의 일부가 된다. 예를 들면 다음은 java.lang.String과 대응된다.

    java.lang.String
    String
    Str

많은 수의 인자가 종종 각각의 가능한 선택들 사이에 구별하기 충분하기 때문에 이 간략화된 형태는 인자에 대응될 가장 짧은 문자열을 사용하여 많은 타입을 저장할수 있다.

3.3.5. depends-on 사용하기

대부분의 상황을 위해 bean이 다른 것들의 의존성이라는 사실은 하나의 bean이 다른것의 프라퍼티처럼 셋팅한다는 사실에 의해 간단하게 표현된다. 이것은 XmlBeanFactory내 ref 요소를 가지고 수행한다. 이것의 다양한 종류에서 때때로 컨테이너를 인식하는 bean은 간단하게 주어진 의존성(문자열 값이나 문자열 값과 같은것을 평가하는 idref 요소의 대안을 사용하여)의 id이다. 첫번째 bean은 이것의 의존성을 위해 컨테이너에 프로그램마다 다른 방식으로 요청한다. 어느 경우에나 의존성은 의존적인 bean이전에 초기화된다.

다소 덜 직접적인(예를 들면, 데이터베이스 드라이버 등록과 같은 클래스내 정적인 초기자가 트리거 될 필요가 있을때) bean들 사이의 의존성이 있는 비교적 드물게 발생하는 상황을 위해 depends-on 요소는 이 초기화된 요소를 사용하는 bean이전에 초기화되기 위한 하나 이상의 bean을 명시적으로 강제하기 위해 사용된다.

다음은 예제설정이다.

<bean id="beanOne" class="ExampleBean" depends-on="manager">
    <property name="manager"><ref local="manager"/></property>
</bean>

<bean id="manager" class="ManagerBean"/>

3.3.6. Autowiring 협력자

BeanFactory는 협력자 bean들 사이의 관계를 autowire 할수 있다. 이것은 BeanFactory의 내용을 조사함으로써 당신의 bean을 위해 Spring이 자동적으로 협력자(다른 bean)를 분석하는것이 가능하다는 것을 의미한다. autowiring 기능은 5개의 모드를 가진다. Autowiring은 다른 bean이 autowire되지 않는 동안 bean마다 명시되고 몇몇 bean을 위해 가능하게 될수 있다. autowiring을 사용하면 명백하게 많은 양의 타이핑을 줄이고 프라퍼티나 생성자의인자를 명시할 필요를 줄이거나 제거하는것이 가능하다. [1]XmlBeanFactory에서 bean정의를 위한 autowire모드는 bean요소의 autowire 속성을 사용하여 명시한다. 다음의 값이 허용된다.

Table 3.2. Autowiring 모드

모드설명
noautowiring이 전혀없다. bean참조는 ref 요소를 통해 정의되어야만 한다. 이 값은 디폴트이고 좀더 큰 제어와 명백함을 주는 협력자를 명시하기 때문에 좀더 큰 배치를 위해 이것이 억제되도록 변경한다. 몇몇 확장을 위해 이것은 시스템의 구조에 대한 문서의 형태이다.
byName프라퍼티 이름에 의한 Autowiring. 이 옵션은 BeanFactory를 조사하고 autowire될 필요가 있는 프라퍼티와 같은 이름의 bean을 찾는다. 예를 들면 당신이 만약 이름에 의해 autowire하기 위해 셋팅하는 bean정의를 가지고 이것이 master 프라퍼티(이것은 setMaster(...) 메소드를 가진다.)를 포함한다면 Spring은 master라는 이름의 bean정의를 찾을것이고 프라퍼티를 셋팅하기 위해 이것을 사용한다.
byTypeBeanFactory내 프라퍼티 타입의 bean이 정확하게 하나 있다면 프라퍼티를 autowire가 되도록 허용한다. 만약 하나 이상이 있다면 치명적인 예외가 던져지고 이것은 bean을 위한 byType autowiring을 사용하지 않는것을 나타낸다. 만약 대응되는 bean이 없다면 아무것도 발생하지 않는다(프라퍼티가 셋팅되지 않는다.). 만약 이 기능에 호감이 가지 않는다면 이 경우에 던져질 에러를 명시하는 dependency-check="objects" 속성값을 셋팅한다.
constructor이것은 byType와 유사하지만 생성자의 인자에 적용한다. bean factory내 생성자의 인자타입의 bean이 정확하게 하나가 아닐경우 치명적인 에러가 발생한다.
autodetectbean클래스의 내성을 통해 constructorbyType를 선택하라. 만약 디폴트 생성자가 발견된다면 byType는 적용된다.

propertyconstructor-arg내 명싱적인 의존성이 언제나 autowiring을 오버라이드한다는것에 주의하라. Autowire 행위는 모든 autowiring가 완성된후 수행될 의존성 체크와 조합될수 있다.

autowiring 주위의 pros와 cons를 이해하는것이 중요하다. 다음은 autowiring의 몇몇 장점이다.

  • 이것은 요구되는 설정의 양을 명백하게 감소시킨다.(어쨌든 이 장 어디서든 언급되는 설정 "template"의 사용같은 기법은 여기서도 가치있다. )

  • 당신의 객체가 발전하는것처럼 그것 자체를 최신식으로 유지하는 설정을 야기한다. 예를 들면 만약 당신이 클래스에 추가적으로 의존성을 추가할 필요가 있다면 그 의존성은 설정을 변경할 필요없이 자동적으로 만족될수 있다. 게다가 배치하는 동안 코드기초가 좀더 안정화가 될때 명시적으로 wiring하기 위한 교체의 옵션이 없이 autowiring을 위해 견고한 경우가 된다.

autowiring 의 몇몇 단점

  • 명시적인 wiring보다는 좀더 마법적같다. 비록 위 테이블에서 언급된것처럼 Spring은 기대되지 않는 결과를 가지는 모호함과 같은 경우에 추측을 파하기 위해 주의한다. 당신의 Spring관리 객체들 간의 관계는 더 이상 명시적으로 문서화되지 않는다.

  • wiring정보는 아마도 Spring애플리케이션 컨텍스트로부터 문서를 생성하는 툴을 위해 사용가능하지는 않을것이다.

  • type에 의한 autowiring은 setter메소드나 생성자의 인자에 의해 명시되는 타입의 하나의 bean정의가 있을때만 작동할것이다. 당신은 어떠한 잠재적인 모호함이 있을경우 명시적인 wiring을 사용할 필요가 있다.

모든 경우에 "틀리다(wrong)" 나 "맞다(right)"가 답은 아니다. 우리는 프로젝트를 통한 일관성(consistency)의 정도(degree)를 추천한다. 예를 들면 autowiring이 대개 사용되지 않을때 이것은 개발자에게 하나또는 두개의 bean정의를 사용하는것에 혼동을 줄지도 모른다.

3.3.7. 의존성을 위한 체크

Spring은 BeanFactory로 배치되는 bean의 분석되지 않은 의존성의 존재를 체크하도록 시도하는 능력을 가진다. 그것들은 bean정의내 그것들을 위한 실제값을 셋팅하지 않거나 autowiring기능에 의해 자동적으로 제공되는 bean의 자바빈 프라퍼티이다.

이 기능은 모든 프라퍼티(또는 특정 타입의 모든 프라퍼티)가 bean에 셋팅되는지 확인하기를 원할때 때때로 유용하다. 물론 많은 경우에 bean클래스는 많은 프라퍼티 또는 모든 사용시나리오를 위해 적용하지 않는 몇몇 프라퍼티를 위한 디폴트 값을 가질것이다. 그래서 이 기능은 제한적으로 사용가능하다. 의존성체크는 autowiring기능처럼 bean단위로 사용가능하거나 사용불가능하다. 디폴트는 의존성을 체크하지 않는 것이다. 의존성체크는 다양한 모드로 다루어질수 있다. XmlBeanFactory에서 이것은 bean정의내 dependency-check속성을 통해 명시되고 다음의 값을 가진다.

Table 3.3. 의존성체크 모드

모드설명
none의존성 체크가 없다. 그것들을 위해 명시되는 값이 없는 bean의 프라퍼티가 간단하게 셋팅하지 않는다.
simple원시타입과 collection(다른 beanㄷ처럼 협력자를 제외한 모든 것)을 위해 수행되는 의존성 체크.
object협력자를 위해 수행되는 의존성 체크.
all협력자, 원시타입 그리고 collection을 위해 수행되는 의존성 체크.

3.4. bean의 성질을 커스터마이징하기.

3.4.1. Lifecycle 인터페이스

Spring은 BeanFactory내 당신의 bean 행위를 변경하기 위한 다양한 표시자(marker)인터페이스를 제공한다. 그것들은 InitializingBeanDisposableBean를 포함한다. 이 인터페이스를 구현하는 것은 bean에게 초기화와 파괴화(destruction)의 작업을 수행하도록 허용하는 전자를 위해 afterPropertiesSet()을 후자를 위해 destroy()를 호출함으로써 BeanFactory내 결과를 생성한다.

내부적으로 Spring은 이것이 적당한 메소드를 찾고 호출할수 있는 어떠한 표시자(marker) 인터페이스를 처리하기 위해 BeanPostProcessors를 사용한다. 만약 Spring이 특별히 제공하지 않는 사용자 지정 기능이나 다른 생명주기 행위가 필요하다면 당신은 BeanPostProcessor를 구현할수 있다. 이것에 대한 좀더 상세한 정보는 Section 3.7, “BeanPostprocessors로 bean 커스터마이징하기”에서 찾을수 있다.

모든 다른 종류의 생명주기 표시자(marker)인터페이스는 아래에서 언급된다. 추가물중 하나에서 당신은 Spring이 bean을 어떻게 관리하고 그러한 생명주기 기능들이 당신의 bean의 성질을 어떻게 변경하고 그들이 어떻게 관리되는지 보여주는 다이어그램을 찾을수 있다.

3.4.1.1. InitializingBean / init-method

org.springframework.beans.factory.InitializingBean을 구현하는것은 bean의 필요한 모든 프라퍼티가 BeanFactory에 의해 셋팅된 후 bean에게 초기화작업을 수행하는것을 허용한다. InitializingBean인터페이스는 정확하게 하나의 메소드만 명시한다.

    * Invoked by a BeanFactory after it has set all bean properties supplied
    * (and satisfied BeanFactoryAware and ApplicationContextAware).
    * <p>This method allows the bean instance to perform initialization only
    * possible when all bean properties have been set and to throw an
    * exception in the event of misconfiguration.
    * @throws Exception in the event of misconfiguration (such
    * as failure to set an essential property) or if initialization fails.
    */
    void afterPropertiesSet() throws Exception;

메모 : 대개 InitializingBean 표시자(marker) 인터페이스의 사용은 제거될수 있다. (그리고 Spring에 코드를 불필요하게 결합한 후 억제된다.). bean정의는 명시되기 위한 일반적인 초기화 메소드를 위한 지원을 제공한다. XmlBeanFactory의 경우, 이것은 init-method 속성을 통해 수행된다. 예를 들면, 다음의 정의처럼.

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>

public class ExampleBean {
    public void init() {
        // do some initialization work
    }
}

는 다음과 정확하게 같다.

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>

public class AnotherExampleBean implements InitializingBean {
    public void afterPropertiesSet() {
        // do some initialization work
    }
}

하지만 Spring에 코드를 결합하지는 않는다.

3.4.1.2. DisposableBean / destroy-method

org.springframework.beans.factory.DisposableBean를 구현하는것은 BeanFactory가 파괴된(destroyed)것을 포함할때 bean에게 콜백을 얻는 것을 허용한다. DisposableBean인터페이스는 하나의 메소드를 명시한다.

    /**
    * Invoked by a BeanFactory on destruction of a singleton.
    * @throws Exception in case of shutdown errors.
    * Exceptions will get logged but not re-thrown to allow
    * other beans to release their resources too.
    */
    void destroy() throws Exception;

메모 : DisposableBean 표시자(marker) 인터페이스의 사용은 제거될수 있다. (그리고 Spring에 코드를 불필요하게 결합한 후 억제된다.). bean정의는 명시되기 위한 일반적인 파괴(destroy) 메소드를 위한 지원을 제공한다. XmlBeanFactory의 경우에, 이것은 destroy-method 속성을 통해 수행된다. 예를 들면, 다음의 정의처럼.

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>

public class ExampleBean {
    public void cleanup() {
        // do some destruction work (like closing connection)
    }
}

는 다음과 정확하게 같다.

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>

public class AnotherExampleBean implements DisposableBean {
    public void destroy() {
        // do some destruction work
    }
}

하지만 Spring에 코드를 결합하지는 않는다.

중요한 메모 : 프로토타입 모드로 bean을 배치할때, bean의 생명주기는 미세한게 변경된다. 정의에 의해 Spring은 이것이 생성된 이후 non-singleton/prototype bean의 완벽한 생명주기를 관리할수는 없다. 클라이언트와 컨테이너에 주어진 것은 더이상 추적하지 않는다. 당신은 'new' 연산자를 위한 대체물처럼 non-singleton/prototype bean에 대해 이야기 할때 Spring의 역활에 대해 생각할수 있다. 어떤 생명주기 형상은 클라이언트에 의해 다루어질수 있는 지점을 지난다. BeanFactory내 bean의 생명주기는 Section 3.4.1, “Lifecycle 인터페이스”에서 좀더 상세하게 언급된다. .

3.4.2. 당신이 누구인지 알고 있다.(Knowing who you are)

3.4.2.1. BeanFactoryAware

org.springframework.beans.factory.BeanFactoryAware인터페이스를 구현하는 클래스는 BeanFactory에 의해 생성되었을때 이것을 생성하는 BeanFactory에 대한 참조를 제공한다.

public interface BeanFactoryAware {
   /**
    * Callback that supplies the owning factory to a bean instance.
    * <p>Invoked after population of normal bean properties but before an init
    * callback like InitializingBean's afterPropertiesSet or a custom init-method.
    * @param beanFactory owning BeanFactory (may not be null).
    * The bean can immediately call methods on the factory.
    * @throws BeansException in case of initialization errors
    * @see BeanInitializationException
    */
    void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

이것은 bean에게 org.springframework.beans.factory.BeanFactory인터페이스를 통하거나 추가적인 기능을 드러내는 이것의 알려진 하위클래스에 대한 참조를 형변환함으로써 프로그램마다 다르게 그것들을 생성한 BeanFactory를 변경하는것을 허용한다. 원래 이것은 다른 bean의 프로그램마다 다른 검색으로 구성된다. 이 기능이 유용할때 이것이 Spring에 코드를 결합하고 Inversion of Control스타일을 따르지 않는 이 후 프라퍼티처럼 bean에 제공되는 협력자가 위치한 곳에 이것이 대개 제거될수 있다.

3.4.2.2. BeanNameAware

만약 bean이 org.springframework.beans.factory.BeanNameAware인터페이스를 구현하고 BeanFactory내 배치된다면 BeanFactory는 이것이 배치된 id의 bean을 알리기 위한 인터페이스를 통해 bean을 호출할것이다. 콜백은 일반적인 bean프라퍼티의 활성화 이후지만 InitializingBeanafterPropertiesSet 이나 사용자 지정 init-method같은 콜백을 초기화하기 전에 호출될것이다.

3.4.3. FactoryBean

org.springframework.beans.factory.FactoryBean인터페이스는 자체적으로 factory인 객체에 의해 구현되는 것이다. BeanFactory 인터페이스는 3개의 메소드를 제공한다.

  • Object getObject(): 이 factory가 생성하는 객체의 인스턴스를 반환한다. 인스턴스는 공유될수(이 factory가 싱글톤이나 프로토타입을 반환하는지에 대한 여부에 의존하여) 있다.

  • boolean isSingleton(): 만약 이 FactoryBean이 싱글톤을 반환한다면 true를 반환하고 다른경우라면 false를 반환한다.

  • Class getObjectType(): getObject() 메소드에 의해 반환되는 객체 타입이나 타입이 미리 알려지지 않았다면 null을 반환한다.

3.5. 추상 그리고 자식 bean정의

bean정의는 잠재적으로 컨테이너 특정 정보(이를 테면, 초기화 메소드, 정적 factory 메소드명, 등등)와 생성자의 인자와 프라퍼티 값을 포함하는 많은 양의 설정정보를 포함한다. 자식 bean정의는 부모 정의로부터 설정정보를 상속하는 bean정의이다. 이것은 필요하다면 몇몇값을 오버라이드하거나 다른것을 추가할수 있다. 부모와 자식 bean정의를 사용하는것은 잠재적으로 많은 양의 타이핑을 줄일수 있다. 효과적으로 이것은 템플릿형태이다.

프로그램마다 다르게 BeanFactory로 작업을 수행할때 자식 bean정의는 ChildBeanDefinition 클래스에 의해 표현된다. 대부분의 사용자는 XmlBeanFactory와 같은 몇가지내에서 선언적인 설정 bean정의대신에 이 수준에서 그것들과 함께 작동하지는 않을것이다. XmlBeanFactory bean정의에서 자식 bean정의는 이 속성의 값처럼 부모 bean을 명시하는 parent 속성을 사용하여 간단하게 표시될수 있다.

<bean id="inheritedTestBean" abstract="true"
    class="org.springframework.beans.TestBean">
    <property name="name"><value>parent</value></property>
    <property name="age"><value>1</value></property>
</bean>

<bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean"
      parent="inheritedTestBean" init-method="initialize">
    <property name="name"><value>override</value></property>
    <!-- age should inherit value of 1 from parent -->
  </bean>

자식 bean정의는 아무것도 명시가 되어 있지 않지만 이것을 오버라이드할수 있다면 부모 정의의 bean클래스를 사용할것이다. 후자의 경우 자식 bean클래스는 부모 bean클래스와 호환되어야만 한다. 이를 테면 부모의 프라퍼티 값을 받을수 있어야 한다.

자식 bean정의는 생성자의 인자값, 프라퍼티값과 부모로 부터 상속된 메소드를 새로운 값을 추가하는 선택사항과 함께 상속할것이다. 만약 메소드를 초기화한다면 destroy메소드와/또는 정적 factory메소드는 명시된다. 그것들은 관련된 부모 셋팅을 오버라이드할것이다.

남은 셋팅들은 언제나 자식 정의로부터 가져올것이다.: depends on, autowire mode, dependency check, singleton, lazy init.

위 예제에서 우리는 abstract 속성을 사용하여 추상적으로 부모 bean정의를 명시적으로 표시했다는 것을 알라. 이 경우 부모 정의는 클래스를 명시하지 않는다.

<bean id="inheritedTestBeanWithoutClass">
    <property name="name"><value>parent</value></property>
    <property name="age"><value>1</value></property>
</bean>

<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
      parent="inheritedTestBeanWithoutClass" init-method="initialize">
    <property name="name"><value>override</value></property>
    <!-- age should inherit value of 1 from parent -->
</bean>

부모 bean은 불완전하고 또한 추상적이라고 생각된 이후에는 인스턴스화될수 없다. 정의가 이것처럼(명시적이거나 함축적인) 추상적이라고 생각될 때 이것은 자식 정의를 위한 부모 정의처럼 제공될 순수한 템플릿이나 추상 bean정의처럼 사용가능하다. 그것 자체(다른 bean의 ref프라퍼티를 참조하거나 부모 bean id를 가진 명시적인 getBean()호출을 하여)의 추상적인 부모 bean들을 사용하는것을 시도하면 에러를 보게될것이다. 유사하게도 컨테이너의 내부적인 preInstantiateSingletons 메소드는 추상적이라고 생각되는 bean정의를 완벽하게 무시할것이다.

중요한 메모: 애플리케이션 컨텍스트(간단한 bean factory가 아닌)는 디폴트에 의해 모든 싱글톤으로 미리 인스턴스화될것이다. 그러므로 이것은 만약 당신이 템플릿처럼만 오직 사용되는 경향이 있는 (부모) bean정의를 가지고 이 정의가 클래스를 명시한다면 당신은 abstract속성값을 true로 셋팅해야만 하는 반면에 애플리케이션 컨텍스트는 이것을 실질적으로 미리 인스턴스화할것이라는 것은 중요(적어도 싱글톤 bean을 위해서)하다.

3.6. BeanFactory와 상호작동하기

BeanFactory는 기본적으로 다른 bean과 그것들의 의존성의 등록을 유지하는 향상된 factory능력을 위한 인터페이스에 지나지 않는다. BeanFactory는 당신에게 bean factory를 사용하여 bean정의를 읽고 그것들에 접근하는 것을 가능하게 한다. BeanFactory를 사용할때 당신은 다음처럼 하나를 생성하고 XML형태의 몇몇 bean정의내에서 읽을것이다.

InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);

getBean(String)를 사용하여 당신은 당신의 bean인스턴스를 가져올수 있다. 당신은 이것을 싱글톤(디폴트)처럼 이것을 명시하여 같은 bean의 참조를 얻거나 singletonfalse로 셋팅한다면 매번 새로운 인스턴스를 얻을것이다. BeanFactory의 클라이언트측 시각은 놀라울정도로 간단한다. BeanFactory 인터페이스는 호출할 클라이언트를 위해 오직 5개의 메소드만을 가진다.

  • 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) 메소드의 모든 규칙(위에서 본)을 적용한다.

  • boolean isSingleton(String): 주어진 이름하에 등록된 bean정의나 bean인스턴스가 싱글톤이거나 프로토타입인지 아닌지 조사한다. 만약 주어진 이름에 관련된 bean이 발견되지 않는다면 예외(NoSuchBeanDefinitionException)가 던져질것이다.

  • String[] getAliases(String): 만약 bean정의내 어떠한 것도 명시되어 있다면 주어진 bean이름을 위한 별칭을 반환한다.

3.6.1. BeanFactory의 생성물이 아닌 FactoryBean 얻기

때때로 BeanFactory가 생성하는 bean이 아닌 실질적인 FactoryBean인스턴스 자체를 위해 BeanFactory에 요청할 필요가 있다. 이것은 BeanFactory(ApplicationContext을 포함하는)의 getBean 메소드를 호출할때 &를 가진 bean id를 덧붙여서 수행된다. 그래서 id myBean를 가진 주어진 FactoryBean을 위해 BeanFactory의 getBean("myBean")를 호출하는것은 FactoryBean의 생성물을 반환할것이지만 getBean("&myBean")을 호출하는것은 FactoryBean인스턴스 자체를 반환할것이다.

3.7. BeanPostprocessors로 bean 커스터마이징하기

bean 후-처리자는 두개의 콜백메소드로 구성된 org.springframework.beans.factory.config.BeanPostProcessor인터페이스를 구현하는 자바클래스이다. 그러한 클래스가 BeanFactory에 의해 생성되는 각각의 bean인스턴스를 위해 BeanFactory와 함께 후-처리자처럼 등록되었을때 후-처리자는 어떠한 초기화 메소드(afterPropertiesSet와 어떤 선언된 초기화 메소드)가 호출되기 전과 나중에 BeanFactory로 부터 콜백을 얻을것이다. 후-처리자는 콜백을 완벽하게 무시하는것을 포함해서 bean으로 바라는것을 하는데 자유롭다. bean 후-처리자는 표시자(marker) 인터페이스를 체크하거나 프록시로 bean을 포장하는 것과 같은것을 수행한다. 몇몇 Spring 헬퍼(helper)클래스는 bean 후-처리자처럼 구현되었다.

BeanFactory가 ApplicationContext보다 미세할 정도로 다르게 bean 후-처리자를 처리하는것을 아는것은 중요하다. ApplicationContext는 BeanPostProcessor 인터페이스를 구현하고 bean생성하는 factory에 의해 적당히 호출되기 위한 후-처리자처럼 그것들을 등록하는 것으로 배치되는 어떠한 bean을 자동적으로 감지할것이다. 어떤 다른 bean으로 유사한 형태로 후-처리자를 배치 할 필요만 있다. 반면에 명백한 BeanFactory를 사용할때 bean 후-처리자는 다음같은 코드순으로 명시적으로 등록되어야 한다.

ConfigurableBeanFactory bf = new .....;     // create BeanFactory
   ...                       // now register some beans
// now register any needed BeanPostProcessors
MyBeanPostProcessor pp = new MyBeanPostProcessor();
bf.addBeanPostProcessor(pp);

// now start using the factory
  ...

이 수작업(manual) 등록 단계는 편리하지 않고 ApplictionContexts는 BeanFactory의 기능적으로 수퍼셋이기 때문에 bean 후-처리자가 필요할 때 사용되는 ApplicationContext종류가 대개 추천된다.

3.8. BeanFactoryPostprocessors를 가진 bean factory커스터마이징하기

bean factory 후-처리자는 org.springframework.beans.factory.config.BeanFactoryPostProcessor 인터페이스를 구현하는 자바클래스이다. 이것은 생성된 후 전체 BeanFactory를 위한 몇몇 종류의 변경을 적용하기 위해 수동( BeanFactory의 경우)이나 자동(ApplicationContext의 경우)으로 수행된다. Spring은 밑에서 언급되는 PropertyResourceConfigurerPropertyPlaceHolderConfigurer 그리고 이 문서 나중에 언급되는것처럼 다른 bean을 트랜잭션하게 포장하거나 다른 종류의 프록시와 매우 유용한 BeanNameAutoProxyCreator와 같은 많은 수의 미리존재하는 bean factory 후-처리자를 포함한다. BeanFactoryPostProcessor는 사용자지정 편집기(Section 3.9, “추가적인 사용자지정 PropertyEditors 등록하기”에서 언급되는것처럼)를 추가하기 위해 사용될수 있다.

BeanFactory내에서 BeanFactoryPostProcessor 적용의 처리는 수동이고 이것과 유사할것이다.

XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
// create placeholderconfigurer to 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);

ApplicationContext는 BeanFactoryPostProcessor 인터페이스를 구현하는 것으로 배치되는 어떠한 bean을 감지할것이고 적당한 시점에 bean factory 후-처리자처럼 자동적으로 그것들을 사용한다. 다른 bean을 위한 유사한 형태로 이러한 후-처리자를 배치만하는것이다.

이 수작업(manual) 등록 단계는 편리하지 않고 ApplictionContexts는 BeanFactory의 기능적으로 수퍼셋이기 때문에 bean factory 후-처리자가 필요할 때 사용되는 ApplicationContext종류가 대개 추천된다.

3.8.1. PropertyPlaceholderConfigurer

bean factory 후-처리자처럼 구현된 PropertyPlaceholderConfigurer는 BeanFactory정의로부터 자바프라퍼티 형태의 다른 분리된 파일로 몇몇 프라퍼티값들을 구체화하기 위해 사용된다. 이것은 몇몇 key 프라퍼티(예를 들면 데이터베이스 URL, 사용자명, 비밀번호)를 복잡하거나 핵심이 되는 XML정의파일이나 BeanFactory을 위한 파일을 변경하는 위험없이 커스터마이징하기 위한 애플리케이션을 배치하는것을 사람에게 허용하는데 유용하다.

위치유지자(placeholder)값과 함께 데이터소스가 정의된 BeanFactory정의로부터의 일부를 검토하라.

아래의 예제에서 데이터소스가 명시되어 있고 우리는 외부 프라퍼티파일로부터 몇몇 프라퍼티를 설정할것이다. 실행시 우리는 데이터소스의 몇몇 프라퍼티를 대체할 BeanFactory로 PropertyPlaceholderConfigurer을 적용할것이다.

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
    <property name="url"><value>${jdbc.url}</value></property>
    <property name="username"><value>${jdbc.username}</value></property>
    <property name="password"><value>${jdbc.password}</value></property>
</bean>

실질적인 값들은 프라퍼티형태로 다른 파일로부터 나온다.

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

BeanFactory와 같이 이것을 사용하기 위해 bean factory 후-처리자는 수동으로 수행된다.

XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
cfg.postProcessBeanFactory(factory);

ApplicationContexts는 BeanFactoryPostProcessor를 구현하는 그것들에 배치되는 bean을 자동적으로 인식하고 적용하는 것이 가능하다는 알라. 이것은 여기서 서술된것처럼 그것들을 의미한다. PropertyPlaceholderConfiguer를 적용하는 것은 ApplicationContext을 사용할때보다 좀더 편리하다. 이러한 이유로 이것이나 다른 bean factory 후-처리자를 사용하길 바라는 사용자는 BeanFactory대신에 ApplicationContext을 사용하는것이 추천된다.

PropertyPlaceHolderConfigurer는 당신이 명시한 프라퍼티파일내에서만 프라퍼티를 찾지는 않는다. 하지만 만약 당신이 사용하길 시도하는 프라퍼티를 찾을수 없다면 자바 시스템 프라퍼티에 대해 체크한다. 이 행위는 설정자의 systemPropertiesMode 프라퍼티를 셋팅함으로써 커스터마이징될수 있다. 이것은 3개의 값을 가진다. 언제나 오버라이드 하도록 설정하는 하나와 결코 오버라이드하지 않는 하나, 프라퍼티가 정의된 프라퍼티파일내 찾을수 없을때만 단지 오버라이드하는 것이 있다. 좀더 많은 정보를 위해서는 PropertiesPlaceholderConfigurer를 위한 JavaDoc를 보라.

3.8.2. PropertyOverrideConfigurer

PropertyOverrideConfigurer, 다른 bean factory 후-처리자는 PropertyPlaceholderConfigurer와 비슷하지만 후자와는 대조적으로 원래의 정의는 디폴트 값을 가지거나 bean프라퍼티를 위한 값을 가질수 없다. 만약 오버라이딩된 파일이 어떤 bean프라퍼티를 위한 항목을 가지지 않는다면 디폴트 컨텍스트 정의가 사용된다.

bean factory정의는 오버라이드된것을 인식하지 않는다. 그래서 이것은 사용될 설정자를 오버라이드한 XML정의 파일을 찾을 때 즉시 명확하지 않다는것에 주의하라. 다중 PropertyOverrideConfigurers가 같은 bean프라퍼티를 위해 다른 값을 정의하는 경우에 가장 마지막의 값이 사용될것이다.(오버라이드기법에 따라.)

프라퍼티 파일 설정 라인은 다음과 같은 형태로 될것이다.

beanName.property=value

예제 프라퍼티 파일은 다음처럼 보일것이다.

dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb

예제 파일은 driverurl 프라퍼티를 가진 dataSource라고 불리는 것안에서 bean을 포함하는 BeanFactory정의에 대해 사용가능할것이다.

3.9. 추가적인 사용자지정 PropertyEditors 등록하기

문자열 값처럼 bean프라퍼티를 셋팅할때 BeanFactory는 이 문자열을 프라퍼티의 복합(complex)타입으로 형변환하기 위한 표준적인 자바빈 PropertyEditors를 사용한다. Spring은 많은 수의 PropertyEditors를 미리 등록한다. (예를 들면, 문자열처럼 표현되는 클래스명을 실제 클래스객체로 변환하는). 추가적으로 자바의 표준적인 자바빈 PropertyEditor 룩업 기법은 적당하게 명명되기 위한 클래스를 위해 PropertyEditor를 허용하고 자동적으로 발견되기 위한 지원을 제공하는 클래스처럼 같은 패키지내 위치한다.

만약 사용자지정 PropertyEditors을 등록할 필요가 있다면 여기엔 사용가능한 다양한 기법이 있다.

대개 편리하지 않거나 추천되지 않는 대부분의 수동 접근법은 당신이 BeanFactory참조를 가진다고 가정하고 ConfigurableBeanFactory 인터페이스의 registerCustomEditor() 메소드를 간단히 사용한다.

좀더 편리한 기법은 CustomEditorConfigurer 라고 불리는 특별한 bean factory 후-처리자를 사용하는 것이다. 비록 bean factory 후-처리자가 BeanFactory와 함께 반 수동적으로 사용될수 있다 하더라도 이것은 여기에서 언급되는 것처럼 강력하게 추천되기 때문에 내포된 프라퍼티 셋업을 가진다. 이것은 다른 bean에 유사한 방법으로 배치되고 자동적으로 감지되며 적용되는 ApplicationContext과 함께 사용된다.

모든 bean factory와 애플리케이션 컨텍스트는 프라퍼티 전환을 다루기 위한 BeanWrapper라고 불리는 몇가지의 사용을 통해 자동적으로 내장된 프라퍼티 편집기를 사용한다. BeanWrapper이 등록하는 표준적인 프라퍼티 편집기는 다음 장에서 목록화된다 . 추가적으로 ApplicationContexts 또한 오버라이드하거나 애플리케이션 컨텍스트 타입을 명시하기 위한 선호하는 방법으로 자원 룩업을 다루기 위해 추가적인 3가지 편집기를 추가한다. 그것들은 InputStreamEditor, ResourceEditor 그리고 URLEditor이다.

3.10. 존재하는 bean을 위한 별칭을 추가하기 위한 별칭 요소 사용하기.

bean정의 자체에서 당신은 id속성을 통해 명시된 하나의 이름을 위한 조합을 사용하고 alias속성을 통해 많은 수의 다른 이름을 사용하는 방법을 통해 bean을 위한 한개 이상의 이름을 제공할것이다. 이 모든 이름은 같은 bean에 대해 동일한 별칭이 검토될수 있고 애플리케이션내 사용되는 각각의 컴포넌트가 컴포넌트 자체를 위해 명시하는 bean이름을 사용하는 공통적인 의존성을 참조하도록 허용하는것과 같은 몇가지 상황을 위해 유용하다.

bean이 실질적으로 정의될 때 모든 별칭을 명시하는것이 언제나 충분한것은 아니다. 이것은 때때로 어떤곳에 정의되는 bean을 위한 별칭을 소개하는것이 바람직하다. 이것은 단독으로 사용되는 alias 요소를 통해 수행될수 있다.

<alias name="fromName" alias="toName"/>

이 경우, 같은 컨텍스트내 fromName라는 이름의 bean은 toName처럼 참조될수 있는 별칭정의를 나중에 사용할수 있다.

견고한 예제처럼, A 컴포넌트가 XML일부내에서 componentA-dataSource라고 불리는 데이터소스 bean을 정의하는 경우를 검토하라. B 컴포넌트는 XML일부내에서 componentB-dataSource처럼 데이터소스를 참조할것이다. 그리고 주된 애플리케이션 MyApp는 자신만의 XML일부를 정의하고 모든 3개의 일부로부터 마지막 애플리케이션 컨텍스트를 조합한다. 그리고 myApp-dataSource처럼 데이터소스를 참조할것이다. 이 시나리오는 다음의 단독 별칭을 가진 MyApp XML일부를 추가함으로써 쉽게 다루어질수 있다.

<alias name="componentA-dataSource" alias="componentB-dataSource"/> <alias name="componentA-dataSource" alias="myApp-dataSource"/>

지금 각각의 컴포넌트와 주된 애플리케이션은 유일하고 다른 정의와 충돌하지 않도록 보증된 이름을 통해 데이터소스를 참조할수 있다. 게다가 그것들은 같은 bean을 참조한다.

3.11. ApplicationContext에 대한 소개

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기능에 추가한 기능을 언급한다.

3.12. ApplicationContext에 추가된 기능

이전 부분에서 벌써 밝힌것처럼 ApplicationContext는 BeanFactory로부터 이것을 구별하는 두어가지의 기능을 가진다. 우리는 그것들을 하나씩 먼저 알아보자.

3.12.1. MessageSource 사용하기

ApplicationContext 인터페이스는 MessageSource 라고 불리는 인터페이스를 확장해서 메시징(i18n또는 국제화)기능을 제공한다. NestingMessageSource와 함께 구조적인 메시지를 분석하는 능력을 가진다. 여기엔 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 구현물을 제공한다. 여기엔 ResourceBundleMessageSourceStaticMessageSource가 있다. 둘다 메시지를 내포하기 위해 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, exceptionswindows라고 불리는 당신의 클래스패스내 정의된 3개의 자원번들을 가진다고 가정한다. ResourceBundles을 통한 메시지를 분석하는 JDK표준적인 방법을 사용하여 메시지를 분석하기 위한 요청이 다루어질것이다. TODO: SHOW AN EXAMPLE

3.12.2. events 전파하기

ApplicationContext내 이벤트 핸들링은 ApplicationEvent 클래스와 ApplicationListener인터페이스를 통해 제공된다. 만약 ApplicationListener 인터페이스를 구현하는 bean이 컨텍스트로 배치된다면 매번 ApplicationEvent는 통지될 bean인 ApplicationContext에 배포된다. 기본적으로 이것은 표준적인 Observer 디자인 패턴이다. Spring은 3가지 표준적인 이벤트를 제공한다.

Table 3.4. 내장된 이벤트

이벤트설명
ContextRefreshedEventApplicationContext가 초기화되거나 재생(refresh)될때 배포되는 이벤트. 여기서 초기화는 모든 bean이 로드되고 싱글톤은 미리 인스턴스화되며 ApplicationContext는 사용할 준비가 된다는 것을 의미한다.
ContextClosedEventApplicationContext의 close()메소드를 사용하여 ApplicationContext가 닫힐때 배포되는 이벤트. 여기서 닫히는 것은 싱글톤이 없어지는(destroy)되는것을 의미한다.
RequestHandledEvent웹 특정 이벤트는 HTTP요청이 서비스(이를 테면 요청이 종료될때 after가 배포될것이다.)되는 모든 bean을 말한다. 이 이벤트는 Spring의 DispatcherServlet을 사용하는 웹 애플리케이션에서만 적용가능하다.

사용자정의 이벤트를 구현하는것은 잘 작동될수 있다. ApplicationContext의 publishEvent() 메소드를 간단히 호출하는 것은 당신의 사용자정의 이벤트 클래스가 ApplicationEvent을 구현하는 파라미터를 명시하는 것이다. 이벤트 리스너(listener)는 이벤트를 동시에 받아들인다. 이것은 publishEvent() 메소드는 모든 리스너가 이벤트 처리를 종료할때 까지 블럭된다는것을 의미한다. 게다가 리스너가 이벤트를 받을때 이것은 배포자(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</value>
    </property>
</bean>

그리고 다음은 실질적인 bean이다.

public class EmailBean implements ApplicationContextAware {

    /** the blacklist */
    private List blackList;
    
    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 {

    /** notification address */
    private String notificationAddress;
    
    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(ApplicationEvent evt) {
        if (evt instanceof BlackListEvent) {
            // notify appropriate person
        }
    }
}

물론 이 특별한 예제는 기본적인 이벤트 기법을 설명하기에는 충분하지만 좀더 나은 방법(아마도 AOP기능을 사용하여)으로 구현될수 있을것이다.

3.12.3. Spring내에서 자원(resources) 사용하기

많은 애플리케이션은 자원에 접근할 필요가 있다. 자원을 파일을 포함할수 있지만 웹페이지나 NNTP 뉴스피드와 같은 것들도 포함할수 있다. Spring은 프로토콜에 비의존적인 방법으로 자원에 접근하는 깔끔하고 투명한 방법을 제공한다. ApplicationContext 인터페이스는 이것을 다루는 메소드(getResource(String))를 포함한다.

Resource클래스는 모든 Resource구현물을 통해 공유되는 두어가지의 메소드를 정의한다.

Table 3.5. Resource 기능

메소드설명
getInputStream()자원에 대해 InputStream을 열고 이것을 반환한다.
exists()자원이 존재하는지 체크하고 존재하지 않는다면 false를 반환한다.
isOpen()true를 반환하면 다중 스트림(stream)이 이 자원을 위해 열릴수 없다. 이것은 몇몇 자원을 위해 false가 될것이다. 하지만 예를 들어 파일-기반의 자원은 동시에 여러번 읽을수 없다.
getDescription()자원의 설명(description)을 반환한다. 종종 완전한 경로의 파일명이나 실질적인 URL이 반환된다.

두어가지의 Resource구현물이 Spring에 의해 제공된다. 그것들 모두 자원의 실질적인 위치를 표현하는 문자열값이 필요하다. 문자열값에 기반으로 하여 Spring은 당신을 위해 알맞은 Resource구현물을 자동적으로 선택할것이다. 처음으로 자원을 위해 ApplicationContext를 요청할 때 Spring의 모든것은 당신이 명시하고 어느 접두사로 찾는 자원의 위치를 조사할것이다. ApplicationContext의 구현물에 의존하여 하나 이상의 Resource구현물은 사용가능하다. Resource는 ResourceEditor를 사용하여 설절될때 가장 좋을수 있고 예로써 XmlBeanFactory가 있다.

3.13. ApplicationContext내에서 사용자정의 행위

BeanFactory는 이것(InitializingBean or DisposableBean처럼 표시자(marker) 인터페이스와 같은)에 배치된 bean들의 생명주기를 제어하는 많은 수의 기법을 이미 제공하고 있다. 그것들의 설정은 XmlBeanFactory 설정와 bean 후-처리자내 init-methoddestroy-method속성과 같이 동등하다. ApplicationContext에서 그것들 모두 작동을 하지만 추가적인 기법은 bean과 컨테이너의 사용자정의 행위를 위해 추가된다.

3.13.1. ApplicationContextAware 표시자(marker) 인터페이스

BeanFactory와 함께 사용가능한 모든 표시자(marker) 인터페이스는 여전히 작동한다. ApplicationContext는 org.springframework.context.ApplicationContextAware를 구현하는 bean인 하나의 추가적인 표시자(marker) 인터페이스를 추가한다. 이 인터페이스를 구현하고 컨텍스트로 배치되는 bean은 인터페이스의 setApplicationContext()를 사용하여 bean의 생성을 콜백될것이다. 그리고 나중에 컨텍스트와 함께 상호작동을 위해 저장될 컨텍스트에 대한 참조를 제공한다.

3.13.2. BeanPostProcessor

org.springframework.beans.factory.config.BeanPostProcessor인터페이스를 구현하는 자바 클래스인 bean 후-처리자는 벌써 언급되었다. 이것은 여기서 언급할 가치가 있다. 후-처리자는 명확한 BeanFactory보다 ApplicationContexts내에서 사용하는것이 좀더 편리하다. ApplicationContexts내에서 위 표시자(marker) 인터페이스를 구현하는 어떤 배치된 bean은 factory내 각각의 bean을 위해 생성시각에 적당하게 호출되도록 bean 후-처리자처럼 자동적으로 감지하고 등록된다.

3.13.3. BeanFactoryPostProcessor

org.springframework.beans.factory.config.BeanFactoryPostProcessor 인터페이스를 구현하는 자바 클래스인 Bean factory 후-처리자는 벌써 언급되었다. 이것은 여기서 언급할 가치가 있다. 그 bean factory 후-처리자는 보통의 BeanFactory내에서 보다 ApplicationContexts내에서 사용되는 것이 좀더 편리하다. ApplicationContexts에서 위 표시자(marker)인터페이스를 구현하는 어느 배치된 bean은 적절한 시각에 호출되기 위해 bean factory 후-처리자처럼 자동적으로 감지된다.

3.13.4. PropertyPlaceholderConfigurer

PropertyPlaceholderConfigurer 는 BeanFactory와 함께 사용되므로 벌써 설명되었다. 이것은 여기서 언급되는것이 가치있다. 이것은 컨텍스트가 자동적으로 어느 bean factory 후-처리자를 인식하고 적용하기 때문에 그것들이 다른 bean처럼 ApplicationContext으로 간단하게 배치될때 ApplicationContext와 함께 사용하는것이 대개 좀더 편리하다. 이것을 수행하기 위한 수동모드의 단계는 필요가 없다.

<!-- property placeholder post-processor -->
<bean id="placeholderConfig"
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location"><value>jdbc.properties</value></property>
</bean>

3.14. 추가적인 사용자정의 PropertyEditors 등록하기

이미 언급된것처럼, 표준 자바빈 PropertyEditors는 프라퍼티를 프라퍼티의 실질적인 복합타입을 위한 문자열처럼 표시되는 값으로 변환하기 위해 사용된다. CustomEditorConfigurer, bean factory 후-처리자, ApplicationContext를 위해 추가적인 PropertyEditors를 위한 지원을 편리하게 추가하기 위해 사용된다.

사용자 클래스인 ExoticType와 프라퍼티처럼 ExoticType 셋을 필요로 하는 다른 클래스인 DependsOnExoticType를 검토해보자.

public class ExoticType {
    private String name;
    public ExoticType(String name) {
        this.name = name;
    }
}

public class DependsOnExoticType {    
    private ExoticType type;
    public void setType(ExoticType type) {
        this.type = type;
    }
}

이것들이 적절히 셋업될 때, 우리는 PropertyEditor는 실제 ExoticType객체로 변환할 문자열처럼 타입 프라퍼티를 할당하는것을 가능하게 하도록 원한다.

<bean id="sample" class="example.DependsOnExoticType">
    <property name="type"><value>aNameForExoticType</value></property>
</bean>

PropertyEditor 는 이것과 유사하게 보일수 있다.

// converts string representation to ExoticType object
public class ExoticTypeEditor extends PropertyEditorSupport {

    private String format;

    public void setFormat(String format) {
        this.format = format;
    }
    
    public void setAsText(String text) {
        if (format != null && format.equals("upperCase")) {
            text = text.toUpperCase();
        }
        ExoticType type = new ExoticType(text);
        setValue(type);
    }
}

마지막으로 우리는 필요할 때 처럼 이것을 사용할 ApplicationContext를 가진 새로운 PropertyEditor을 등록하기 위해 CustomEditorConfigurer을 사용한다.

<bean id="customEditorConfigurer" 
    class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <entry key="example.ExoticType">
                <bean class="example.ExoticTypeEditor">
                    <property name="format">
                        <value>upperCase</value>
                    </property>
                </bean>
            </entry>
        </map>
    </property>
</bean>

3.15. 프라퍼티 표현에서 bean프라퍼티 또는 생성자의 인자를 셋팅하기.

PropertyPathFactoryBean은 주어진 대상 객체의 프라퍼티 경로를 평가하는 FactoryBean이다. 대상 객체는 직접적 또는 bean 이름을 통해 명시될수 있다. 이 값은 프라퍼티값이나 생성자의 인자처럼 다른 bean정의로 사용될수있다..

이것은 다른 bean에 대해 이름에 의해 사용되는 경로의 예제이다.

// target bean to be referenced by name
<bean id="person" class="org.springframework.beans.TestBean" singleton="false">
  <property name="age"><value>10</value></property>
  <property name="spouse">
    <bean class="org.springframework.beans.TestBean">
      <property name="age"><value>11</value></property>
    </bean>
  </property>
</bean>

// will result in 11, which is the value of property 'spouse.age' of bean 'person'
<bean id="theAge" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
  <property name="targetBeanName"><value>person</value></property>
  <property name="propertyPath"><value>spouse.age</value></property>
</bean>

이 예제에서 경로는 내부 bean에 대해 평가된다.

// will result in 12, which is the value of property 'age' of the inner bean
<bean id="theAge" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
  <property name="targetObject">
    <bean class="org.springframework.beans.TestBean">
      <property name="age"><value>12</value></property>
    </bean>
  </property>
   <property name="propertyPath"><value>age</value></property>
</bean>

bean이름이 프라퍼티 경로인 간략화된 형태 또한 있다.

// will result in 10, which is the value of property 'age' of bean 'person'
<bean id="person.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>

이 형태는 bean의 이름내 이것이 경로인 같은 id를 사용할 어떤 참조인 선택사항이 없다는 것을 의미한다. 물론 내부 bean처럼 사용된다면 전부를 참조할 필요는 없다.

<bean id="..." class="...">
  <proprty name="age">
    <bean id="person.age"
          class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
  </property>
</bean>

결과 타입은 실질적인 정의내 명시적으로 셋팅될수 있다. 이것은 대부분의 사용 상황을 위해 필요하지는 않지만 몇몇 사항을 위해서 사용될수 있다. 이 기능을 위한 좀더 다양한 정보를 위해서 JavaDoc를 보라.

3.16. 필드값으로부터 bean프라퍼티 또는 생성자의 인자를 셋팅하기.

FieldRetrievingFactoryBean은 정적이거나 비-정적인 필드값을 가져오는 FactoryBean이다. 이것은 다른 bean을 위해 프라퍼티값이나 생성자의 인자를 셋팅하기 위해 사용될수 있는 public 형태의 정적인 final 상수를 가져오기 위해 전형적으로 사용된다.

staticField 프라퍼티를 사용해서 정적 필드가 나타나는 방법을 보여주는 예제이다.

<bean id="myField"
      class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
  <property name="staticField"><value>java.sql.Connection.TRANSACTION_SERIALIZABLE</value></property>
</bean>

정적 필드가 bean이름처럼 정의되는 곳의 편리한 사용형태 또한 있다.

<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
      class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>

이것은 bean id내(그래서 다른 bean은 더 긴 이름을 사용하기 위해 참조한다.) 결코 어떠한 선택사항도 없지만 이 형태는 정의하기에 매우 간결하고 id가 bean참조를 위해 명시되지 않은 이후 내부 bean처럼 사용하기 위해 매우 편리하다.

<bean id="..." class="...">
  <proprty name="isolation">
    <bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
      class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
  </property>
</bean>

이것은 또한 JavaDoc에서 언급되는 것처럼 다른 bean의 비-정적인 필드를 접근하는것이 가능하다.

3.17. 다른 메소드를 호출하고 선택적으로 반환값을 사용한다.

이것은 몇몇 다른 클래스가 사용되기 전 몇몇 순차적인 초기화를 수행하기 위해 하나의 클래스내 정적이거나 비-정적인 메소드를 호출하는것이 때때로 필요하다. 추가적으로 이것은 컨테이너내 다른 bean의 메소드 호출 결과나 어느 임의의 클래스의 정적 메소드호출 결과처럼 bean의 프라퍼티를 셋팅하는것이 때때로 필요하다. 이 두가지 목적을 위해 MethodInvokingFactoryBean를 호출하는 헬퍼 클래스는 사용될수 있다. 이것은 정적이나 인스턴스 메소드 호출의 결과인 값을 반환하는 FactoryBean 이다.

우리는 어쨌든 추천된다. 두번째 사용 상황을 위해 이전에 언급된 factory 메소드는 거의 대부분의 선택에 훨씬 좋다.

bean 정의의 예제(XML기반의 BeanFactory정의내)는 몇몇 순차적인 정적 초기화를 강제로 수행하기 위한 이 클래스를 사용한다.

<bean id="force-init" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  <property name="staticMethod"><value>com.example.MyClass.initialize</value></property>
</bean>

<bean id="bean1" class="..." depends-on="force-init">
  ...
</bean>

bean1을 위한 정의는 첫번째 초기화 강제 초기화(force-init)를 유발할 강제 초기화(force-init) bean을 참조하기 위해 depends-on 속성을 사용하였다. 그리고 bean1이 첫번재 초기화될때 정적인 초기화 메소드를 호출한다.

정적인 factory메소드를 호출하기 위한 이 클래스를 사용하는 bean정의의 예제이다.

<bean id="myClass" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  <property name="staticMethod"><value>com.whatever.MyClassFactory.getInstance</value></property>
</bean>

자바 시스템 프라퍼티에서 얻기 위해 정적 메소드와 그 다음 인스턴스 메소드를 호출하는 예제이다. 어느정도 장황하지만 이것은 작동한다.

<bean id="sysProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  <property name="targetClass"><value>java.lang.System</value></property>
  <property name="targetMethod"><value>getProperties</value></property>
</bean>
<bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  <property name="targetObject"><ref local="sysProps"/></property>
  <property name="targetMethod"><value>getProperty</value></property>
  <property name="arguments">
    <list>
      <value>java.version</value>
    </list>
  </property>
</bean>

이것은 factory메소드에 접근하기 위해 대부분 사용되는것이 기대되는것을 알라. 디폴트에 의해 MethodInvokingFactoryBean는 싱글톤 형태로 작동한다. 객체를 생성하는 factory를 위해 컨테이너에 의한 첫번째 요청은 최근과 하위 요청을 위해 캐시되고 반환될 값을 반환하는 명시된 메소드 호출을 야기할것이다. factory의 내부 singleton 프라퍼티는 객체를 요청하는 매번 대상 메소드를 호출하는것을 야기하기 위해 false로 셋팅될수 있다.

정적인 대상 메소드는 정적 메소드가 정의되는 클래스를 명시하는 targetClass를 가지고 정적 메소드이름을 표시하는 문자열을 위해 targetMethod을 셋팅함으로써 명시될수 있다. 대안으로 대상 인스턴스 메소드는 대상 객체처럼 targetObject와 대상객체의 호출을 위한 메소드 이름처럼 targetMethod을 셋팅하여 명시될수 있다. 메소드 호출을 위한 인자는 args 프라퍼티를 셋팅하여 명시될수 있다.

3.18. 하나의 파일로부터 다른것으로 bean정의를 끌어오기

컨테이너 정의를 다중 XML파일로 분류하는것은 종종 유용하다. 그 다음 그러한 XML부분들로부터 설정된 애플리케이션 컨텍스트를 로드하는 하나의 방법은 다중 Resource위치를 가지는 애플리케이션 컨텍스트 생성자를 사용하는 것이다. bean factory를 사용하여 bean정의 리더(reader)는 각각의 파일로 부터 정의를 순서대로 읽기 위해 여러번 사용될수 있다.

대개 Spring팀은 이것이 컨테이너 설정 파일이 다른것과 조합된 사실을 인지할수 없도록 유지하기 때문에 위 접근법을 선호한다. 어쟀든 대안적인 접근법은 하나의 XML 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="..."/>
  . . .

이 예제에서 외부 bean정의는 3개의 파일인 services.xml, messageSource.xml, 과 themeSource.xml로 부터 로드되고 있다. 모든 위치 경로는 끌어오기(import)를 수행하는 정의 파일에 상대적으로 검토된다. 그래서 이 경우 messageSource.xmlthemeSource.xml이 import파일의 위치 아래의 resources 위치에 있어야만 하는 동안 services.xml은 끌어오기(import)를 수행하는 파일처럼 같은 디렉토리나 클래스패스 위치에 있어야만 한다. 당신이 보는것처럼 앞쪽의 슬래쉬(slash)는 실질적으로 무시된다. 하지만 상대적인 경로를 검토할때 이것은 아마도 슬래쉬(slash)를 전혀 사용하지 않는것이 더 좋다.

끌어오기(import)를 수행중인 파일의 내용은 DTD를 통해 가장 상위레벨 beans요소를 포함하는 완전히 유요한 XML bean정의 파일이어야 한다.

3.19. 웹 애플리케이션으로부터 ApplicationContext생성하기.

프로그램마다 다르게 종종 생성될 BeanFactory에 적대되는 것처럼, ApplicationContexts는 예를 들어 ContextLoader을 사용하여 선언적으로 생성될수 있다. 물론 당신은 ApplicationContext 구현물중 하나를 사용하여 프로그램마다 다르게 ApplicationContext을 생성할수 있다. 첫번째 ContextLoader를 조사해보고 이것의 구현물들을 조사해보자.

ContextLoader는 ContextLoaderListenerContextLoaderServlet의 두가지의 구현물을 가진다. 그것들 모두 같은 기능을 가지지만 리스너(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 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 파라미터를 사용할것이다.

3.20. Glue 코드와 좋지않은 싱글톤

애플리케이션내 코드의 대부분은 이것이 생성될때 컨테이너에 의해 자신만의 의존성이 제공되고 컨테이너를 완벽하게 자각하지 못하는 BeanFactory나 ApplicationContext 컨테이너에 코드가 제공되는 의존성 삽입(Dependency Injection-Inversion of Control)을 사용하여 쓰여지는게 가장 좋다. 어쨌든 다른 코드와 함께 묶기 위한 때때로 필요한 코드의 작은 glue레이어를 위해 BeanFactory나 ApplicationContext를 위해 접근하는 싱글톤(또는 준(quasi)-싱글톤 스타일을 위해 때때로 필요하다. 예를 들어 써드파티(third party) 코드가 BeanFactory의 객체를 얻기 위해 이것을 강제로 하게 할수 있는 능력없이 새로운 객체를 직접적으로 생성(Class.forName() 스타일)하도록 시도할수 있다. 만약 써드파티(third party)모드에 의해 생성된 객체가 작은 스텁(stub)나 프록시라면 위임하는 실제 객체를 얻기 위해 BeanFactory/ApplicationContext에 접근하는 싱글톤 스타일을 사용한다. inversion of control은 여전히 코드의 대부분을 위해 달성된다.(객체는 BeanFactory로 부터 나온다.). 게다가 대부분의 코드는 컨테이너나 이것이 접근되는 방법을 자각하지 않는다. 그리고 모든 이익을 가지는 다른 코드로 부터 커플링되지 않은체로 남게된다. EJB는 BeanFactory로 부터 나오는 명확한 자바 구현물 객체를 위해 위임하는 스텁/프록시 접근법을 사용할수 있다. BeanFactory는 이론상 싱글톤이 되지 않는 동안 이것은 비-싱글톤 BeanFactory를 사용하는 각각의 bean을 위해 메모리 사용이나 초기화 시점(Hibernate SessionFactory처럼 BeanFactory내 bean을 사용할 때)의 개념에서 비사실적일수 있다.

다른 예제처럼, 다중 레이어(이를테면, 다양한 JAR파일들, EJB들, 그리고 EAR처럼 패키지된 WAR파일)의 복잡한 J2EE애플리케이션에서 이것 자체의 ApplicationContext정의(구조를 효과적으로 형상화하는)를 가진 각각의 레이어와 함께 가장 상위 구조내 단 하나의 웹 애플리케이션(WAR)이 존재할때 각각의 레이어의 다중 XML정의 파일에서 하나의 복잡한 ApplicationContext를 간단하게 생성하기 위한 접근법이 선호된다. 모든 ApplicationContext종류는 이 형태로 다중 정의 파일로 부터 생성될수 있다. 어쨌든 구조의 가장 상위의 다중의 구성원(sibling) 웹 애플리케이션을 가진다면 이것은 아래쪽의 레이어로부터 대부분 일치하는 bean정의를 구성하는 각각의 웹 애플리케이션을 위한 ApplicationContext을 생성하는것이 메모리 사용을 증가시키거나 오랜 시간동안 초기화(이를테면, Hibernate SessionFactory)하는 그리고 부작용(side-effects)과 같은 여러개의 bean을 생성하는 문제의 소지가 있다. 대안으로 ContextSingletonBeanFactoryLocatorSingletonBeanFactoryLocator 와 같은 클래스는 웹 애플리케이션 ApplicationContexts의 부모처럼 사용될수 있는 효과적인 싱글톤형태로 다중 구조적 BeanFactory나 ApplicationContexts 로드를 요구하기 위해 사용될수 있다. 그 결과는 아래쪽의 레이어를 위한 bean정의가 필요할때만 오직 한번 로드되는것이다.

3.20.1. SingletonBeanFactoryLocator 와 ContextSingletonBeanFactoryLocator을 사용하기

당신은 각각의 JavaDoc를 봐서 SingletonBeanFactoryLocatorContextSingletonBeanFactoryLocator 를 사용하는 상세화된 예제를 볼수 있을것이다.

EJB장에서 언급되는것처럼, EJB를 위한 Spring의 편리한 기본 클래스는 필요하다면 SingletonBeanFactoryLocatorContextSingletonBeanFactoryLocator 의 사용으로 쉽게 대체되는 비-싱글톤 BeanFactoryLocator을 대개 사용한다.