1. 概要

このチュートリアルでは、Springの@AliasForアノテーションについてについて学習します。

まず、それが使用されているフレームワーク内からの例を見ていきます。 次に、いくつかのカスタマイズされた例を見ていきます。

2. 注釈

@AliasFor は、バージョン4.2以降のフレームワークの一部です。 いくつかのコアSpringアノテーションが更新され、このアノテーションが含まれるようになりました。

これを使用して、単一の注釈内またはメタ注釈から構成される注釈内の属性を装飾できます。 つまり、メタ注釈は、別の注釈に適用できる注釈です。

同じアノテーションで、 @AliasForを使用して属性のエイリアスを宣言し、それらを交換可能に適用できるようにします。 または、合成アノテーションで使用して、メタアノテーションの属性をオーバーライドすることもできます。 つまり、合成アノテーションの属性を@AliasForで装飾すると、メタアノテーションの指定された属性がオーバーライドされます。

興味深いことに、 @Bean @ComponentScan @Scope @RequestMapping @などのメニーコアSpringアノテーションRestController は、@AliasForを使用して内部属性エイリアスを構成するようになりました。

注釈の定義は次のとおりです。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {
    @AliasFor("attribute")
    String value() default "";
    
    @AliasFor("value")
    String attribute() default "";

    Class<? extends Annotation> annotation() default Annotation.class;
}

重要なのは、このアノテーションを明示的にだけでなく暗黙的にも使用できることです。 暗黙的な使用は、アノテーション内のエイリアスにのみ制限されます。 それに比べて、メタアノテーションの属性を明示的に使用することもできます。

これについては、次のセクションの例で詳しく説明します。

3. アノテーション内の明示的なエイリアス

単一のアノテーション内の明示的なエイリアスを理解するために、コアのSpringアノテーション@ComponentScanについて考えてみましょう。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};
...
}

ご覧のとおり、ここではvaluebasePackagesのエイリアスとして明示的に定義されており、その逆も同様です。 これは、それらを交換可能に使用できることを意味します

事実上、これら2つの使用法は似ています。

@ComponentScan(basePackages = "com.baeldung.aliasfor")

@ComponentScan(value = "com.baeldung.aliasfor")

さらに、2つの属性も default としてマークされているので、これをより簡潔に記述しましょう。

@ComponentScan("com.baeldung.aliasfor")

また、Springがこのシナリオで義務付けている実装要件がいくつかあります。 まず、エイリアス属性は同じデフォルト値を宣言する必要があります。 さらに、それらは同じリターンタイプを持つ必要があります。 これらの制約のいずれかに違反すると、フレームワークはAnnotationConfigurationExceptionをスローします。

4. メタアノテーションの属性の明示的なエイリアス

次に、メタアノテーションの例を見て、そこから合成アノテーションを作成しましょう。 次に、カスタムエイリアスでのエイリアスの明示的な使用法を確認します。

まず、フレームワークアノテーションRequestMappingをメタアノテーションとして考えてみましょう。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default "";
    
    @AliasFor("path")
    String[] value() default {};

    @AliasFor("value")
    String[] path() default {};

    RequestMethod[] method() default {};
    ...
}

次に、そこから合成アノテーションMyMappingを作成します。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@RequestMapping
public @interface MyMapping {
    @AliasFor(annotation = RequestMapping.class, attribute = "method")
    RequestMethod[] action() default {};
}

ご覧のとおり、 @MyMapping では、action@RequestMappingの属性methodの明示的なエイリアスです。 つまり、作成されたアノテーションのアクションは、メタアノテーションのメソッドをオーバーライドします。

アノテーション内のエイリアスと同様に、メタアノテーション属性エイリアスも同じリターンタイプである必要があります。 たとえば、この場合は RequestMethod[]です。 さらに、属性 annotation は、 annotation = RequestMapping.class の使用法のように、メタアノテーションを参照する必要があります。

実例を示すために、MyMappingControllerというコントローラークラスを追加しましょう。 そのメソッドをカスタムアノテーションで装飾します。

具体的には、ここでは @MyMapping route、およびactionの2つの属性のみを追加します。

@Controller
public class MyMappingController {

    @MyMapping(action = RequestMethod.PATCH, route = "/test")
    public void mappingMethod() {}
    
}

最後に、明示的なエイリアスがどのように動作するかを確認するために、簡単なテストを追加しましょう。

@Test
public void givenComposedAnnotation_whenExplicitAlias_thenMetaAnnotationAttributeOverridden() {
    for (Method method : controllerClass.getMethods()) {
        if (method.isAnnotationPresent(MyMapping.class)) {
            MyMapping annotation = AnnotationUtils.findAnnotation(method, MyMapping.class);
            RequestMapping metaAnnotation = 
              AnnotationUtils.findAnnotation(method, RequestMapping.class);

            assertEquals(RequestMethod.PATCH, annotation.action()[0]);

            assertEquals(0, metaAnnotation.method().length);
        }
    }
}

ご覧のとおり、カスタムアノテーションの属性 action は、メタアノテーション@RequestMappingの属性methodをオーバーライドしました。

5. アノテーション内の暗黙のエイリアス

これを理解するために、@MyMapping内にさらにいくつかのエイリアスを追加しましょう。

@AliasFor(annotation = RequestMapping.class, attribute = "path")
String[] value() default {};

@AliasFor(annotation = RequestMapping.class, attribute = "path")
String[] mapping() default {};
    
@AliasFor(annotation = RequestMapping.class, attribute = "path")
String[] route() default {};

この状況では、 value mapping 、および route は、@RequestMappingpathの明示的なメタアノテーションオーバーライドです。 ]。 したがって、それらは相互の暗黙のエイリアスでもあります。 つまり、 @MyMappingの場合、これら3つの属性を同じ意味で使用できます。

これを示すために、前のセクションと同じコントローラーを使用します。 そして、ここに別のテストがあります:

@Test
public void givenComposedAnnotation_whenImplictAlias_thenAttributesEqual() {
    for (Method method : controllerClass.getMethods()) {
        if (method.isAnnotationPresent(MyMapping.class)) {
            MyMapping annotationOnBean = 
              AnnotationUtils.findAnnotation(method, MyMapping.class);

            assertEquals(annotationOnBean.mapping()[0], annotationOnBean.route()[0]);
            assertEquals(annotationOnBean.value()[0], annotationOnBean.route()[0]);
        }
    }
}

特に、コントローラーメソッドのアノテーションで属性valuemappingを定義していません。 ただし、は、routeと同じ値を暗黙的に保持します。

6. 結論

このチュートリアルでは、 SpringFrameworkの@AliasForアノテーションについて学習しました。 この例では、明示的および暗黙的な使用シナリオを確認しました。

いつものように、ソースコードはGitHubから入手できます。