Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

[Spring 레퍼런스] 4장 IoC 컨테이너 #9

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.




4.9 어노테이션기반의 컨테이너 설정


꺽쇄괄호(< >)를 사용한 선언 대신 컴포넌트를 연결하기 위해 바이트코드 메타데이터에 의존하는 어노테이션 기반의 설정을 XML 설정의 대안으로 제공한다. 개발자들은 빈 연결을 나타내려고 XML을 사용하는 대신 적절한 클래스, 메서드 필스 선언에 어노테이션을 사용해서 선언을 컴포넌트 클래스로 옮겼다. Section 4.8.1.2, “예제 : RequiredAnnotationBeanPostProcessor”에서 말했듯이 BeanPostProcessor에 어노테이션을 사용해서 스프링 IoC 컨테이너를 확장하는 것이 일반적인 목적이다. 예를 들어 스프링 2.0에서 도입한 @Required 어노테이션으로 필수 프로퍼티를 강제할 수 있다. 스프링 2.5는 어노테이션을 스프링의 의존성 주입과 같이 일반적인 접근을 따를 수 있도록 했다. 본질적으로 @Autowired 어노테이션은 Section 4.4.5, “협력객체의 자동연결(Autowiring)”에서 설명한 것과 같은 기능을 제공하지만 더 세세하게 제어할 수 있고 더 넓은 범위에 적용할 수 있다. 스프링 2.5에서는 @PostConstruct, @PreDestroy같은 JSR-250 어노테이션에 대한 지원도 추가했다. 스프링 3.0은 @Inject와 @Named같은 javax.inject 패키지에 포함된 JSR-330 (Java의 의존성 주입) 어노테이션에 대한 지원을 추가했다. 이러한 어노테이션들에 대한 자세한 내용은 관련 섹션에서 확인할 수 있다.

Note
어노테이션 주입은 XML 주입 이전에 수행되므로 두가지 접근에 모두 연결된 프로퍼티들은 XML 설정이 어노테이션 설정을 오버라이드할 것이다.

언제나 그렇듯이 어노테이션들도 개별적인 빈 정의처럼 등록할 수 있지만 XML 기반의 스프링 설정에서 다음의 태그를 포함하면 암묵적으로 등록할 수 있다.(context 네임스페이스가 포함되었다.)

XML을 이용해서 스프링을 설정하는 것보다 어노테이션이 더 나은가?
어노테이션 기반의 설정을 도입하자 이 접근이 XML보다 '나은지'에 대한 의문이 생겼다. 간단한 답을 말하자면 경우에 따라 다르다. 좀 더 길게 얘기하자면 각 접근은 장단점이 있고 보통 개발자가 어떤 전략이 더 적합하다고 결정하는지에 달려있다. 이 접근들이 정의된 방법으로 인해서 어노테이션은 많은 컨텍스트를 제공하고 이는 더 짧고 더 간결한 설정으로 이어진다. 하지만 XML은 소스코드를 건드리거나 다시 컴파일할 필요없이 컴포넌트를 연결하는데 탁원하다. 어떤 개발자들은 소스로 연결하는 것을 선호하는 반면 어떤 개발자들은 어노테이션이 붙은 클래스들은 POJO가 아닐뿐더러 설정이 분산되어서 제어하기 어렵다고 주장한다.

어떤 선택을 하던지 스프링은 두 가지 스타일을 모두 제공하고 두 가지 스타일을 섞어서 사용할 수도 있다. 스프링이 JavaConfig 옵션을 통해 타겟 컴포넌트 소스코드를 건드릴 필요없이 비침입적인 방법으로 어노테이션을 사용할 수 있게 한다는 점은 중요하다. 그리고 도구적인 관점에서 모든 설정 스타일은 SpringSource Tool Suite에서 지원한다.


<?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:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">

   <context:annotation-config/>

</beans>


(앞에서 얘기한 RequiredAnnotationBeanPostProcessor처럼 AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor같은 후처리자는 암묵적으로 등록된다.)

Note
<context:annotation-config/>는 자신이 정의된 어플리케이션 컨텍스트에서만 어노테이션이 붙은 빈들을 찾는다. 이는 DispatcherServlet에 대한 WebApplicationContext에서 <context:annotation-config/>를 설정했다면 서비스가 아닌 컨트롤러에서만 @Autowired 빈을 확인한다는 의미이다. 더 자세한 정보는 Section 16.2, “The DispatcherServlet”를 참고해라.


4.9.1 @Required
@Required 어노테이션을 다음 예제처럼 빈 프로퍼티 setter 메서드에 적용한다.


public class SimpleMovieLister {

  private MovieFinder movieFinder;

  @Required
  public void setMovieFinder(MovieFinder movieFinder) {
    this.movieFinder = movieFinder;
  }

  // ...
}

이 어노테이션이 적용된 빈 프로퍼티는 빈 정의의 명확한 프로퍼티 값이나 자동연결을 통해서 설정시에 반드시 존재해야(populated) 한다는 것은 간단히 나타낸다. 적용된 빈 프로퍼티가 존재하지 않는다면 컨테이너는 예외를 던진다. 이는 최대한 명시적으로 실패를 나타내서 NullPointerException을 피할 수 있도록 한다. 빈 클래스내에 단언문을 두는 것은 여전히 권장한다. 예를 들어 init 메서드에 단언문을 작성한다. 이를 통해 컨테이너 외부의 클래스를 사용하는 경우에도 필수 참조와 값을 강제할 수 있다.

4.9.2 @Autowired
기대하듯이 "전통적인" setter 메서드에 @Autowired 어노테이션을 적용할 수 있다.

public class SimpleMovieLister {

  private MovieFinder movieFinder;

  @Autowired
  public void setMovieFinder(MovieFinder movieFinder) {
    this.movieFinder = movieFinder;
  }

  // ...
}

Note
다음 예제처럼 JSR 330의 @Inject 어노테이션을 스프링의 @Autowired 어노테이션 대신 사용할 수 있다. 더 자세한 정보는 here를 참고해라.

임의의 이름을 가진 메서드나 아규먼트가 여러게 있는 메서드에도 이 어노테이션을 적용할 수 있다.


public class MovieRecommender {

  private MovieCatalog movieCatalog;

  private CustomerPreferenceDao customerPreferenceDao;

  @Autowired
  public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
    this.movieCatalog = movieCatalog;
    this.customerPreferenceDao = customerPreferenceDao;
  }

  // ...
}

생성자나 필드에도 @Autowired를 적용할 수 있다.


public class MovieRecommender {

  @Autowired
  private MovieCatalog movieCatalog;

  private CustomerPreferenceDao customerPreferenceDao;

  @Autowired
  public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
    this.customerPreferenceDao = customerPreferenceDao;
  }

  // ...
}

특정 타입의 배열을 요구하는 필드나 메서드에 어노테이션을 추가함으로써 ApplicationContext에서 해당 타입의 모든 빈을 제공할 수도 있다.


public class MovieRecommender {

  @Autowired
  private MovieCatalog[] movieCatalogs;

  // ...
}

타입있는 컬렉션에도 똑같이 적용된다.


public class MovieRecommender {

  private Set<MovieCatalog> movieCatalogs;

  @Autowired
  public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
    this.movieCatalogs = movieCatalogs;
  }

  // ...
}

타입있는 Map도 요구하는 키의 타입이 String이라면 자동연결 할 수 있다. 맵의 값들은 요구하는 타입의 모든 빈을 담을 것이고 키는 대응하는 빈의 이름을 담을 것이다.


public class MovieRecommender {

  private Map<String, MovieCatalog> movieCatalogs;

  @Autowired
  public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
    this.movieCatalogs = movieCatalogs;
  }

  // ...
}

기본적으로 자동연결은 사용가능한 후보 빈이 없으면 실패한다. 어노테이션이 붙은 메서드, 생성자, 필드가 필수 의존성을 나타내는 것처럼 다루는 것이 기본 동작이다. 다음 예제에서 보여주듯 이 기본 동작을 변경할 수 있다.

public class SimpleMovieLister {

  private MovieFinder movieFinder;

  @Autowired(required=false)
  public void setMovieFinder(MovieFinder movieFinder) {
    this.movieFinder = movieFinder;
  }

  // ...
}

Note
클래스당 하나의 어노테이션이 붙은 생성자만 필수로 표시할 수 있지만 필수가 아닌 다수의 생성자에도 어노테이션을 붙힐 수 있다. 이 경우에 각각의 생성자는 후보군으로 다루고 스프링은 의존성을 만족할 수 있으면서 제일 많은 수의 아규먼트를 가진 가장 탐욕적인(greediest) 생성자를 사용한다.

@Autowired의 required 속성은 @Required 어노테이션과 상관없이 권장한다. required 속성은 해당 프로퍼티가 자동연결 목적에는 필수가 아니라는 것을 나타내고 자동연결을 할 수 없는 경우에 이 프로퍼티는 무시된다. 반면에 @Required는 컨테이너가 제공하는 어떤 방법으로든 설정되어야 하는 프로퍼티임을 강제한다는 면에서 더 강력하다. 주입되는 값이 없다면 그에 대응하는 예외가 발생한다.

인터페이스들에 대해서도 @Autowired를 사용할 수 있다. 이 인터페이스들은 잘 알려진 해결가능한 의존성들이다: BeanFactory, ApplicationContext, Environment, ResourceLoader, ApplicationEventPublisher, MessageSource. 이러한 인터페이스들과 ConfigurableApplicationContext나 ResourcePatternResolver같이 이 인터페이스들을 상속받은 인터페이스는 특별한 설정을 하지 않고도 자동으로 해결된다.


public class MovieRecommender {

  @Autowired
  private ApplicationContext context;

  public MovieRecommender() {
  }

  // ...
}

Note
@Autowired, @Inject, @Resource, @Value 어노테이션들은 스프링의 BeanPostProcessor 구현체가 처리한다. 이는 (무엇이든간에) 자신만의 BeanPostProcessor나 BeanFactoryPostProcessor 타입내에서는 이러한 어노테이션들을 적용할 수 없다는 의미이다. 이러한 타입들은 반드시 명시적으로 XML이나 스프링의 @Bean 메서드를 사용해서 '연결해야' 한다.


4.9.3 qualifiers를 사용한 어노테이션 기반 자동연결의 미세조정
타입으로 자동연결을 하면 다수의 후보들이 생기기 때문에 종종 선택과정에 대해 더 세밀한 제어가 필요하다. 이러한 제어를 하는 한 가지 방법은 스프링의 @Qualifier 어노테이션이다. 각 아규먼트에 특정 빈이 선택되도록 일치하는 타입의 세트를 한정시켜서 qualifier 값들을 지정한 아규먼트들에 연결할 수 있다. 가장 간단한 경우에 이는 평이한 기술적인 값이 될 수 있다.


public class MovieRecommender {

  @Autowired
  @Qualifier("main")
  private MovieCatalog movieCatalog;

  // ...
}

생성자 아규먼트나 메서드의 파라미터에 개별적으로 @Qualifier 어노테이션을 지정할 수도 있다.


public class MovieRecommender {

  private MovieCatalog movieCatalog;

  private CustomerPreferenceDao customerPreferenceDao;

  @Autowired
  public void prepare(@Qualifier("main") MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
    this.movieCatalog = movieCatalog;
    this.customerPreferenceDao = customerPreferenceDao;
  }

  // ...
}

일치하는 빈 정의들은 다음과 같다. qualifier값으로 "main"을 가진 빈은 "main"값으로 제한된 생성자 아규먼트와 연결된다.



<?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:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <context:annotation-config/>

  <bean class="example.SimpleMovieCatalog">
      <qualifier value="main"/>
      <!-- 이 빈이 필요로하는 의존성을 주입한다 -->
  </bean>

  <bean class="example.SimpleMovieCatalog">
      <qualifier value="action"/>
      <!-- 이 빈이 필요로하는 의존성을 주입한다 -->
  </bean>

  <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>



일치하는 값을 찾지 못했을 때(fallback match)를 대비해서 빈의 이름이 기본 qualifier 값으로 간주된다. 그러므로 중첩된 qualifier 요소대신에 "main"이라는 id로 빈을 정의해도 같은 결과가 나온다. 하지만 이름으로 특정 빈을 참조하는 이 관례를 사용할 수 있더라도 @Autowired는 본질적으로 추가적인 의미있는 qualifier로 타입주도 주입에 대한 것이다. 이 말은 실패했을 때(fallback) 빈 이름을 사용하더라도 qualifier 값은 항상 일치된 타입의 세트내에서 의미로 제한한다는 의미이다. 유일한 빈 id에 대한 참조를 의미론적으로 표현하지 않는다. 앞의 예제처럼 익명 빈 정의의 경우에 자동으로 생성되는빈 id와는 독립적으로 특정 컴포넌트의 특징을 표현하는 "main"나 "EMEA"나 "persistent"가 좋은 qualifier 값이다.

앞에서 얘기했듯이 qualifier는 Set<MovieCatalog> 같은 타입있는 컬렉션에도 적용된다. 이 경우에 선언된 qualifier에 일치하는 모든 빈이 컬렉션으로 주입된다. 이는 qualifier는 유일할 필요가 없다는 의미이다. 더 적절하게 말하면 필터링 크리테리아(criteria)를 간단히 구성한다. 예를 들어 "action" 이라는 같은 qualifier 값을 가진 MovieCatalog 빈을 다수 정의할 수 있다. 이 빈들은 모두 @Qualifier("action") 어노테이션이 붙은 Set<MovieCatalog>로 주입될 것이다.

Tip
이름을 사용하는 어노테이션 주도 주입을 표현하려 한다면 @Qualifier 값으로 빈 이름을 참조하는 것이 기술적으로 가능하더라도 @Autowired을 주로 사용하지 마라. 대신 의미적으로 유일한 이름의 특정 타겟 컴포넌트를 식별하기 위해 정의된 JSR-250 @Resource 어노테이션을 사용해라. 선언된 타입은 일치하는 빈을 찾는 과정과는 관계가 없다.

이러한 의미적 차이점의 특정한 영향처럼 컬렉션이나 맵 타입으로 정의된 빈들은 @Autowired로 주입될 수 없다. 그 이유는 타입 매칭이 이 빈들에는 적용하기가 적절하지 않기 때문이다. 이러한 빈에는 유일한 이름으로 컬렉션이나 맵 타입의 빈을 참조하는 @Resource를 사용해라.

필드, 생성자, 여러 아규먼트를 가진 메서드에 적용하는 @Autowired는 파라미터 레벨에서 qualifier 어노테이션으로 제한할 수 있다. 반면에 @Resource는 필드나 하나의 아규먼트만 가진 프로퍼티 setter 메서드만 지원한다. 그 결과 주입 대상이 생성자나 다수의 아규먼트를 가진 메서드라면 qualifier를 붙여라.

자신만의 커스텀 qualifier 어노테이션을 생성할 수 있다. 간단히 어노테이션을 정의하고 정의내에서 @Qualifier 어노테이션을 제공하라.


@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {

  String value();
}

그러면 자동연결된 필드나 파라미터에 커스텀 qualifier를 제공할 수 있다.


public class MovieRecommender {

  @Autowired
  @Genre("Action")
  private MovieCatalog actionCatalog;

  private MovieCatalog comedyCatalog;

  @Autowired
  public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
    this.comedyCatalog = comedyCatalog;
  }

  // ...
}

그 다음 후보 빈 정의에 대한 정보를 제공하라. <bean/> 태그의 하위요소로 <qualifier/> 태그들을 추가할 수 있고 커스텀 qualifier 어노테이션과 일치하는 type과 value를 지정해라. 타입은 정규화된 qualifier 어노테이션과 일치한다. 또는 충돌하는 이름이 존재할 위험이 없다면 편리하게 짧은 클래스명을 사용할 수 있다. 두 가지 접근을 다음 예제에서 보여준다.



<?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:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <context:annotation-config/>

  <bean class="example.SimpleMovieCatalog">
      <qualifier type="Genre" value="Action"/>
      <!-- 이 빈이 필요로하는 의존성을 주입한다 -->
  </bean>

  <bean class="example.SimpleMovieCatalog">
      <qualifier type="example.Genre" value="Comedy"/>
      <!-- 이 빈이 필요로하는 의존성을 주입한다 -->
  </bean>

  <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>



Section 4.10, “클래스패스 스캔과 관리된 컨포넌트”에서 XML에서 qualifier 메타데이터를 제공하는 어노테이션 기반 qualifier의 대안을 볼 수 있다. 구체적인 내용은 Section 4.10.7, “어노테이션으로 qualifier 메타데이터 제공하기”를 봐라.

여러 경우에 이는 값이 없이 어노테이션을 사용하는 것이 충분할 것이다. 어노테이션이 상당히 일반적인 목적을 제공할 때 유용할 것이고 여러 다른 타입의 의존성사이에 적용할 수 있다. 예를 들면 인터넷을 이용할 수 없는 경우에 검색된 오프라인 카탈로그를 제공할 것이다. 우선 간단한 어노테이션을 정의한다.


@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {

}

그 다음 어노테이션을 자동연결할 필드나 프로퍼티에 추가한다.


public class MovieRecommender {

  @Autowired
  @Offline
  private MovieCatalog offlineCatalog;

  // ...
}

이제 빈정의는 qualifier type만 필요로 한다.



<bean class="example.SimpleMovieCatalog">
  <qualifier type="Offline"/>
  <!-- 이 빈이 필요로하는 의존성을 주입한다 -->
</bean>



이름있는 속성을 받는 커스텀 qualifier 어노테이션을 간단한 value 속성에 추가하거나 value 속성 대신 정의할 수도 있다. 다수의 속성 값들이 자동연결하려고 필드나 파라니터에 지정되어 있다면 빈 정의는 자동연결 후보로 여겨지는 속성 값들 모두와 반드시 일치해야 한다. 예를 들어 다음 어노테이션 정의를 살펴보자.


@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {

  String genre();

  Format format();
}

이 경우에 Format은 enum이다.


public enum Format {

  VHS, DVD, BLURAY
}

자동연결되는 필드들은 커스텀 qualifier가 붙어야 하고 genre와 format 두 속성에 대한 값을 포함해야 한다.

public class MovieRecommender {

  @Autowired
  @MovieQualifier(format=Format.VHS, genre="Action")
  private MovieCatalog actionVhsCatalog;

  @Autowired
  @MovieQualifier(format=Format.VHS, genre="Comedy")
  private MovieCatalog comedyVhsCatalog;

  @Autowired
  @MovieQualifier(format=Format.DVD, genre="Action")
  private MovieCatalog actionDvdCatalog;

  @Autowired
  @MovieQualifier(format=Format.BLURAY, genre="Comedy")
  private MovieCatalog comedyBluRayCatalog;

  // ...
}

결국 빈 정의는 일치하는 qualifier 값들을 포함해야 한다. 이 예제는 빈의 메타 속성들을 <qualifier/> 하위요소 대신 사용할 수 있다는 것을 보여준다. <qualifier/>와 그 속성들을 사용할 수 있다면 먼저 처리된다. 하지만 다음 예제에서 마지막 두 빈 정의들처럼 그러한 qualifier가 존재하지 않는다면 자동연결 메카니즘은 <meta/> 태그내에서 제공된 값들에 의존할 것이다.



<?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:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <context:annotation-config/>

  <bean class="example.SimpleMovieCatalog">
      <qualifier type="MovieQualifier">
          <attribute key="format" value="VHS"/>
          <attribute key="genre" value="Action"/>
      </qualifier>
      <!-- 이 빈이 필요로하는 의존성을 주입한다 -->
  </bean>

  <bean class="example.SimpleMovieCatalog">
      <qualifier type="MovieQualifier">
          <attribute key="format" value="VHS"/>
          <attribute key="genre" value="Comedy"/>
      </qualifier>
      <!-- 이 빈이 필요로하는 의존성을 주입한다 -->
  </bean>

  <bean class="example.SimpleMovieCatalog">
      <meta key="format" value="DVD"/>
      <meta key="genre" value="Action"/>
      <!-- 이 빈이 필요로하는 의존성을 주입한다 -->
  </bean>

  <bean class="example.SimpleMovieCatalog">
      <meta key="format" value="BLURAY"/>
      <meta key="genre" value="Comedy"/>
      <!-- 이 빈이 필요로하는 의존성을 주입한다 -->
  </bean>

</beans>



4.9.4 CustomAutowireConfigurer
CustomAutowireConfigurer는 스프링의 @Qualifier 어노테이션이 붙어있지 않더라도 자신만의 커스텀 qualifier 어노테이션 타입을 등록할 수 있도록 해주는 BeanFactoryPostProcessor다.



<bean id="customAutowireConfigurer"
     class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
  <property name="customQualifierTypes">
      <set>
          <value>example.CustomQualifier</value>
      </set>
  </property>
</bean>

AutowireCandidateResolver의 특정 구현체는 자바 버전에 의존적인 어플리케이션 컨텍스트에 대해 활성화된다. 자바 5 이전의 버전은 qualifier 어노테이션을 지원하지 않으므로 자동연결 후보는 <beans/> 요소에서 사용할 수 있는 default-autowire-candidates 패턴들 뿐만 아니라 각 빈 정의의 autowire-candidate 값으로 단독으로 결정된다. 자바 5 이상의 버전에는 @Qualifier 어노테이션과 CustomAutowireConfigurer으로 등록된 커스텀 어노테이션들이 존재하므로 제 역할을 할 것이다.

자바 버전에 상관없이 다수의 빈들이 자동연결 후보가 되었을 때 "주요(primary)" 후보가 결정되는 방식은 동일하다. 후보 중에 정확하게 primary 속성이 true으로 설정된 빈이 있다면 이를 선택할 것이다.

4.9.5 @Resource
스프링은 필드나 빈 프로퍼티 setter 메서드에 JSR-250 @Resource 어노테이션을 사용한 주입도 지원한다. 이는 Java EE 5나 6에서 일반적인 패턴이다. 예를 들어 JSF 1.2에서 관리되는 빈이나 JAX-WS 2.0 엔드포인트가 있다. 스프링은 스프링이 관리하는 객체들처런 이 패턴을 지원한다.

@Resource에는 name 속성이 있고 기본적으로 스프링이 값을 주입할 빈의 이름으로 인터프린트한다. 다시 말하자면 다음 예제에서 보여주듯 by-name 의미를 따른다.


public class SimpleMovieLister {

  private MovieFinder movieFinder;

  @Resource(name="myMovieFinder")
  public void setMovieFinder(MovieFinder movieFinder) {
      this.movieFinder = movieFinder;
  }
}

명시적으로 이름을 지정하지 않았다면 기본 이름은 필드명이나 setter 메서드에서 가져온다. 필드의 경우에는 필드명을 사용하고 setter 메서드의 경우에는 빈 프로퍼티 이름을 사용한다. 그래서 다음 예제는 "movieFinder"라는 이름의 빈을 setter 메서드에 주입할 것이다.


public class SimpleMovieLister {

  private MovieFinder movieFinder;

  @Resource
  public void setMovieFinder(MovieFinder movieFinder) {
      this.movieFinder = movieFinder;
  }
}

Note
어노테이션으로 제공한 이름은 CommonAnnotationBeanPostProcessor에 친화적인 ApplicationContext가 빈 이름으로 처리한다. 스프링의 SimpleJndiBeanFactory를 명시적으로 설정했다면 이 이름은 JNDI를 통해서 처리될 수 있다. 하지만 기본 동작에 의존하고 간접적인 수준을 유지하기 위해 스프링의 JNDI 검색 기능을 간단히 사용하기를 권장한다.

명시적으로 name을 지정하지 않은 @Resource 사용의 베타적인 경우에 @Autowired와 유사하게 @Resource는 지정한 이름있는 빈 대신 일치하는 주요 타입을 찾고 잘 알려진 해결할 수 있는 의존성을 해결한다. 잘 알려진 해결할 수 있는 의존성은 BeanFactory, ApplicationContext, ResourceLoader, ApplicationEventPublisher, MessageSource 인터페이스들이다.

그러므로 다음 예제에서 customerPreferenceDao 필드는 customerPreferenceDao라는 이름의 빈을 먼저 찾고 못 찾으면 CustomerPreferenceDao 타입과 일치하는 주요 타입을 찾는다. "context" 필드는 알고 있는 해결할 수 있는 의존성 타입인 ApplicationContext에 기반해서 주입된다.


public class MovieRecommender {

  @Resource
  private CustomerPreferenceDao customerPreferenceDao;

  @Resource
  private ApplicationContext context;

  public MovieRecommender() {
  }

  // ...
}

4.9.6 @PostConstruct와 @PreDestroy
CommonAnnotationBeanPostProcessor는 @Resource 어노테이션 뿐만 아니라 JSR-250 라이프사이클 어노테이션도 인식한다. 스프링 2.5에서 도입된 이러한 어노테이션들에 대한 지원은 초기화 콜백와 파괴 콜백에서 설명한 또다른 대안을 제공할 것이다. 스프링 ApplicationContext에 등록된 CommonAnnotationBeanPostProcessor가 있을 때 이러한 어노테이션 중 하나가 붙은 메서드는 대응되는 스프링 라이프사이클 메서드나 명시적으로 선언된 콜백 메서드와 동시에 호출된다. 다음 예제에서 캐시는 초기화에 따라 먼저 채워지고 파괴할 때 비워진다.


public class CachingMovieLister {

  @PostConstruct
  public void populateMovieCache() {
    // 초기화에 따라 movie cache가 채워진다...
  }

  @PreDestroy
  public void clearMovieCache() {
    // 파괴할 때 movie cache가 비워진다...
  }
}

Note
여러가지 라이프사이클 메카니즘을 조합한 효과에 대해서 자세히 알고 싶다면 Section 4.6.1.4, “라이프사이클 메카니즘 조합하기”를 봐라.

2012/04/26 00:59 2012/04/26 00:59