深入淺出注解球匕,框架設(shè)計的基礎(chǔ)

何為注解

注解(Annotation)是 Java 中的一個類型,通俗地理解就像一個標簽帖烘,貼在了代碼上亮曹。眾所周知,Spring 支持大量注解,基于注解可以完成 Bean 的注入和生命周期管理乾忱,注解也是取代 xml 配置的一種方法。使用注解可以把元數(shù)據(jù)和源代碼綁定在一起历极,可以用于描述代碼無法描述的信息窄瘟,并在編譯或運行中使用。

在 Java 中使用注解非常簡單趟卸,比如我們可以定義一個用于標記類型的注解蹄葱,并根據(jù)這個注解從 Spring 中提取出相應(yīng)的 Bean。在這段代碼中锄列,我們使用了 Java 自帶的注解 @Retention图云、@Override,Spring 提供的注解 @Service邻邮、@PostConstruct竣况,以及自定義的注解 @MyAnnotation。

@Service
@MyAnnotation
public class TestAnnotation implements ApplicationContextAware {
    ApplicationContext context;

    //定義一個注解
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation{}

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.context = context;
    }

    @PostConstruct
    private void init(){
        Map<String, Object> beans = context.getBeansWithAnnotation(MyAnnotation.class);
        System.out.println("找到 bean " + beans.keySet());
    }
}

定義注解

定義注解非常像定義接口筒严,在 interface 關(guān)鍵詞前面加了一個 @丹泉。實際上,注解類型與其他類型一樣鸭蛙,最終也是編譯成 class 文件摹恨。定義注解需要用到一些元注解,這些元注解是 Java 語言提供的娶视,用來定義注解的一些特性晒哄。

元注解 @Inherited 用于聲明該注解支持子類繼承父類中的注解。

元注解 @Documented 用于聲明該注解需要包含到 Javadoc 中肪获。

元注解 @Retention 定義注解的級別寝凌,可選值定義在 RetentionPolicy 枚舉類,包括運行時(RUNTIME)贪磺、類文件(CLASS)硫兰、源代碼(SOURCE)。

元注解 @Target 定義注解的使用范圍寒锚,值的類型是 ElementType 枚舉值劫映。注解的使用范圍可以設(shè)置一個或多個,常用的有 ElementType.TYPE 表示用于 Java 類刹前、ElementType.METHOD 表示用于 Java 方法泳赋、ElementType.FIELD 表示用于類的字段、ElementType.PARAMETER 表示用于方法的參數(shù)喇喉。

注解支持定義元素祖今,定義的形式有點類似方法定義,并可以使用 default 設(shè)置默認值。如下代碼中定義的值為 value千诬,獲取值調(diào)用 value() 即可耍目,如果使用時沒有給出值則默認值為 test。需要注意的是注解的默認值不能是 null徐绑,最終一定會有一個值邪驮。如果需要表示值不存在,需要自己定義一個特殊值來表示傲茄。

注解的元素類型支持基本類型毅访、String、Class盘榨、enum喻粹、Annotation 以及這些類型的數(shù)組。

//定義一個注解
@Inherited
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation2 {
    String value() default "test";
}

從 Java 8 開始草巡,新增了元注解 @Repeatable守呜,用于表示注解可以有多個值。如下的代碼 SingleAnnotation 可以有多個值山憨,使用 @Repeatable 聲明了多個值合并為注解 @RepeatAnnotation弛饭。這種情況下,@RepeatAnnotation 的元素必須有 value萍歉,且類型必須為 SingleAnnotation 數(shù)組侣颂,才能進行聚合。

使用時枪孩,@SingleAnnotation 可以定義多次給出多個不同的值憔晒,解析注解時則需要按照 @RepeatAnnotation 進行解析。關(guān)于注解的解析蔑舞,請參考后文注解處理器拒担。

@Retention(RetentionPolicy.RUNTIME)
@interface RepeatAnnotation {
    SingleAnnotation[] value();
}

@Retention(RetentionPolicy.RUNTIME)
@Repeatable(RepeatAnnotation.class)
@interface SingleAnnotation{
    String value();
}

@SingleAnnotation("hello")
@SingleAnnotation("world")
public class RepeatAnnotationTest{
    public static void main(String[] args){
        RepeatAnnotation annotation = RepeatAnnotationTest.class.getAnnotation(RepeatAnnotation.class);
        for (SingleAnnotation singleAnnotation : annotation.value()) {
            System.out.println(singleAnnotation.value());
        }
    }
}

Java 中的注解不支持繼承,因此不能使用 extends 關(guān)鍵字攻询。但是注解可以進行嵌套从撼,也就是一個注解的值是另一個注解。如下的代碼中钧栖,MyAnnotation3 注解的第二個值類型為 MyAnnotation2低零,使用時對第二個值賦值一樣需要按照注解的格式寫為 @MyAnnotation2("world")。

public @interface MyAnnotation3 {
    String value() default "test";
    MyAnnotation2 annotation();
}

@MyAnnotation3(value = "hello"
    ,annotation = @MyAnnotation2("world"))
private void test2(){}

注解處理器

定義注解以后需要編寫注解處理器拯杠,才能發(fā)揮注解的作用掏婶。在最開始的例子中,我們定義的注解處理器從 Spring 上下文中找出所有使用了注解 MyAnnotation 的 Java Bean潭陪,然后打印出這些 Bean 的名字雄妥。

@PostConstruct
private void init(){
    Map<String, Object> beans = context.getBeansWithAnnotation(MyAnnotation.class);
    System.out.println("找到 bean " + beans.keySet());
}

在 Java 中可以通過反射很方便地處理注解最蕾。對于 Class 上面的注解,可以通過 Class 的方法獲取所有注解或指定注解老厌,如下所示瘟则。

//獲取類型的所有注解
TestAnnotation.class.getAnnotations();
TestAnnotation.class.getDeclaredAnnotations();
//獲取類型的指定注解
TestAnnotation.class.getAnnotation(MyAnnotation.class);
TestAnnotation.class.getDeclaredAnnotation(MyAnnotation.class);

對于方法上的注解,可以通過 Method 的方法獲取所有注解或指定注解枝秤,如下所示壹粟。Method 還提供了 getParameterAnnotations() 來獲取方法參數(shù)上面的所有注解,每個參數(shù)的注解都是一個數(shù)組宿百,返回值為二維數(shù)組。

Method init = TestAnnotation.class.getDeclaredMethod("init");
//獲取方法的所有注解
init.getAnnotations();
init.getDeclaredAnnotations();
//獲取方法的指定注解
init.getAnnotation(PostConstruct.class);
init.getDeclaredAnnotation(PostConstruct.class);
//獲取方法的參數(shù)的所有注解洪添,返回二維數(shù)組
init.getParameterAnnotations();

對注解進行處理時垦页,注解可以當做普通的變量對待。如下代碼所示干奢,annotation 是一個注解類型的變量痊焊,與普通變量區(qū)別不大,可以有類型定義忿峻,值也可以是 null薄啥。但是讀取注解的值時,需要調(diào)用方法逛尚,如 annotation.value()垄惧。

MyAnnotation2 annotation = TestAnnotation.class.getAnnotation(MyAnnotation2.class);
if(annotation != null){
    System.out.println(annotation.value());
}

結(jié)語

關(guān)于 Java 注解,本文只覆蓋這些內(nèi)容绰寞。注解是一個被廣泛使用的特性到逊,如 Java 本身的 @Override、Spring 中的 @Service @Autowired滤钱、MyBatis 中的 @Insert @Update 等觉壶。基于注解件缸,實現(xiàn)了對代碼的自動化處理铜靶,如掃描和自動注入 JavaBean,大大簡化了 Java 程序員的開發(fā)工作他炊。正因如此争剿,我們不僅需要了解常用的注解使用方式,還需要深入理解注解背后的工作原理痊末。

本文首發(fā)于公眾號程序之心秒梅,每天給你誠意滿滿的干貨,歡迎關(guān)注舌胶。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捆蜀,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辆它,老刑警劉巖誊薄,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锰茉,居然都是意外死亡呢蔫,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門飒筑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來片吊,“玉大人,你說我怎么就攤上這事协屡∏渭梗” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵肤晓,是天一觀的道長爷贫。 經(jīng)常有香客問我,道長补憾,這世上最難降的妖魔是什么漫萄? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮盈匾,結(jié)果婚禮上腾务,老公的妹妹穿的比我還像新娘。我一直安慰自己削饵,他們只是感情好窑睁,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著葵孤,像睡著了一般担钮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上尤仍,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天箫津,我揣著相機與錄音,去河邊找鬼宰啦。 笑死苏遥,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的赡模。 我是一名探鬼主播田炭,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼漓柑!你這毒婦竟也來了教硫?” 一聲冷哼從身側(cè)響起叨吮,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瞬矩,沒想到半個月后茶鉴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡景用,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年涵叮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伞插。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡割粮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出媚污,到底是詐尸還是另有隱情舀瓢,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布杠步,位于F島的核電站,受9級特大地震影響榜轿,放射性物質(zhì)發(fā)生泄漏幽歼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一谬盐、第九天 我趴在偏房一處隱蔽的房頂上張望甸私。 院中可真熱鬧,春花似錦飞傀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽听盖。三九已至,卻和暖如春幢痘,著一層夾襖步出監(jiān)牢的瞬間唬格,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工颜说, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留购岗,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓门粪,卻偏偏與公主長得像喊积,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子玄妈,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理乾吻,服務(wù)發(fā)現(xiàn)髓梅,斷路器,智...
    卡卡羅2017閱讀 134,638評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,778評論 6 342
  • 本文章涉及代碼已放到github上annotation-study 1.Annotation為何而來 What:A...
    zlcook閱讀 29,131評論 15 116
  • 南京,并不陌生的城市辜御。第一次見她鸭你,七歲不到,小學(xué)一年級的五一黃金周擒权,媽媽帶著我到江蘇一帶旅游袱巨。或許是太小不懂事碳抄,也...
    眾知閱讀 255評論 1 0
  • 毛毛站在家門口愉老,猶豫了很久很久不敢進去,手里是折成一小塊的試卷剖效。78分嫉入。 毛毛的爸爸媽媽望子成龍非常嚴厲,對于學(xué)習(xí)...
    拾光人生閱讀 416評論 3 2