1.9 어노테이션 기반의 컨테이너 구성 by sh

어노테이션 기반의 컨테이너 구성

1.9. 어노테이션 기반의 컨테이너 구성

스프링을 구성하기 위해 XML보다 어노테이션이 더 유용합니까?

어노테이션 기반 구성의 도입으로 이 접근 법이 XML보다 “더 나은” 것인 지 여부에 대한 질문이 제기되었습니다. 간략한 대답은 “의존한다” 입니다. 긴 대답은 “각각의 접근 방식에 장단점이 있으며 일반적으로 어떤 전략이 더 적합한 지 결정하는 것은 개발자의 몫”입니다. 어노테이션이 정의된 방식으로 인해, 어노테이션은 선언문에 많은 문맥을 제공하므로 짧고 간결한 구성이 가능합니다. 그러나 XML은 소스 코드를 건드리거나 다시 컴파일 하지 않고도 구성 요소를 연결할 때 탁월합니다. 일부 개발자들은 소스에 가까운 배선을 선호하는 반면, 일부 다른 개발자들은 어노테이션이 달린 클래스는 더 이상 POJO가 아니며 구성이 분산되고 제어하기가 더 어려워 진다고 주장합니다.

  • (+)

    • XML Vs. Annotation

      • 시스템 전반에 영향을 주는 메타데이터는 XML로 설정하여 코드로부터 독립적으로 분리되는 것이 바람직하다.

      • 설계 시 확정되는 부분은 Annotation 기반 설정으로 개발의 생산성을 향상 시키는 것이 바람직하다.

    • POJO (= Plain Old Java Object)

      • 순수 자바 객체 (순수하게 "setter"와 "getter"로 이루어진 Value Object성의 빈)

선택과 상관없이 스프링은 두 스타일을 모두 수용할 수 있으며 함께 혼합할 수도 있습니다. Java Config 옵션을 통해 스프링은 대상 구성 요소 소스 코드를 건드리지 않고 어노테이션을 비 침습적인 방법으로 사용할 수 있으며 툴링 측면에서 모든 구성 스타일이 Spring Tool Suite에서 지원된다는 점을 지적할 가치가 있습니다.

  • (+)

    • 스프링의 Java Config 기능

      • XML이 아닌 자바 코드를 이용해서 컨테이너를 설정할 수 있는 기능.

      Ex. @Configuration, @Bean

      Cf. https://www.slideshare.net/arawnkr/spring-camp-2013-java-configuration

XML 설정의 대안은 어노테이션 기반 구성에 의해 제공됩니다. 이 구성은 꺽쇠 괄호 선언 대신 구성 요소를 연결하기 위해 바이트 코드 메타 데이터를 사용합니다. XML을 사용하여 Bean 배선을 설명하는 대신 개발자는 관련 클래스나 메소드 또는 필드 선언에 대한 어노테이션을 사용하여 구성을 구성 요소 클래스 자체로 이동합니다. 예제 RequiredAnnotationBeanPostProcess에서 언급한 것처럼, 어노테이션과 함께 BeanPostProcessor를 사용하는 것은 스프링 IoC 컨테이너를 확장하는 일반적인 방법입니다. 예를 들어, 스프링 2.0은 @Required 어노테이션으로 필수 속성을 적용할 수 있는 가능성을 소개했습니다. 스프링 2.5는 스프링의 의존성 삽입을 유도하는 일반적인 접근 방식을 따르는 것을 가능하게 했습니다. 본질적으로 @Autowired 어노테이션은 Autowiring Collaborators에서 설명한 것과 동일한 기능을 제공하지만, 보다 세분화된 제어와 폭 넓은 적용 가능성을 제공합니다. 스프링 2.5는 또한 @PostConstruct 및 @PreDestroy와 같은 JSR-250 어노테이션에 대한 지원을 추가했습니다. 스프링 3.0은 @Inject와 @Named와 같은 javax.inject 패키지에 포함된 JSR-330 (Dependency Injection for Java) 어노테이션을 지원했습니다. 이러한 어노테이션에 대한 자세한 내용은 관련 섹션에서 확인할 수 있습니다.

  • (+)

    • Annotation : 자바 소스 코드에 추가하여 사용하는 메타 데이터의 일종.

      • 컴파일러에 의해 생성되는 .class 파일에 포함되며 그 .class 파일을 통해 읽혀진다. (클래스들과 마찬가지로 바이트 코드 파일로 컴파일 및 저장되기 때문에 일반 자바 객체와 마찬가지로 처리될 수 있음)

어노테이션 삽입은 XML 삽입 전에 수행됩니다. 따라서 XML 구성은 두 가지 접근 방식을 통해 연결된 속성에 대한 어노테이션을 대체합니다.

항상 그렇듯이, 개별 Bean 정의로 어노테이션을 등록할 수 있지만, 다음 태그를 XML 기반 스프링 구성에 포함하여 암시적으로 등록할 수도 있습니다. (context 네임스페이스 포함) :

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

    <context:annotation-config/>

</beans>

(암시적으로 등록된 post-processor에는 AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor 및 앞서 언급했던 RequiredAnnotationBeanPostProcesso 가 포함됩니다)

  • (+)

    • BeanPostProcessor

      • 개발자가 원하는(또는 컨테이너의 기본 로직을 오버라이트하는) 인스턴스화 및 의존성 처리 로직 등을 구현할 수 있는 메소드를 정의하는 인터페이스.

    • <context:annotation-config/>의 추가를 통해, 어노테이션 관련하여 아래의 BeanPostProcessor들을 함께 등록하여 어노테이션을 인식하게 한다.

      • RequireAnnotationBeanPostProcessor -> @Required 처리

      • AutowiredAnnotationBeanPostProcessor -> @Autowired 처리

      • CommonAnnotationBeanPostProcessor -> @Resource, @PostConstruct, @PreDestroy 처리

      • ConfigurationClassBeanPostProcessor -> @Configuration 처리

!

<context:annotation-config/>는 이것이 정의된 동일한 응용 프로그램 컨텍스트에서의 빈들에 대한 어노테이션만 찾습니다. 즉, <context:annotation-config/>를 DispatcherServlet의 WebApplicationContext에 넣는다면, 서비스가 아니라 컨트롤러의 @Autowired Bean만 검사합니다. 자세한 정보는 DispatcherServlet 을 참조하십시오.

1.9.1. @Required

@Required 어노테이션은 다음 예제와 같이 빈 속성 setter 메소드에 적용됩니다 :

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

이 어노테이션은 영향을 받는 빈 프로퍼티가이 설정 시에 빈 정의의 명시적 프로퍼티 값이나 오토와이어링을 통해 채워 져야만 한다는 것을 의미합니다. 만일 영향을 받는 빈 프로퍼티가 채워지지 않는다면 컨테이너가 예외를 던집니다. 이것은 명시적으로 실패할 수 있으므로 NullPointerException인스턴스를 피할 수 있습니다. Assertion을 Bean 클래스 자체에 넣는 것을 권장합니다. (예: init 메소드) 이렇게 하면 컨테이너 외부에서 클래스를 사용하는 경우에도 필요한 참조 및 값들이 적용됩니다.

1.9.2. Using @Autowired

JSR 330의 @Inject어노테이션은 이 섹션에 포함된 예제에서 스프링의 @Autowired어노테이션 대신 사용할 수 있습니다. 자세한 내용은 여기를 참조하십시오.

  • (+)

    • @Autowired Vs. @Inject

      • 두 어노테이션 모두 타입에 의해 의존 관계를 자동을 연결해주는 기능을 한다.

      • @Autowired

        스프링에서 등장한 어노테이션. 스프링 이외에서 사용 불가능.

      • @Inject

        JSR에 실려 있는 자바 기반의 어노테이션. 스프링 이외에서도 사용 가능.

다음 예제와 같이 @Autowired 어노테이션을 생성자에 적용할 수 있습니다:

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

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

    // ...
}

스프링 프레임워크 4.3에서, 타겟 빈이이 단지 하나의 생성자만을 정의한다면 그러한 생성자에 대한 @Autowired어노테이션은 더 이상 필요하지 않습니다. 그러나 여러 생성자를 사용할 수 있는 경우에는, 사용할 컨테이너를 설명하기 위해 적어도 하나의 컨테이너에 어노테이션을 달아야 합니다.

다음 예제와 같이 @Autowired 어노테이션은 "전통적인" setter 메소드에도 적용할 수 있습니다:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

다음의 예제 처럼, 임의의 이름 및 복수의 인수를 가지는 메소드에도 어노테이션을 적용할 수 있습니다:

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 {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    private MovieCatalog movieCatalog;

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

    // ...
}

타겟 컴포넌트(예: MovieCatalog또는 CustomerPreferenceDao)가 @Autowired-annotated 주입 지점에 사용하는 타입에 따라 일관되게 선언되어 있는 지 확인하십시오. 그렇지 않으면, 실행 시 일치하는 타입이 없어 주입이 실패할 수 있습니다. 클래스 경로 검색을 통해 발견된 XML 정의된 빈 또는 컴포넌트 클래스들의 경우에 컨테이너는 일반적으로 구체적인 타입을 미리 알고 있습니다. 그러나, @Bean 팩토리 메소드의 경우에는 선언된 리턴 타입이 충분히 표현력이 있는 지 확인해야 합니다. 여러 인터페이스를 구현하는 컴포넌트 또는 해당 구현 타입에서 잠재적으로 참조되는 컴포넌트의 경우 팩토리 메소드에서 가장 구체적인 반환 타입을 선언하는 것을 고려하십시오. (최소한 해당 빈을 참조하는 주입 지점에서 요구되는 만큼 구체적으로)

다음의 예와 같이, 해당 타입의 배열을 필요로 하는 필드 또는 메소드에 어노테이션을 추가하는 것에 의해 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;
    }

    // ...
}

배열이나 목록의 항목을 특정 순서로 정렬 하려면, 타겟 빈들이 org.springframework.core.Ordered 인터페이스를 구현하거나 @Order또는 표준 @Priority어노테이션을 사용할 수 있습니다. 그렇지 않은 경우, 그 순서는 컨테이너 내의 대응하는 타겟 빈 정의의 등록 순서를 따릅니다.

@Order 어노테이션은 대상 클래스 레벨과 @Bean 메소드에서 선언할 수 있으며 잠재적으로 개별 빈의 정의로 선언할 수 있습니다 (동일한 빈 클래스를 사용하는 여러 정의의 경우). @Order값들은 주입 지점에서 우선 순위에 영향을 줄 수 있지만, 종속 관계 및 @DependsOn 선언에 의해 결정되는 직각적인 우려인 싱글톤 시작 순서에는 영향을 미치지 않습니다

표준 javax.annotation.Priority어노테이션은 메소드에서 선언될 수 없기 때문에 @Bean 레벨에서 사용할 수 없습니다. 그것의 의미는 @Order 값을 @Primary와 조합하여 각 타입에 대해 단일 빈에 모델링할 수 있습니다.

  • (+)

    • @Order

      • 스프링 4에서 도입한 기능으로, 같은 타입의 빈이 콜렉션에 오토와이어드될 때 순서를 지정하는 기능을 한다. (낮은 순서가 우선 순위가 높음)

예견된 키 타입이 String인 동안 타입화된 Map 인스턴스 조차 자동 실행될 수 있습니다. Map 값들은 예상되는 타입의 모든 빈을 포함하며, 키는 다음 예제와 같이 해당하는 빈의 이름을 포함합니다:

public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;

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

    // ...
}

기본적으로, 오토와이어링은 사용 가능한 후보 빈이 없다면 실패하게 된다. 기본 동작은 어노테이션이 선언된 메소드, 생성자 및 필드를 Required로 지정되어 있는 것으로 처리합니다. 다음 예제에서 이 동작을 설명된 대로 변경할 수 있습니다:

  • (+)

    • @Autowired를 적용한 필드를 반드시 설정할 필요가 없는 경우에는, @Autowired의 "required" 속성 값을 "false"로 지정한다.

      • 이렇게 지정된 경우에는, 해당 타입에 맞는 빈 객체가 존재하지 않는다해도 예외를 발생시키지 않는다.

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

클래스 당 하나의 어노테이션이 붙은 생성자만 required로 표기가 가능하지만, 필수가 아닌 여러개의 생성자에도 어노테이션을 붙일 수 있다. 이 경우에 각각은 주입이 가능한 빈들 사이에서 고려 되어지며 스프링은 의존성이 충족될 수 있는 가장 필요한 생성자를 사용합니다.

@Required와 상관없이 @Autowired의 “required” 속성은 권장됩니다. “required” 속성은 해당 프로퍼티가 오토와이어링 목적에는 필수가 아니라는 것을 나타냅니다. 오토와이어링을 할 수 없는 경우에 이 프로퍼티는 무시됩니다. 반면에, @Required는 컨테이너에 의해 지원되는 어떤 방법으로든 설정한 속성을 적용한다는 점에서 더 강력합니다. 주입되는 값이 없다면, 그에 대응하는 예외가 발생합니다.

또는 다음 예제와 같이 Java 8의 java.util.Optional을 통해 특정 종속성의 불필요한 특성을 표현할 수 있습니다:

  • (+)

    • Optional : NULL이 될 수도 있는 객체를 감싸고 있는 일종의 래퍼 클래스.

public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
        ...
    }
}

스프링 프레임워크 5.0에서 @Nullable 어노테이션을 사용할 수도 있습니다. (예: JSR-305의 javax.annotation.Nullable과 같은 모든 패키지의 모든 종류):

public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        ...
    }
}

BeanFactory, ApplicationContext, Environment, ResourceLoader, ApplicationEventPublisher, and MessageSource와 같이 잘 알려진 해결 가능한 종속성을 갖는 인터페이스에 @Autowired를 사용할 수도 있습니다. 이러한 인터페이스와 ConfigurableApplicationContext 또는 ResourcePatternResolver 와 같은 확장 인터페이스는 특별한 설정이 필요 없이 자동으로 해결됩니다. 다음 예제에서는 ApplicationContext 객체를 오토와이어링합니다:

public class MovieRecommender {

    @Autowired
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...
}

The @Autowired, @Inject, @Resource, and @Value 어노테이션은 스프링 BeanPostProcessor구현에 의해 처리됩니다. 즉, 자신의 BeanPostProcessor또는 BeanFactoryPostProcessor(있을 경우) 타입 내에 이러한 어노테이션을 적용할 수 없습니다. 이러한 타입들은 XML 또는 스프링 @Bean 메소드를 사용하여 명시적으로 “유선 연결”해야 합니다.

1.9.3. Fine-tuning Annotation-based Autowiring with @Primary

타입에 의한 오토와이어링은 여러 후보로 이어질 수 있으므로, 선택 프로세스를 더 잘 제어할 필요가 있습니다. 이를 수행하는 한 가지 방법이 스프링의 @Primary 어노테이션을 사용하는 것입니다. @Primary는 여러 빈이 단일 값 종속성에 대해 오토와이어드 될 후보가 될 때 특정 빈에 우선 순위를 부여해야 함을 나타냅니다. 정확하게 하나의 primary 빈이 후보들 사이에 존재하면, 이것이 오토와이어드 된 값이 됩니다.

firstMovieCatalog를 기본 MovieCatalog로 정의하는 다음 구성을 고려하십시오:

@Configuration
public class MovieConfiguration {

    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

    // ...
}

앞의 구성에서 다음 MovieRecommenderfirstMovieCatalog이 자동 주입됩니다:

public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;

    // ...
}

해당 빈의 정의는 다음과 같습니다:

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

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog" primary="true">
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <!-- inject any dependencies required by this bean -->
    </bean>

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

</beans>

1.9.4. Fine-tuning Annotation-based Autowiring with Qualifiers

@Primary는 하나의 기본 후보가 결정될 수 있을 때 여러 개의 인스턴스와 함께 타입에 의한 오토와이어링을 사용하는 효과적인 방법입니다. 선택 프로세스에 대한 더 많은 제어가 필요한 경우 스프링의 @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;
    }

    // ...
}

다음 예제는 해당 빈의 정의를 보여줍니다.

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

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="action"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

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

</beans>
  1. "main” qualifier 값을 가진 빈은 동일한 값으로 규정된 생성자 인수와 연결됩니다.

  2. "action" qualifier 값을 가진 빈은 동일한 값으로 규정된 생성자 인수와 연결됩니다.

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

앞서 애기했듯이 qualifier는 Set 같은 타입 있는 컬렉션에도 적용됩니다. 이 경우에 선언된 qualifier에 일치하는 모든 빈이 컬렉션으로 주입됩니다. 이는 qualifier는 유일할 필요가 없다는 의미입니다. 오히려 필터링 기준을 구성합니다. 예를 들어 “action”이라는 동일한 qualifier 값을 가진 여러 개의 MovieCatalog빈을 정의할 수 있습니다. 이 빈들은 모두 @Qualifier("action")어노테이션이 붙은 Set로 주입될 것입니다.

타입이 일치하는 후보 빈들 내에서 대상 빈 이름에 대해 qualifier 값을 선택하게 하면, 주입 지점에 @Qualifier 어노테이션이 필요하지 않습니다. 유일하지 않은 의존성 상황에 대해 다른 해결 지시자(예: qualifier 또는 primary 표시)가 없다면, 스프링은 대상 빈 이름에 대해 주입 지점 이름 (즉, 필드 이름 또는 매개 변수 이름)을 일치 시킵니다.

이름으로 어노테이션 중심의 주입을 표현하려는 경우, @Qualifier값으로 빈 이름을 참조하는 것이 기술적으로 가능하더라도 @Autowired를 주로 사용하지 마십시오. 대신, 의미적으로 유일한 이름의 특정 대상 컴포넌트를 식별하기 위해 정의된 JSR-250 @Resource어노테이션을 사용하십시오. 선언된 타입은 일치하는 빈을 찾는 과정과는 관계가 없습니다. @Autowired는 다소 다른 의미를 가지고 있습니다. 타입에 의해 후보 빈을 선택한 후, 지정된 Stringqualifier 값은 해당 타입의 선택된 후보 내에서만 고려됩니다. (예: 동일한 qualifier 레이블로 표시된 빈에 대한 계정 qualifier 일치)

  • (+) @Autowired Vs. @Resource

    • 공통 : 적절한 의존 객체를 빈 객체들 중에서 찾아 주입

    • 차이 : 빈 객체의 id와 class 중 어떤 것을 우선하여 검색하는 지에 대해 차이가 존재

      • @Autowird : 스프링이 타입(class)을 우선으로 빈 객체를 검색

      • @Resource : 스프링이 이름(id)을 우선으로 빈 객체를 검색

        -> 이름으로 찾을 거면, 타입 우선인 Autowired를 사용하지 말고 차라리 Resource를 사용하는 을 권장.

콜렉션, Map, 또는 배열 타입으로 정의된 빈의 경우 @Resource는 고유한 이름으로 특정 Collection 또는 배열 빈을 참조하는 좋은 솔루션입니다. 즉, 4.3의 컬렉션에서 @Bean 반환 타입 시그니처 또는 컬렉션 상속 계층 구조에서 요소 타입 정보가 유지되는 한 Map 및 배열 타입을 스프링의 @Autowired타입 일치 알고리즘과 일치시킬 수 있습니다. 이 경우 qualifier 값을 사용하여 이전 단락에서 설명한대로 동일한 타입의 콜렉션 중에서 선택할 수 있습니다.

4.3에서 @Autowired는 또한 주입에 대한 자체 참조(즉, 현재 주입된 빈에 대한 참조)를 고려합니다. 셀프 주입은 대체적인 기능입니다. 다른 컴포넌트에 대한 정기적인 종속성이 항상 우선합니다. 이러한 의미에서 자체 참조는 정기적인 후보자 선정에 참여하지 않아서 특히 중요하지 않습니다. 반대로, 그들은 항상 최우선 순위로 끝납니다. 실제로, 셀프 참조는 마지막 수단으로만 사용해야 합니다. (예: 빈의 트랜잭션 프록시를 통해 동일한 인스턴스의 다른 메소드를 호출하는 경우) 이러한 시나리오에서 영향을 받은 메소드를 별도의 delegate 빈으로 분해하는 것을 고려하십시오. 또는 @Resource를 사용할 수 있습니다. @Resource는 고유의 이름으로 현재 빈으로 프록시를 다시 가져올 수 있습니다.

@Autowired는 필드, 생성자 및 다중 인수 메서드에 적용되므로 매개 변수 수준에서 qualifier 어노테이션을 좁힐 수 있습니다. 반대로 @Resource는 단일 인수를 사용하는 필드 및 빈 등록 정보 설정 메소드에서만 지원됩니다. 따라서 주입 대상이 생성자 또는 다중 인수 메서드인 경우 qualifier를 사용해야 합니다.

  • (+) @Autowired 및 @Resource 적용 순서

고유의 사용자 정의 qualifier 어노테이션을 만들 수 있습니다. 이렇게 하려면, 다음 예제와 같이 어노테이션을 정의하고 정의 내에서 @Qualifier 어노테이션을 제공하십시오:

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

    String value();
}
  • (+) 어노테이션 커스터마이징에 사용하는 메타 데이터

    • @Target : 어디에 선언?

      • ElementType.TYPE : 클래스, 인터페이스, enum 선언부

      • ElementType.CONSTRUCTOR : 생성자 선언부

      • ElementType.LOCAL_VARIABLE : 지역 변수 선언부

      • ElementType.METHOD : 메소드 선언부

      • ElementType.PACKAGE : 패키지 선언부

      • ElementType.PARAMETER : 파라미터 선언부

    • @Retention : 유지 방식은?

      • RetentionPolicy.RUNTIME : 컴파일러에 의해 class 파일에 추가되고 런타임 시 VM에서 유지

      • RetentionPolicy.SOURCE : 컴파일 시 class 파일에 추가되지 않는다.

      • RetentionPolicy.CLASS : 클래스 안에 애노테이션이 추가되지만 런타임 시 VM에서는 사용되지 않는다.

그런 다음, 다음의 예제와 같이 오토와이어드 된 필드 및 매개 변수에 사용자 지정 qualifier를 제공할 수 있습니다:

public class MovieRecommender {

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

    private MovieCatalog comedyCatalog;

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

    // ...
}

다음 단계는 자동 엮어질 후보 빈 정의에 해당 정보를 추가하는 것입니다. <qualifier/>태그를 <bean/> 태그의 하위 요소로 추가한 다음, 사용자 정의 qualifier 어노테이션과 일치하도록 typevalue을 지정할 수 있습니다. 타입은 어노테이션의 패키지를 포함한 전체 클래스 명과 일치합니다. 또는, 이름이 충돌하는 위험이 없다면 ‘짧은’ 클래스 이름을 사용할 수 있습니다. 다음 예제는 두 가지 접근 방법을 보여줍니다:

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

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="Genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="example.Genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

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

</beans>

Classpath Scanning and Managed Components (1.10) 에서는 XML로 qualifier 메타데이터를 제공하는 어노테이션 기반의 대안을 볼 수 있습니다. 구체적으로는 Providing Qualifier Metadata with Annotations를 참조하십시오.

경우에 따라서는 값 없이 어노테이션을 사용하는 것으로 충분할 수 있습니다. 이것은 어노테이션이 보다 일반적인 목적을 위해 사용되고 여러 개의 다른 타입의 종속성으로 적용될 수 있을 때 유용할 수 있습니다. 예를 들어, 인터넷 연결이 불가능할 때 검색될 수 있는 Offline 카탈로그를 제공할 수 있습니다. 먼저, 다음 예제와 같이 간단한 어노테이션을 정의합니다.

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

}

그런 다음, 다음 예제와 같이 어노테이션을 오토와이어드할 필드 또는 속성에 추가하십시오:

public class MovieRecommender {

    @Autowired
    @Offline 
    private MovieCatalog offlineCatalog;

    // ...
}

이 라인에 @Offline 어노테이션을 추가합니다.

이제 빈의 정의는 다음 예와 같이 qualifier 타입만 필요합니다:

<bean class="example.SimpleMovieCatalog">
    <qualifier type="Offline"/> 
    <!-- inject any dependencies required by this bean -->
</bean>

이 요소는 qualifier를 구체화합니다.

간단한 값 속성 대신에 이름이 지정된 속성을 허용하는 qualifier 어노테이션을 정의할 수도 있습니다. 만일 여러 개의 속성 값들이 자동 엮어질 필드나 매개 변수에 지정된다면, 빈 정의는 오토와이어링 대상이라고 생각되는 모든 속성 값과 일치해야 합니다. 예를 들어, 다음 어노테이션 정의를 고려하십시오:

@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/>태그 내에서 제공되는 값을 대신 사용하게 됩니다. (마지막 2개의 빈 정의 참조):

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

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute key="format" value="VHS"/>
            <attribute key="genre" value="Action"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute key="format" value="VHS"/>
            <attribute key="genre" value="Comedy"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="DVD"/>
        <meta key="genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="BLURAY"/>
        <meta key="genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

</beans>

1.9.5. Using Generics as Autowiring Qualifiers

@Qualifier 어노테이션 외에도 암시적인 형태의 자격으로 자바 제네릭 타입을 사용할 수 있습니다. 예를 들어, 다음 구성을 가지고 있다고 가정합니다:

  • (+) 스프링 4.0 부터 자바 제네릭 입을 주입받을 수 있음. (4.0 미만도 가능은 했지만 번거로운 추가 작업이 필요)

@Configuration
public class MyConfiguration {

    @Bean
    public StringStore stringStore() {
        return new StringStore();
    }

    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore();
    }
}
  • (+) stringStor, integerStore 가 해당 빈을 나타내는 qualifier라고 생각하면 됨.

  • (+) 반환 타입은 Store<T>

앞의 빈이 제네릭 인터페이스(즉, Store<String>and Store<Integer>)를 구현한다고 가정하면, 다음 예제와 같이 Store 인터페이스를 호출하고 제네릭이 qualifier로 사용됩니다:

@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean

@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

제네릭 qualifier는 리스트, 맵 인스턴스, 배열을 오토와이어링할 때에도 적용됩니다. 다음 예제에서는 제네릭 리스트를 오토와이어링합니다:

// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;

1.9.6. Using CustomAutowireConfigurer

CustomAutowireConfigurerBeanFactoryPostProcessor로, 사용자 정의 qualifier 어노테이션 타입을 스프링의 @Qualifier로 어노테이션 처리하지 않고도 등록할 수 있습니다. 다음 예제에서는 CustomAutowireConfigurer를 사용하는 방법을 보여줍니다:

  • (+)

    • CustomAutowireConfigurerBeanFactoryPostProcessor를 구현함.

      • BeanFactoryPostProcessorCustomAutowireConfigurer 유형을 편리하게 등록할 수 있는 구현임.

      • BeanFactoryPostProcessor

        : 컨텍스트의 기본 빈 펙토리의 빈 특성 값을 적용하여 응용 프로그램 컨텍스트의 빈 정의를 사용자 정의하여 수정할 수 있도록 하는 인터페이스. (순서 등)

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

AutowireCandidateResolver는 오토와이어링 후보를 다음과 같이 결정합니다:

  • 각 빈 정의의 default-autowire-candidates값 (자바 5 이전 버전)

  • 요소에서 사용 가능한 모든 default-autowire-candidates패턴 (자바 5 이전 버전)

  • @Qualifier어노테이션과 CustomAutowireConfigurer에 등록된 모든 사용자 지정 어노테이션의 존재 (자바 5 이상의 버전)

    • (+) 자바 5 이전의 버전은 qualifier 어노테이션을 지원하지 않음.

자바 버전에 상관없이 여러 개의 빈이 오토와이어링 후보로 자격이 주어졌을 때, “primary” 후보가 결정되는 방식은 동일합니다 : 후보 중 정확하게 primary 속성이 true로 설정된 빈이 있다면, 이것이 선택됩니다.

1.9.7. Injection with @Resource

스프링은 필드 또는 빈 속성 setter 메소드에서 JSR-250 @Resource어노테이션을 사용하여 주입을 지원합니다. 이는 Java EE 5 및 6 (예: JSF 1.2 관리 빈 또는 JAX-WS 2.0 엔드 포인트)의 공통 패턴입니다. 스프링은 스프링 관리 객체에 대해서도 이 패턴을 지원합니다

@Resource는 name 속성을 취합니다. 기본적으로 스프링은 그 값을 주입할 빈 이름으로 해석합니다. 즉, 다음 예에서와 같이 이름 별 의미 체계를 따릅니다:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

이 라인에 @Resource를 주입합니다.

이름을 명시적으로 지정하지 않으면, 기본 이름이 필드 이름 또는 setter 메소드에서 파생됩니다. 필드의 경우, 필드 이름이 필요합니다. Setter 멧드의 경우, 빈 속성 이름을 사용합니다. 다음 예제에서는 setter 메소드에 삽입된 movieFinder라는 빈을 갖게 됩니다:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

어노테이션으로 제공된 이름은 CommonAnnotationBeanPostProcessor가 인식하는 ApplicationContext에 의해 빈 이름으로 해석됩니다. 만일 스프링의 SimpleJndiBeanFactory가 명시적으로 설정되어 있다면, 이름은 JNDI를 통해 처리될 수 있습니다. 그러나, 기본적인 동작에 의존하고 간접적인 수준을 유지하기 위해 스프링의 JADI 검색 기능을 간단히 사용하기를 권장합니다.

  • (+) JNDI (Java Naming and Directory Interface)

    • 이름과 객체를 매핑 시켜주는 Naming Service를 감싸는 인터페이스 집합으로서, 표준 방법을 통해 naming 서비스에 접근할 수 있도록 해준다.

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

그러므로 다음 예제에서 customerPreferenceDao필드는 customerPreferenceDao 라는 이름의 빈을 먼저 찾고, 못 찾으면 customerPreferenceDao타입과 일치하는 주요 타입을 찾습니다:

public class MovieRecommender {

    @Resource
    private CustomerPreferenceDao customerPreferenceDao;

    @Resource
    private ApplicationContext context; 

    public MovieRecommender() {
    }

    // ...
}

context 필드는 알고 있는 해결할 수 있는 의존성 타입인 ApplicationContext에 기반해서 주입됩니다.

1.9.8. Using @PostConstruct and @PreDestroy

CommonAnnotationBeanPostProcessor@Resource 어노테이션 뿐만 아니라 JSR-250 라이프 사이클 어노테이션을 인식합니다. 스프링 2.5에서 소개된 이 어노테이션에 대한 지원은 ininitialization callbacksdestruction callbacks에서 설명한 것에 대한 또 다른 대안을 제공합니다. CommonAnnotationBeanPostProcessor가 스프링 ApplicationContext내에 등록되어 있으면, 이러한 어노테이션 중 하나를 전달하는 메서드는 해당 라이프 사이클 인터페이스 메소드 또는 명시적으로 선언된 콜백 메소드와 같은 라이프 사이클의 동일한 지점에서 호출됩니다. 다음 예제에서, 캐시는 초기화 시에 미리 populate되고, 삭제 시에 clear됩니다:

  • (+)

    • PostConstruct And. @PreDestroy

      • @PostConstruct : 객체(빈)가 생성된 후 별도의 초기화 작업을 위해 실행하는 메소드를 선언한다.

      Cf. 초기화 메서드 : 빈 객체가 생성되고 DI 작업까지 마친 다음 실행되는 메서드. (기본적으로 객체의 초기화 작업은 생성자에서 진행하지만, DI를 통해 빈이 주입된 후에 초기화할 작업이 있을 경우, 초기화 메서드를 이용)

      • @PreDestroy : 스프링 컨테이너에서 객체(빈)를 제거하기 전에 해야 할 작업이 있을 때 실행하는 메소드를 선언한다.

      • (공통) CommonAnnotationBeanPostProcessor 클래스를 빈으로 등록시키거나 <context:annotation-config/>태그를 사용하면 된다.

public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

다양한 라이프 사이클 메커니즘 결합의 효과에 대한 자세한 내용은 Combining Lifecycle Mechanisms를 참조하십시오.

Last updated