1. 程式人生 > >Java學習之註解Annotation實現原理

Java學習之註解Annotation實現原理

前言:

   最近學習了EventBus、BufferKinfe、GreenDao、Retrofit 等優秀開源框架,它們新版本無一另外的都使用到了註解的方式,我們使用在使用的時候也嚐到不少好處,基於這種想法我覺得有必要對註解有個更深刻的認識,今天中午把公司的專案搞完了,晚上加個班學習總結一下Java的註解。

什麼是註解?

      對於很多初次接觸的開發者來說應該都有這個疑問?Annontation是Java5開始引入的新特徵,中文名稱叫註解。它提供了一種安全的類似註釋的機制,用來將任何的資訊或元資料(metadata)與程式元素(類、方法、成員變數等)進行關聯。為程式的元素(類、方法、成員變數)加上更直觀更明瞭的說明,這些說明資訊是與程式的業務邏輯無關,並且供指定的工具或框架使用。Annontation像一種修飾符一樣,應用於包、型別、構造方法、方法、成員變數、引數及本地變數的宣告語句中。

註解的用處:

      1、生成文件。這是最常見的,也是java 最早提供的註解。常用的有@param @return 等

      2、跟蹤程式碼依賴性,實現替代配置檔案功能。比如Dagger 2依賴注入,未來java開發,將大量註解配置,具有很大用處;

      3、在編譯時進行格式檢查。如@override 放在方法前,如果你這個方法並不是覆蓋了超類方法,則編譯時就能檢查出。

元註解:

java.lang.annotation提供了四種元註解,專門註解其他的註解:

   @Documented –註解是否將包含在JavaDoc中
   @Retention –什麼時候使用該註解
   @Target

 –註解用於什麼地方
   @Inherited – 是否允許子類繼承該註解

  1.)@Retention– 定義該註解的生命週期
  •   RetentionPolicy.SOURCE : 在編譯階段丟棄。這些註解在編譯結束之後就不再有任何意義,所以它們不會寫入位元組碼。@Override, @SuppressWarnings都屬於這類註解。
  •   RetentionPolicy.CLASS : 在類載入的時候丟棄。在位元組碼檔案的處理中有用。註解預設使用這種方式
  •   RetentionPolicy.RUNTIME : 始終不會丟棄,執行期也保留該註解,因此可以使用反射機制讀取該註解的資訊。我們自定義的註解通常使用這種方式。

  舉例:bufferKnife 8.0 中@BindView 生命週期為CLASS

@Retention(CLASS) @Target(FIELD)
public @interface BindView {
  /** View ID to which the field will be bound. */
  @IdRes int value();
}
  2.)Target – 表示該註解用於什麼地方。預設值為任何元素,表示該註解用於什麼地方。可用的ElementType引數包括
  • ElementType.CONSTRUCTOR:用於描述構造器
  • ElementType.FIELD:成員變數、物件、屬性(包括enum例項)
  • ElementType.LOCAL_VARIABLE:用於描述區域性變數
  • ElementType.METHOD:用於描述方法
  • ElementType.PACKAGE:用於描述包
  • ElementType.PARAMETER:用於描述引數
  • ElementType.TYPE:用於描述類、介面(包括註解型別) 或enum宣告

 舉例Retrofit 2 中@Field 作用域為引數

@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {
  String value();

  /** Specifies whether the {@linkplain #value() name} and value are already URL encoded. */
  boolean encoded() default false;
}
 3.)@Documented–一個簡單的Annotations標記註解,表示是否將註解資訊新增在java文件中。
 4.)@Inherited – 定義該註釋和子類的關係

     @Inherited 元註解是一個標記註解,@Inherited闡述了某個被標註的型別是被繼承的。如果一個使用了@Inherited修飾的annotation型別被用於一個class,則這個annotation將被用於該class的子類。

常見標準的Annotation:

  1.)Override

      java.lang.Override是一個標記型別註解,它被用作標註方法。它說明了被標註的方法過載了父類的方法,起到了斷言的作用。如果我們使用了這種註解在一個沒有覆蓋父類方法的方法時,java編譯器將以一個編譯錯誤來警示。

  2.)Deprecated

     Deprecated也是一種標記型別註解。當一個型別或者型別成員使用@Deprecated修飾的話,編譯器將不鼓勵使用這個被標註的程式元素。所以使用這種修飾具有一定的“延續性”:如果我們在程式碼中通過繼承或者覆蓋的方式使用了這個過時的型別或者成員,雖然繼承或者覆蓋後的型別或者成員並不是被宣告為@Deprecated,但編譯器仍然要報警。

 3.)SuppressWarnings

     SuppressWarning不是一個標記型別註解。它有一個型別為String[]的成員,這個成員的值為被禁止的警告名。對於javac編譯器來講,被-Xlint選項有效的警告名也同樣對@SuppressWarings有效,同時編譯器忽略掉無法識別的警告名。

@SuppressWarnings("unchecked") 

自定義註解:

這裡模擬一個滿足網路請求介面,以及如何獲取介面的註解函式,引數執行請求。

1.)定義註解:


@ReqType 請求型別

@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface ReqType {

    /**
     * 請求方式列舉
     *
     */
    enum ReqTypeEnum{ GET,POST,DELETE,PUT};

    /**
     * 請求方式
     * @return
     */
    ReqTypeEnum reqType() default ReqTypeEnum.POST;
}

@ReqUrl 請求地址

@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface ReqUrl {
    String reqUrl() default "";
}

@ReqParam 請求引數

@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface ReqParam {
    String value() default "";
}

從上面可以看出註解引數的可支援資料型別有如下:

           1.所有基本資料型別(int,float,boolean,byte,double,char,long,short)
    2.String型別
    3.Class型別
    4.enum型別
    5.Annotation型別
    6.以上所有型別的陣列

 而且不難發現@interface用來宣告一個註解,其中的每一個方法實際上是聲明瞭一個配置引數。方法的名稱就是引數的名稱,返回值型別就是引數的型別(返回值型別只能是基本型別、Class、String、enum)。可以通過default來宣告引數的預設值。

2.)如何使用自定義註解
public interface IReqApi {
    
    @ReqType(reqType = ReqType.ReqTypeEnum.POST)//宣告採用post請求
    @ReqUrl(reqUrl = "www.xxx.com/openApi/login")//請求Url地址
    String login(@ReqParam("userId") String userId, @ReqParam("pwd") String pwd);//引數使用者名稱 密碼
    
}
3.)如何獲取註解引數

 這裡強調一下,Annotation是被動的元資料,永遠不會有主動行為,但凡Annotation起作用的場合都是有一個執行機制/呼叫者通過反射獲得了這個元資料然後根據它採取行動。

通過反射機制獲取函式註解資訊

      Method[] declaredMethods = IReqApi.class.getDeclaredMethods();
        for (Method method : declaredMethods) {
            Annotation[]  methodAnnotations = method.getAnnotations();
            Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations();
        }

也可以獲取指定的註解

ReqType reqType =method.getAnnotation(ReqType.class);
 4.)具體實現註解介面呼叫

這裡採用Java動態代理機制來實現,將定義介面與實現分離開,這個後期有時間再做總結。

    private void testApi() {
        IReqApi api = create(IReqApi.class);
        api.login("whoislcj", "123456");
    }

    public <T> T create(final Class<T> service) {
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object... args)
                            throws Throwable {// Annotation[]  methodAnnotations = method.getAnnotations();//拿到函式註解陣列
                        ReqType reqType = method.getAnnotation(ReqType.class);
                        Log.e(TAG, "IReqApi---reqType->" + (reqType.reqType() == ReqType.ReqTypeEnum.POST ? "POST" : "OTHER"));
                        ReqUrl reqUrl = method.getAnnotation(ReqUrl.class);
                        Log.e(TAG, "IReqApi---reqUrl->" + reqUrl.reqUrl());
                        Type[] parameterTypes = method.getGenericParameterTypes();
                        Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations();//拿到引數註解
                        for (int i = 0; i < parameterAnnotationsArray.length; i++) {
                            Annotation[] annotations = parameterAnnotationsArray[i];
                            if (annotations != null) {
                                ReqParam reqParam = (ReqParam) annotations[0];
                                Log.e(TAG, "reqParam---reqParam->" + reqParam.value() + "==" + args[i]);
                            }
                        }
                        //下面就可以執行相應的網路請求獲取結果 返回結果
                        String result = "";//這裡模擬一個結果

                        return result;
                    }
                });
    }

列印結果:

以上通過註解定義引數,通過動態代理方式執行函式,模擬了最基本的Retrofit 2中網路實現原理。

相關推薦

Java學習註解Annotation實現原理

前言:    最近學習了EventBus、BufferKinfe、GreenDao、Retrofit 等優秀開源框架,它們新版本無一另外的都使用到了註解的方式,我們使用在使用的時候也嚐到不少好處,基於這種想法我覺得有必要對註解有個更深刻的認識,今天中午把公司的專案搞完了,晚上加個班學習總結一下Java的註解

JAVA基礎學習-AQS的實現原理分析

ctf red 無限 ole 同步器 failed err lang 行鎖 AbstractQueuedSynchronizer是JUC的核心框架,其設計非常精妙。 使用了Java的模板方法模式。 首先試圖還原一下其使用場景:對於排他鎖,在同一時刻,N個線程只有1個線程能獲

JAVA基礎學習-ThreadPoolExecutor的實現原理

ack 實現原理 finally after () ech 資源 源碼 了解 池技術是性能優化的重要手段:連接池,線程池已經是開發中的標配了。面試中這個知識點也是高頻問題。抽空學習了Java的ThreadPoolExecutor, 把學習的思路記錄一下。 由於線程的創建和銷

lua學習閉包實現原理

引入 內嵌 種類 同時 概念比較 就會 類型 種類型 賦值語句 感覺學習lua的過程中, 閉包的概念比較難以理解,這裏記錄下對閉包的學習。 閉包的概念 在Lua中,閉包(closure)是由一個函數和該函數會訪問到的非局部變量(或者是upvalue)組成的,其中

Java 基礎--註解Annotation詳解

註解 java 基礎 time span face 自定義註解 div ace rtu 自定義註解入門: public @interface Annotation01 { //set default value ""; String value() de

python學習列表物件實現原理解析

l=[1,2,3] id(l[0]) 1652911120 id(l[1]) 1652911152 id(l[2]) 1652911184

spring原始碼學習路---IOC實現原理(二)

上一章我們已經初步認識了BeanFactory和BeanDefinition,一個是IOC的核心工廠介面,一個是IOC的bean定義介面,上章提到說我們無法讓BeanFactory持有一個Map package org.springframework.beans.factory.supp

Java底層HashMap底層實現原理

HashMap簡介       HashMap 是一個散列表,它儲存的內容是鍵值對(key-value)對映。 HashMap 繼承於AbstractMap,實現了Map、Cloneable、java.io.Serializable介面。 HashMap 的實現不是同步的,

Spring原始碼學習IOC容器實現原理(一)-DefaultListableBeanFactory

從這個繼承體系結構圖來看,我們可以發現DefaultListableBeanFactory是第一個非抽象類,非介面類。實際IOC容器。所以這篇部落格以DefaultListableBeanFactoryIOC容器為基準進行IOC原理解析。 一.兩個重要介面 前面已經分析了BeanFactor,它的三個直接子

Spring原始碼學習路---IOC實現原理(三)

原文地址:https://blog.csdn.net/zuoxiaolong8810/article/details/8548478          上一章我們已經初步認識了BeanFactory和BeanDefinition,一個是IOC的核心工廠介面,一個是IOC的be

註解Annotation實現原理與自定義註解例子

什麼是註解? 對於很多初次接觸的開發者來說應該都有這個疑問?Annontation是Java5開始引入的新特徵,中文名稱叫

javaLock的實現原理

委派 rds use 競爭 從數據 xtend abstract cee 不同 0. 前言 與synchronized不同的是,Lock完全用Java寫成,在java這個層面是無關JVM實現的。 在java.util.concurrent.locks包中有很多Lock的實現

Java 註解(Annotation)

  1.Annotation為何而來 What:Annotation幹嘛的 JDK5開始,java增加了對元資料(MetaData)的支援,怎麼支援?答:通過Annotation(註解)來實現。Annotation提供了為程式元素設定元資料的方法。元資料:描述資料的資料

深入理解java註解實現原理(轉載)

轉自:深入理解java註解的實現原理 今天將從以下4個方面來系統的學習一下java註解 什麼是註解 註解的用途 註解使用演示 註解的實現原理 1,什麼是註解 註解也叫元資料,例如我們常見的@Override和@Deprecated,註解是J

java學習實現一個簡單的ArrayList

package thread1; /** * 實現一個簡單的ArrayList * * @Title: uminton */ public class SimpleArrayList<T> { //陣列中元素的大小 private Intege

深入java集合學習2-ArrayList的實現原理

private void RangeCheck(int index) { //如果index不小於列表的元素個數,就丟擲異常 if (index >= size) throw new IndexOutOfBoundsException("Index: " + index + ",

java學習Swing篇(三)——簡單畫板的實現v1.0

畫板功能實現: 1、直線、矩形、橢圓、多邊形、曲線等基本圖形的繪製; 2、多種顏色可選; 3、橡皮檫採用加粗畫筆,另新增一鍵清屏功能; 4、重繪功能; 5、多邊形採用兩種方式自動閉合:一種滑鼠雙擊閉合,一種為切換其它圖形自動閉合; 6、採用邊界佈局。 圖形效果:

Java註解實現原理

註解是Java裡特殊的定義形式,我們用@interface定義一個註解,然後定義其屬性,之後此註解可以被標識在屬性,方法,類,註解等目標上。我們再使用註解是,通過目標物件的getDeclaredAnnotataion(..)等方法獲取註解例項物件。但是這個例項物

Java執行緒實現原理

 併發不一定依賴多執行緒(如PHP中很常見的多程序併發),但在Java裡談併發,大多數都與執行緒脫不了關係。執行緒是一種比程序更輕量級的排程執行單位,執行緒的引入,可以把一個程序的資源分配和排程執行分開,各個執行緒既可以共享程序資源(記憶體地址、檔案I/O等),又可以獨立排程(執行緒是C

認識下java註解實現原理

1,什麼是註解 註解也叫元資料,例如常見的@Override和@Deprecated,註解是JDK1.5版本開始引入的一個特性,用於對程式碼進行說明,可以對包、類、介面、欄位、方法引數、區域性變數等進行註解 一般常用的註解可以分為三類: 一類是Java自帶的標準註解,包括@Override(標明