Spring組合註解詳解(註解合併及別名屬性覆蓋)

 

Spring組合註解詳解(註解合併及別名屬性覆蓋)

 

 

組合註解


  註解的作用就不用介紹了吧,主要就是用來簡化配置,通過自定義註解或者其他框架提供的註解,只要往方法或者類上一加,就可以實現許多神奇的功能。

  spring 4.2之後就提供了組合註解的實現方式,啥是組合註解呢,其實就是將多個註解作用於一個註解,用一個註解就可以來實現那多個註解的功能,使作用的元素(即方法或類等)看上去更簡潔美觀,當然主要還是更強大的屬性覆蓋功能。

  舉個最常見的組合註解吧,即spring的@RestController,它將@ResponseBody和@Controller兩個註解組合爲一個,我們在Controller類上只要加@RestController即可實現之前要加兩個註解才能實現的功能。代碼如:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller  //組合Controller使其實現Bean註冊
@ResponseBody  //組合ResponseBody使其支持將結果轉化爲json
public @interface RestController {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     * @since 4.0.1
     */
    @AliasFor(annotation = Controller.class)
    String value() default "";

}

 


自定義註解(不使用Spring自帶的註解)實現組合註解功能


  自定義註解(即自己要實現某些功能但不使用spring提供的一些註解,並非網上大部分那種將spring提供的多個註解作用於一個自己定義的註解然後將該註解作用於元素,讓其實現Spring提供的組合功能)時,想使用spring那神奇般的組合註解該如何實現呢。接下來就介紹spring神奇的工具類AnnotatedElementUtils,使用它就可以讓自己的註解實現組合註解般的功能。直接上代碼:

/**
 * @author meilin.huang
 * @version 1.0
 * @date 2019-06-06 21:11
 */
public class SynthesizedAnnotationTest {

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @interface Test1 {
        String test1() default "test1";
    }

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @interface Test2 {
        String test2() default "test2";
    }

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @Test2
    @interface Test3 {
        String tset3() default "test3";
    }

    /**
     * 只有@Test3註解,但是Test3註解上組合了@Test2註解,故就可以通過Spring的工具類獲取到Test2註解的內容,詳見main方法
     * 當然也可以將組合註解作用於更高層次,如Test3組合Test2,Test2組合Test1,然後將Test3作用於元素,通過工具類獲取Test1註解功能
     */
    @Test3
    static class Element {}

    public static void main(String[] args) {
        Test2 test2 = AnnotatedElementUtils.getMergedAnnotation(Element.class, Test2.class);
        System.out.println(test2);// 輸出'@mayfly.sys.common.utils.SynthesizedAnnotationTest$Test2(test2=test2)'
    }
}

 

我們在合併註解的時候,比如註解A中有屬性A(),註解B中有屬性B(),它們的組合註解C這麼寫就可以了:

@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@A
@B
public @Interface C{   // 合併了A, B註解
  
   String A();  // 自動對應註解A中的A屬性
 
   String B();  // 自動對應註解B中的B屬性
 
}


組合註解實現屬性值覆蓋(有點類似子類覆蓋父類屬性方法)


  如果spring只實現了以上那功能,其實作用也不太大,並且實現也就很簡單了,自己便可輕易實現,通過直接查找元素上是否含有該直接註解,沒有則遍歷該元素其他註解,然後遞歸遍歷查找註解的元註解是否含有該元素,直到找到返回即可。

  接下來就介紹下spring組合註解更強大的屬性覆蓋功能,即更低層次的註解屬性方法覆蓋高層次註解的屬性方法,啥意思呢,具體見代碼就比較清晰明瞭了,實現該功能還需要spring提供的另外一個註解即@AliasFor配合完成。

/**
 * @author meilin.huang
 * @version 1.0
 * @date 2019-06-06 21:11
 */
public class SynthesizedAnnotationTest {

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @interface Test1 {
        String test1() default "test1";
    }

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @interface Test2 {
        String test2() default "test2";
    }

    @Target({ ANNOTATION_TYPE, FIELD, TYPE })
    @Retention(RUNTIME)
    @Test2
    @interface Test3 {
        /**
         * AliasFor註解用來表示要覆蓋Test2註解中的test2()屬性方法,
         * annotation屬性聲明的註解類必須存在於該註解的元註解上
         * attribute屬性聲明的值必須存在於Test2註解屬性方法中(即Test2註解的test2方法)
         */
        @AliasFor(annotation = Test2.class, attribute = "test2")
        String test3() default "test3";
    }

    /**
     * 只有@Test3註解,但是Test3註解上組合了@Test2註解,並將該註解的test3方法值用來覆蓋Test2註解中的test2方法
     * 即更低層次聲明的覆蓋規則,會覆蓋更高層次的屬性方法值,即調用高層次的註解方法值實際顯示的是低層所賦的值
     * 當然也可以將組合註解作用於更高層次,如Test3組合Test2,Test2組合Test1,然後將Test3作用於元素,通過工具類獲取Test1註解覆蓋的屬性值
     */
    @Test3(test3 = "覆蓋Test2屬性中的test2方法")
    static class Element {}

    public static void main(String[] args) {
        Test2 test2 = AnnotatedElementUtils.getMergedAnnotation(Element.class, Test2.class);
        // 雖然調用了Test2註解的test2方法,但是實際顯示的是Test3註解中的test3屬性聲明的值
        // 則說明Test2的test2屬性被覆蓋了 
        System.out.println(test2.test2());// out '覆蓋Test2屬性中的test2方法'
    }
}


  以上就是屬性覆蓋的最簡單兩層覆蓋,當然原則上是可以支持無限層覆蓋的,但是用法都是一致的。實現該功能的主要原理其實就是通過jdk的動態代理。具體實現方式有興趣的可以參考AnnotatedElementUtils工具類的實現細節。

  那麼組合註解有啥用呢,其實個人感覺用處是非常大的可以不改變原註解的代碼,就可以定義新註解,並通過覆蓋原則來覆蓋原註解的一些屬性值來實現更多的其他功能擴展,也不會影響原註解的使用。Spring的大量註解都使用這些原則,隨便翻翻源碼註解隨處可見的都是這類組合註解。當然更多的用處還是需要根據自己的需求自己發揮啦,哈哈哈。

  我參考了spring該工具類的源碼,大大簡化了其實現方式(畢竟spring考慮的比較周到,代碼比較複雜),但是在能實現其最基本功能的原則上個人的實現感覺也是夠用了,且性能是spring的兩倍。具體代碼在本人的項目[ https://gitee.com/objs/mayfly ]的AnnotationUtils裏,有興趣的可以參考參考該實現方式蛤,多提寶貴意見~~~

 

 

Spring組合註解的神奇實用功能詳解(功能組合以及別名屬性覆蓋)_mayfly_hml的博客-CSDN博客
https://blog.csdn.net/mayfly_hml/article/details/91070465

 

 

Spring源碼---組合註解/合併註解的問題_小雨的光的博客-CSDN博客
https://blog.csdn.net/qq_28802119/article/details/83573950

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章