欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring 注解編程模型相關(guān)知識(shí)詳解

 更新時(shí)間:2019年09月24日 08:22:43   作者:justmehyp  
這篇文章主要介紹了Spring 注解編程模型相關(guān)知識(shí)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

Spring 中有一個(gè)概念叫「元注解」(Meta-Annotation),通過(guò)元注解,實(shí)現(xiàn)注解的「派生性」,官方的說(shuō)法是「Annotation Hierarchy」。

什么是元注解

所謂元注解,即標(biāo)注在注解上的注解。這種方式所形成的注解層級(jí)結(jié)構(gòu)中,元注解在層級(jí)結(jié)構(gòu)的上面,我叫它父注解(Super Annotation), 被注解的注解在層級(jí)結(jié)構(gòu)的下面,叫它子注解(Sub Annotation)。引入元注解的目的是為了實(shí)現(xiàn)屬性重寫(xiě)(Attribute Override) 的目的。

舉個(gè)簡(jiǎn)單的例子:

有 一個(gè)類(lèi) Home 和 2 個(gè)注解,1 個(gè)叫 @Parent,另一個(gè)叫 @Child ,@Parent 標(biāo)注在 @Child 上,@Child 標(biāo)注在 Home 上,它們都只有一個(gè)屬性,叫 name, 如果 @Parent.name 的默認(rèn)值是 'John',而 @Child.name 的默認(rèn)值是 'Jack'。

這時(shí),從 Home 上獲取 @Child.name,應(yīng)該返回 'Jack',這毫無(wú)懸念。

那么,如果獲取 @Parent.name,應(yīng)該返回什么呢?根據(jù) Spring 注解的「派生性」,@Child.name override @Parent.name,所以返回結(jié)果也是 'Jack'。

上述例子中的類(lèi)和注解,代碼大致如下

@interface Parent {
  String name() default "John";
}

@Parent
@interface Child {
  String name() default "Jack";
}

@Child
class Home { }

注解層級(jí)結(jié)構(gòu):

@Parent

@Child

相對(duì)于「屬性重寫(xiě)」,還有另一個(gè)概念是「屬性別名」(Alias),屬性別名之間是互相等價(jià)的。

我們給上面的 @Child 加一個(gè)屬性 value,并且使用 @AliasFor ,使 @Child.name 和 @Child.value 互相成為別名,并且默認(rèn)值為空字符串:

@interface Child {
  @AliasFor("name")
  String value() default "";
 
  @AliasFor("value")
  String name() default "";
}

標(biāo)注在 Home 上時(shí),給 @Child.value 設(shè)置值為 "Jack":

@Child("Jack")
class Home { }

這時(shí),無(wú)論是獲取 @Child.name 還是獲取 @Child.value,其結(jié)果總是相同的,都是 "Jack"。說(shuō)明了屬性別名之間的等價(jià)性。

屬性別名 和 屬性重寫(xiě)

屬性別名 和 屬性重寫(xiě) 其實(shí)是兩個(gè)完全不同的概念,但是如果不加區(qū)分,模糊概念的話,就會(huì)對(duì)一些現(xiàn)象不符合預(yù)期而感到意外。 考慮以下案例,分別給出 @A.a1、@A.a2、@B.a1、@B.b、@C.c、@C.b 的值:

@interface A {
  String a1() default "1";
  String a2() default "1";
}

@A
@interface B {
  String a1() default "2";
  
  @AliasFor(value = "a2", annotation = A.class)
  String b() default "2";
}

@B
@interface C {
  @AliasFor(value = "a1", annotation = B.class)  
  String c() default "3";

  String b() default "3";
}

在我沒(méi)有弄清概念之前,我覺(jué)得答案應(yīng)該是:@A.a1、@A.a2、@B.a1、@B.b、@C.c、@C.b 全都是 "3"。
理由如下:

  • @C.c 是 @B.a1 的別名,@B.a1 重寫(xiě) @A.a1 ,所以這 3 者是一條鏈上的,它們的值應(yīng)該相等, 是 "3"。
  • @C.b 重寫(xiě) @B.b,@B.b 是 @A.a2 的別名,所以這 3 者 也是一條鏈上的,它們的值也應(yīng)該相等,是 "3"。

而結(jié)果卻是,我錯(cuò)了,@B.a1、@B.b、@C.c、@C.b 的值是 "3", 但 @A.a1、@A.a2 的值是 "2"。

至于為什么,我們先來(lái)認(rèn)真理解一下 屬性別名 和 屬性重寫(xiě) 這 2 個(gè)概念吧。

援引官方 Wiki https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model, 其中有關(guān)于這兩個(gè)概念的澄清。在 「Attribute Aliases and Overrides」 一節(jié)中,官方原文如下:

An attribute alias is an alias from one annotation attribute to another annotation attribute. Attributes within a set of aliases can be used interchangeably and are treated as equivalent. Attribute aliases can be categorized as follows.

Explicit Aliases: if two attributes in one annotation are declared as aliases for each other via @AliasFor, they are explicit aliases.

Implicit Aliases: if two or more attributes in one annotation are declared as explicit overrides for the same attribute in a meta-annotation via @AliasFor, they are implicit aliases.

Transitive Implicit Aliases: given two or more attributes in one annotation that are declared as explicit overrides for attributes in meta-annotations via @AliasFor, if the attributes effectively override the same attribute in a meta-annotation following the law of transitivity, they are transitive implicit aliases.

An attribute override is an annotation attribute that overrides (or shadows) an annotation attribute in a meta-annotation. Attribute overrides can be categorized as follows.

Implicit Overrides: given attribute A in annotation @One and attribute A in annotation @Two, if @One is meta-annotated with @Two, then attribute A in annotation @One is an implicit override for attribute A in annotation @Two based solely on a naming convention (i.e., both attributes are named A).

Explicit Overrides: if attribute A is declared as an alias for attribute B in a meta-annotation via @AliasFor, then A is an explicit override for B.

Transitive Explicit Overrides: if attribute A in annotation @One is an explicit override for attribute B in annotation @Two and B is an explicit override for attribute C in annotation @Three, then A is a transitive explicit override for C following the law of transitivity.

屬性別名,有 3 種, 分別是 顯式別名,隱式別名 和 傳遞隱式別名, 「屬性別名」 只能發(fā)生在同一個(gè)注解內(nèi)部。比如:

顯式別名(互相@AliasFor),@A.a1 和 @A.a2,

@interface A {
  @AliasFor("a2")
  String a1() default "";

  @AliasFor("a1")
  String a2() default "";
}

隱式別名(@AliasFor到同一個(gè)屬性),@B.b1 和 @B.b2

@interface A {
  String a() default "";
}

@A
@interface B {
  @AliasFor(value = "a", annotation = A.class)
  String b1() default "";

  @AliasFor(value = "a", annotation = A.class)
  String b2() default "";
}

傳遞隱式別名(最終@AliasFor到同一個(gè)屬性) @C.c1 和 @C.c2

@interface A {
  String a() default "";
}

@A
@interface B {
  @AliasFor(value = "a", annotation = A.class)
  String b() default "";
}

@B
@interface C {
  @AliasFor(value = "a", annotation = A.class)
  String c1() default "";

  @AliasFor(value = "b", annotation = B.class)
  String c2() default "";
}

屬性重寫(xiě),也有 3 種,分別是 隱式重寫(xiě),顯式重寫(xiě) 和 傳遞顯式重寫(xiě),「屬性重寫(xiě)」只能發(fā)生在注解之間。比如:

隱式重寫(xiě)(同名屬性), @B.a 重寫(xiě) @A.a

@interface A {
  String a() default "";
}

@A
@interface B {
  String a() default "";
}

顯式重寫(xiě)(需要@AliasFor),@B.b 重寫(xiě) @A.a

@interface A {
  String a() default "";
}

@A
@interface B {
  @AliasFor(value = "a", annotation = A.class)
  String b() default "";
}

傳遞顯式重寫(xiě)(需要 @AliasFor),由于 @C.c 重寫(xiě) @B.b, @B.b 重寫(xiě) @A.a, 所以 @C.c 也 重寫(xiě) @A.a

@interface A {
  String a() default "";
}

@A
@interface B {
  @AliasFor(value = "a", annotation = A.class)
  String b() default "";
}

@B
@interface C {
  @AliasFor(value = "b", annotation = B.class)
  String c() default "";
}

理解清楚之后,我們回到剛才的題目,樣例重貼如下:

@interface A {
  String a1() default "1";

  String a2() default "1";
}

@A
@interface B {
  String a1() default "2";
  
  @AliasFor(value = "a2", annotation = A.class)
  String b() default "2";
}

@B
@interface C {
  @AliasFor(value = "a1", annotation = B.class)  
  String c() default "3";

  String b() default "3";
}

解答步驟是:

  • 對(duì)于注解 @C,@C.c = "3", @C.b = "3"
  • 對(duì)于注解 @B, @B.a1 被 @C.c 顯式重寫(xiě), 所以 @B.a1 = @C.c = "3"; @B.b 被 @C.b 隱式重寫(xiě),所以 @B.b = @C.b = "3"
  • 對(duì)于注解 @A, @A.a1 被 @B.a1 隱式重寫(xiě),所以 @A.a1 = @B.a1 = "2"; @A.a2 被 @B.b 顯式重寫(xiě),所以 @A.a2 = @B.b = "2"

可以看到 @A 和 @C 之間沒(méi)有任何關(guān)系。這里也根本沒(méi)有「屬性別名」的存在,不是用了 @AliasFor 就是 「屬性別名」的。

對(duì)于「顯式傳遞重寫(xiě)」,像上面 "@A.a1 被 @B.a1 隱式重寫(xiě), @B.a1 被 @C.c 顯式重寫(xiě)",或者 "@A.a2 被 @B.b 顯式重寫(xiě), B.b 被 @C.b 隱式重寫(xiě)", 重寫(xiě)關(guān)系是不會(huì)傳遞的。

總結(jié)

屬性別名,有 3 種, 分別是 顯式別名,隱式別名 和 傳遞隱式別名, 「屬性別名」 只能發(fā)生在同一個(gè)注解內(nèi)部。 屬性重寫(xiě),也有 3 種,分別是 隱式重寫(xiě),顯式重寫(xiě) 和 傳遞顯式重寫(xiě),「屬性重寫(xiě)」只能發(fā)生在注解之間。

后記

Spring 對(duì)于注解編程模型的代碼實(shí)現(xiàn),主要在 AnnotatedElementUtils 這個(gè)類(lèi)中,做試驗(yàn)可以使用這個(gè)方法:

AnnotatedElementUtils#getMergedAnnotationAttributes。

需要注意的是,「隱式重寫(xiě)」不適用于 value 屬性,貌似 value 屬性是一個(gè)相對(duì)特殊的屬性。

以下示例, @B.value 不會(huì) 隱式重寫(xiě) @A.value

@interface A {
  String value() default "a";
}
@A
@interface B {
  String value() default "b";
}

但只要屬性名不是 value,都可以 隱式重寫(xiě) , @B.xxx 隱式重寫(xiě) @A.xxx

@interface A {
  String xxx() default "a";
}

@A
@interface B {
  String xxx() default "b";
}

我跟了以下源碼,發(fā)現(xiàn)源碼中確實(shí)對(duì) value 屬性做了特殊判斷,代碼位置在 org.springframework.core.annotation.AnnotatedElementUtils.MergedAnnotationAttributesProcessor#postProcess 方法中,代碼片段如下;

// Implicit annotation attribute override based on convention
  else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
    overrideAttribute(element, annotation, attributes, attributeName, attributeName);
  }

其中,AnnotationUtils.VALUE 是一個(gè)常量,其值為 "value"。暫時(shí)沒(méi)有找到官方說(shuō)明為什么要對(duì) value 屬性做特殊處理。猜測(cè)是很多注解只有一個(gè)屬性, 為了編程方便,因?yàn)椴恍枰?@A(value = "hello world) 這樣使用, 只需要 @A("hello world") 即可。這種情況下,如果隱式重寫(xiě),可能不是編碼者想要的結(jié)果。

值得一提的是,顯式重寫(xiě) 沒(méi)有這種特殊處理,以下示例 @B.value 會(huì)顯式重寫(xiě) @A.value:

@interface A {

  String value() default "a";
}

@A
@interface B {

  @AliasFor(annotation = A.class)
  String value() default "b";
}

本文討論所涉及的 Spring Boot 版本 >= 2.0.2.RELEASE。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中的DelayQueue源碼解析

    Java中的DelayQueue源碼解析

    這篇文章主要介紹了Java中的DelayQueue源碼解析,一個(gè)實(shí)現(xiàn)PriorityBlockingQueue實(shí)現(xiàn)延遲獲取的無(wú)界隊(duì)列,在創(chuàng)建元素時(shí),可以指定多久才能從隊(duì)列中獲取當(dāng)前元素,只有延時(shí)期滿后才能從隊(duì)列中獲取元素,需要的朋友可以參考下
    2023-12-12
  • Java利用反射實(shí)現(xiàn)文件的讀取操作

    Java利用反射實(shí)現(xiàn)文件的讀取操作

    這篇文章主要介紹了Java利用反射實(shí)現(xiàn)文件的讀取操作,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • Idea中SpringBoot多模塊項(xiàng)目的建立實(shí)現(xiàn)

    Idea中SpringBoot多模塊項(xiàng)目的建立實(shí)現(xiàn)

    這篇文章主要介紹了Idea中SpringBoot多模塊項(xiàng)目的建立實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • struts2實(shí)現(xiàn)多文件上傳的示例代碼

    struts2實(shí)現(xiàn)多文件上傳的示例代碼

    本篇文章主要介紹了struts2實(shí)現(xiàn)多文件上傳的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-03-03
  • MyBatis foreach 批量更新實(shí)例

    MyBatis foreach 批量更新實(shí)例

    這篇文章主要介紹了MyBatis foreach 批量更新實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-01-01
  • IDEA解決Java:程序包xxxx不存在的問(wèn)題

    IDEA解決Java:程序包xxxx不存在的問(wèn)題

    這篇文章主要介紹了IDEA解決Java:程序包xxxx不存在的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Maven模版Bug及解決辦法

    Maven模版Bug及解決辦法

    默認(rèn),會(huì)幫我們創(chuàng)建src/main/resources 按照Maven的規(guī)范,Maven會(huì)有3個(gè)目錄,分別是: src/main/java : java源文件存放位置 src/main/resource : resource資源,如配置文件等 src/test/java : 測(cè)試代碼源文件存放位置
    2016-04-04
  • 優(yōu)化MyBatis配置文件中的配置詳解

    優(yōu)化MyBatis配置文件中的配置詳解

    這篇文章主要介紹了優(yōu)化MyBatis配置文件中的配置詳解,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • Spring框架生成圖片驗(yàn)證碼實(shí)例

    Spring框架生成圖片驗(yàn)證碼實(shí)例

    驗(yàn)證碼在很多地方都會(huì)遇到,實(shí)現(xiàn)的方法和形式也有很多,主要的目的就是為了安全,防止一些惡意的攻擊等。今天在之前搭建好的一個(gè)spring框架上寫(xiě)了一個(gè)驗(yàn)證碼的生成demo,我會(huì)貼出細(xì)節(jié)代碼,但是spring的配置就不在介紹了,有需要的可以參考借鑒。
    2016-08-08
  • log4j2 項(xiàng)目日志組件的實(shí)例代碼

    log4j2 項(xiàng)目日志組件的實(shí)例代碼

    下面小編就為大家分享一篇log4j2 項(xiàng)目日志組件的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12

最新評(píng)論