天天看點

@AliasFor注解的使用注意事項詳解

作者:java小悠

一、@AliasFor注解簡介

@AliasFor注解是在spring源碼當中提供的,見名知義,他是為了别名而自定義的注解!

但這并不是java原生支援的,需要通過Spring中提供的工具類org.springframework.core.annotation.AnnotationUtils或者org.springframework.core.annotation.AnnotatedElementUtils來解析。AnnotatedElementUtils内部還是調用的AnnotationUtils

二、使用場景

  • 在注解中一對屬性上通過聲明@AliasFor,進行屬性互換。
  • 在注解上聲明了另一個注解,對另一個注解的屬性進行别名覆寫(也可以了解為将一個注解上的屬性值傳遞給另一個注解

三、 注意事項(重點關注)

指定别名,屬性互換的情況下,通過AnnotationUtils仍然可以擷取到值,而通過java原生的方式則無法擷取。 是因為Spring其實是自己實作了jdk動态的攔截器來實作别名功能. 但是: 如果同時設定,并且互為别名的兩個屬性值不一樣就會報錯,抛出如下異常:

java複制代碼Caused by: org.springframework.core.annotation.AnnotationConfigurationException: Different @AliasFor mirror values for annotation [com.sysmenu.annotion.MenuAuthCheck] declared on com.controller.ZnjProjectNoticeController.publishNoticeAnnouncement(com.dto.ZnjProjectNoticeAnnouncementInsertDTO); attribute 'permission' and its alias 'value' are declared with values of [lecture] and [lecture_report].
	at org.springframework.core.annotation.AnnotationTypeMapping$MirrorSets$MirrorSet.resolve(AnnotationTypeMapping.java:711)
	at org.springframework.core.annotation.AnnotationTypeMapping$MirrorSets.resolve(AnnotationTypeMapping.java:666)
	at org.springframework.core.annotation.TypeMappedAnnotation.<init>(TypeMappedAnnotation.java:134)
	at org.springframework.core.annotation.TypeMappedAnnotation.<init>(TypeMappedAnnotation.java:118)
	at org.springframework.core.annotation.TypeMappedAnnotation.of(TypeMappedAnnotation.java:599)
	at org.springframework.core.annotation.MergedAnnotation.of(MergedAnnotation.java:610)
	at org.springframework.core.type.classreading.MergedAnnotationReadingVisitor.visitEnd(MergedAnnotationReadingVisitor.java:96)

           

是以互為别名,指定一個即可,兩個都會有相同的值

四、使用步驟

1.指定别名,屬性互換

1.1自定義注解

java複制代碼@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MenuAuthCheck {
    @AliasFor("value")
    String permission() default "";

    @AliasFor("permission")
    String value() default "";
}
           

1.2使用

java複制代碼@MenuAuthCheck("test_admin")
           

1.3注意事項

  1. 構成别名對的每個屬性都必須用@AliasFor注釋,并且屬性或值必須引用别名對中的另一個屬性。
  2. 這兩個屬性的必須擁有相同的傳回值類型。
  3. 别名屬性必須聲明預設值。
  4. 這兩個屬性必須擁有相同的預設值。

1.4 Spring中的@RequestMapping注解

我們通常直接使用

java複制代碼@RequestMapping("/web"")
           

value注解注釋也表明 這是 path的别名

java複制代碼@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default "";
 
 		/**
	 * The primary mapping expressed by this annotation.
	 * <p>This is an alias for {@link #path}. For example,
	 * {@code @RequestMapping("/foo")} is equivalent to
	 * {@code @RequestMapping(path="/foo")}.
	 * <p><b>Supported at the type level as well as at the method level!</b>
	 * When used at the type level, all method-level mappings inherit
	 * this primary mapping, narrowing it for a specific handler method.
	 * <p><strong>NOTE</strong>: A handler method that is not mapped to any path
	 * explicitly is effectively mapped to an empty path.
	 */
    @AliasFor("path")
    String[] value() default {};
 
    @AliasFor("value")
    String[] path() default {};
 
    RequestMethod[] method() default {};
 
    String[] params() default {};
 
    String[] headers() default {};
 
    String[] consumes() default {};
 
    String[] produces() default {};
}

           

2.将一個注解上的屬性值傳遞給另一個注解對另一個注解的屬性進行别名覆寫

2.1舉例說明

java複制代碼@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestAnnotation {
 
    @AliasFor("name")
    String value() default "";
 
    @AliasFor("value")
    String name() default "";
 
}
           
java複制代碼@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MyAnnotation
public @interface TestAliasAnnotation {
 
    @AliasFor(annotation = TestAnnotation .class, attribute = "value")
    String name() default "";
}
           

将TestAliasAnnotation 注解中的name屬性傳遞給TestAnnotation 注解的value屬性,進行覆寫

2.2注意事項

  • 被标記@AliasFor的屬性和atttibute所指向的元注解屬性必須有相同的傳回值類型。

3.組合多個注解,通過一個注解達到使用多個注解的效果

3.1 這是3個注解

java複制代碼@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation1 {
    String value();
}
 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation2 {
    String name();
}
 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation3 {
    String desc();
}
           

使用@AliasFor注解來定義一個新注解,将這三個注解組合起來如下:

3.2合并注解

java複制代碼/*
* 合并注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@TestAnnotation1 
@TestAnnotation2 
@TestAnnotation3 
public @interface TestMergeAnnotation {
    @AliasFor(annotation = TestAnnotation1 .class, attribute = "value")
    String value() default "";
 
    @AliasFor(annotation = TestAnnotation2 .class, attribute = "name")
    String name() default "";
 
    @AliasFor(annotation = TestAnnotation3 .class, attribute = "desc")
    String desc() default "";
}
           
在這個新注解中使用了@AliasFor注解,使一個注解達到同時使用3個注解的效果

3.3使用

java複制代碼@MyCombinedAnnotation(value = "perfect", name = "who", desc = "this is a merge annotation")
           

等價于三個注解同時使用

java複制代碼@TestAnnotation1 ("hello")
@TestAnnotation2 (name = "world")
@TestAnnotation3 (desc = "this is a merge annotation")
public class TestClass {
}
           

五、總結

  1. 我們在使用時這個注解時,可以直接填寫内容,而不用主動指定"name"和"value"屬性,進而達到了隐示表明屬性别名的效果。
  2. 指定别名,屬性互換的情況下,通過AnnotationUtils仍然可以擷取到值,而通過java原生的方式則無法擷取。 是因為Spring其實是自己實作了jdk動态的攔截器來實作别名功能. 但是: 如果同時設定,并且互為别名的兩個屬性值不一樣就會報錯 是以互為别名,指定一個即可,兩個都會有相同的值

作用

  • 指定别名,屬性互換
  • 将一個注解上的屬性值傳遞給另一個注解對另一個注解的屬性進行别名覆寫
  • 組合多個注解,通過一個注解達到使用多個注解的效果
原文連結:https://juejin.cn/post/7262945227177951292

繼續閱讀