본문 바로가기

Web/spring

[Spring Framework core] 1.9. Annotation-based Container Configuration (2)

728x90

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-autowired-annotation-qualifiers

 

Core Technologies

In the preceding scenario, using @Autowired works well and provides the desired modularity, but determining exactly where the autowired bean definitions are declared is still somewhat ambiguous. For example, as a developer looking at ServiceConfig, how do

docs.spring.io

 

틀린 해석이 있다면 알려주세요

 


1.9.3. Fine-tuning Annotation-based Autowiring with Qualifiers ~1.9.4. Using Generics as Autowiring Qualifiers

 

1.9.3. Fine-tuning Annotation-based Autowiring with Qualifiers

 

@Primary 는 하나의 기본 후보를 결정할 수 있을 때 여러 인스턴스의 타입별 autowiring 을 사용하는 효과적인 방법이다

selection process에 대해 더 많은 컨트롤이 필요할 경우 @Qualifier 애노테이션을 이용한다.

한정자 값을 특정 인수와 연결하여 type 일치시켜 특정 Bean이 선택되게 할 수 있다

 

public class MovieRecommender {

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

    // ...
}

@Qualifier 애노테이션을 사용하여 개별 constructor args 또는 method 매개 변수에 대한 애노테이션을 지정해준다

public class MovieRecommender {

    private final MovieCatalog movieCatalog;

    private final CustomerPreferenceDao customerPreferenceDao;

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

    // ...
}

 

 

 

 

해당 bean definition 이다

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

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> // main 생성자인수와 연결

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

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="action"/>  // action 생성자인수와 연결

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

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

</beans>

 

fallback 일치의 경우 bean 이름은 기본 qualifier value로 간주한다

중첩된 qualifier element 대신 id가 "main"인 빈을 정의해서 동일한 결과를 얻을 수 있다.

그러나! 이 규칙을 사용하여 특정 bean 이름을 참조할 수 있지만, @Autowired는 기본적으로 about type-driven injection with optional semantic qualifiers 에 관한 것이다

qualitify value 값이 bean 이름 fallback을 사용하더라도 set of type 을 매치한다는 것을 의미한다

고유한 bean ID에 대한 참조를 의미로 표현하는 것은 아니다

좋은 qualitify value란 앞의 예제처럼 익명의 bean definition 이 있을 때 자동 생될 수 있는 bean ID와 독립적인 component의 특성을 표현하는(or EMEA) 지속성이다

 

Qualifiers는 유형이 지정된 collection에서도 적용된다

예를 들어, Set<MovieCatalog>에 적용된다. 선언된 qulifiers를 따라 일치하는 모든 빈이 컬렉션으로 주입된다

이는 qualifier가 고유할 필요가 없다는 뜻이다. 필터링의 기준을 구성한다고 생각하면 된다

동일한 qualifier 값을 "action"이라고 사용하여 여러 MovieCatalog 빈을 정의할 수 있으며, 모두 @Qualifier("action")에 주석이 달린 Set<MovieCatalog>에 Injection 된다

 

 

** type-matching candidates 내에서 target bean 이름에 대해서 qualifier value를 선택하도록 하려면 injection point에서 @Qualifier 애노테이션을 달 필요가 없다. 고유하지않은 종속성 상황에 대해서는 다른 resolution indicator표시자(qualifier or primary marker) 가 없으면 Spring은 injection point name(필드이름 또는 매개변수 이름)을 대상 bean 이름에 대해 일치시키고 선택한다.

 

 

That said, if you intend to express annotation-driven injection by name, do not primarily use @Autowired, even if it is capable of selecting by bean name among type-matching candidates. Instead, use the JSR-250 @Resource annotation, which is semantically defined to identify a specific target component by its unique name, with the declared type being irrelevant for the matching process. @Autowired has rather different semantics: After selecting candidate beans by type, the specified String qualifier value is considered within those type-selected candidates only (for example, matching an account qualifier against beans marked with the same qualifier label).

For beans that are themselves defined as a collection, Map, or array type, @Resource is a fine solution, referring to the specific collection or array bean by unique name. That said, as of 4.3, you can match collection, Map, and array types through Spring’s @Autowired type matching algorithm as well, as long as the element type information is preserved in @Bean return type signatures or collection inheritance hierarchies. In this case, you can use qualifier values to select among same-typed collections, as outlined in the previous paragraph.

As of 4.3, @Autowired also considers self references for injection (that is, references back to the bean that is currently injected). Note that self injection is a fallback. Regular dependencies on other components always have precedence. In that sense, self references do not participate in regular candidate selection and are therefore in particular never primary. On the contrary, they always end up as lowest precedence. In practice, you should use self references as a last resort only (for example, for calling other methods on the same instance through the bean’s transactional proxy). Consider factoring out the affected methods to a separate delegate bean in such a scenario. Alternatively, you can use @Resource, which may obtain a proxy back to the current bean by its unique name.

 

 

동일한 구성 클래스에서 @Bean method의 결과를 inject 하려고 시도하는 것은 사실 self-refrence scenario로 보면 된다

실제 필요한 메소드 서명에서 이러한 reference를 LAZY하게 해결하려고 하거나 (구성 클래스의 autowired 필드와 반대로) 영향을 받는 @Bean메소드를 정적으로 선언하여 포함하는 구성 클래스 instance 및 lifecycle 주기에서 분리한다

그렇지않으면 대체되는 단계에서 이러한 bean이 고려되며 대신 기본 후보로 선택된  사용가능한 구성 클래스와 일치시킨다

 

 

@Autowired applies to fields, constructors, and multi-argument methods, allowing for narrowing through qualifier annotations at the parameter level. In contrast, @Resource is supported only for fields and bean property setter methods with a single argument. As a consequence, you should stick with qualifiers if your injection target is a constructor or a multi-argument method.

You can create your own custom qualifier annotations. To do so, define an annotation and provide the @Qualifier annotation within your definition, as the following example shows:

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

    String value();
}
 
 
 

autowired 필드 및 매개변수에 custom 지정 qualifier 를 제공

Then you can provide the custom qualifier on autowired fields and parameters, as the following example shows:

public class MovieRecommender {

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

    private MovieCatalog comedyCatalog;

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

    // ...
}
 

Next, you can provide the information for the candidate bean definitions. You can add <qualifier/> tags as sub-elements of the <bean/> tag and then specify the type and value to match your custom qualifier annotations. The type is matched against the fully-qualified class name of the annotation. Alternately, as a convenience if no risk of conflicting names exists, you can use the short class name. The following example demonstrates both approaches:

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

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-scanning-qualifiers

 

Core Technologies

In the preceding scenario, using @Autowired works well and provides the desired modularity, but determining exactly where the autowired bean definitions are declared is still somewhat ambiguous. For example, as a developer looking at ServiceConfig, how do

docs.spring.io

 

 

 

 

 

값없이 애노테이션만 설정하요 사용할 수도 있다.

애노테이션이 여러 다른 유형의 종속성에 적용될 수 있을 때 유용하다

 

 

다음 예시는 인터넷 연결이 끊겼을 때를 체크하여 검색할 수 있는 오프라인 카탈로그를 제공하는 예시이다

 

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

Then add the annotation to the field or property to be autowired, as shown in the following example:

public class MovieRecommender {

    @Autowired
    @Offline //This line adds the @Offline annotation.
    private MovieCatalog offlineCatalog;

    // ...
}
 
 

Now the bean definition only needs a qualifier type, as shown in the following example:

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

 

 

 

 

 

단순 속성 외에 명명 속성을 사용하는 custom qulifier 애노테이션 정의의 예이다

You can also define custom qualifier annotations that accept named attributes in addition to or instead of the simple value attribute. If multiple attribute values are then specified on a field or parameter to be autowired, a bean definition must match all such attribute values to be considered an autowire candidate. As an example, consider the following annotation definition

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

    String genre();

    Format format();
}
 

In this case Format is an enum, defined as follows:

public enum Format {
    VHS, DVD, BLURAY
}
 

 

The fields to be autowired are annotated with the custom qualifier and include values for both attributes: genre and format, as the following example shows:

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;

    // ...
}
 

Finally, the bean definitions should contain matching qualifier values. This example also demonstrates that you can use bean meta attributes instead of the <qualifier/> elements. If available, the <qualifier/> element and its attributes take precedence, but the autowiring mechanism falls back on the values provided within the <meta/> tags if no such qualifier is present, as in the last two bean definitions in the following example:

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://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.4. Using Generics as Autowiring Qualifiers

@Qualifier 애노테이션 말고도 일반 type을 암시적 규정 형식으로 사용할 수 있다

 

@Configuration
public class MyConfiguration {

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

    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore();
    }
}

예를 들어서 이런 구성이 있는데, 이 빈이 Store<String>, Store<Integer> 인터페이스를 구현한다고 가정하면

 

qualifier 할 수 있다

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

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

 

Generic qualifiers also apply when autowiring lists, Map instances and arrays. The following example autowires a generic List:

// 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;
 

 

 

 

728x90