1.Android架构 retrofit运行时注解(POST详解)+反射实战demo 2分钟手写彻底搞懂注解

今天开始架构师之路!分为6节课,以手写retofit ,Butterknife,Arount,Dagger2,hilit,ASM ,AOP为主


说说你对注解的了解,是怎么解析的

编译时注解与运行时注解,为什么retrofit要使用运行时注解?什么时候用运行时注解?

编译期注解处理的是字节码还是java文件

它们的生命周期不一样,有些接口不用在编译时处理。他们没有用到。  所以运行时更适合

Retrofit:运行时注解,需要用的时候才用到

重写:编译的时候用到的。

运行时注解与编译时注解的区别是什么呢?

a)保留阶段不同。运行时注解保留到运行时,可在运行时访问。而编译时注解保留到编译时,运行时无法访问。

b)原理不同。运行时注解是Java反射机制,而编译时注解通过APT、AbstractProcessor。

c)性能不同。运行时注解由于使用Java反射,因此对性能上有影响。编译时注解对性能没影响。这也是为什么ButterKnife从运行时切换到了编译时的原因。

d)产物不同。运行时注解只需自定义注解处理器即可,不会产生其他文件。而编译时注解通常会产生新的Java源文件。

--------------------- 

Annotation(注解)就是Java提供了一种源程序中的元素关联任何信息或者任何元数据(metadata)的途径和方法。

Annotation是被动的元数据,永远不会有主动行为

既然是被动数据,对于那些已经存在的注解,比如Override,我们只能看看而已,并不知道它具体的工作机制是什么;所以想要理解注解,就直接从自定义注解开始。

元注解

可以看到自定义注解里也会有注解存在,给自定义注解使用的注解就是元注解。

转载:http://blog.csdn.net/lmj623565791/article/details/39269193,本文出自:【张鸿洋的博客】

注解四大组成,二大必须组成

@Target(必须)表示该注解可以用于什么地方,可能的ElementType参数有:

CONSTRUCTOR:构造器的声明

FIELD:域声明(包括enum实例)

LOCAL_VARIABLE:局部变量声明

METHOD:方法声明

PACKAGE:包声明

PARAMETER:参数声明

TYPE:类、接口(包括注解类型)或enum声明

@Retention(常用)表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:

SOURCE:注解将被编译器丢弃

CLASS:注解在class文件中可用,但会被VM丢弃

RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。

@Document将注解包含在Javadoc中

@Inherited允许子类继承父类中的注解

@Target:

  @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

  作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

  取值(ElementType)有:和反射对应

TYPE,  //适用class,interface,enum   

    FIELD, //适用field   

    METHOD,//适用method   

    PARAMETER,  //适用method之上的parameter   

    CONSTRUCTOR, //适用constructor   

    LOCAL_VARIABLE, // 适用区域变量   

   ANNOTATION_TYPE, //适用annotation类型   

   PACKAGE  //适用package   


@Retention:

 @Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

  @Rentention Rentention用来标记自定义注解的有效范围,他的取值有以下三种:

 取值(RetentionPoicy)有:

RetentionPolicy.SOURCE: 只在源代码中保留 一般都是用来增加代码的理解性或者帮助代码检查之类的,比如我们的Override;

RetentionPolicy.CLASS: 默认的选择,能把注解保留到编译后的字节码class文件中,仅仅到字节码文件中,运行时是无法得到的;

RetentionPolicy.RUNTIME: ,注解不仅 能保留到class字节码文件中,还能在运行通过反射获取到,这也是我们最常用的。

如果不写的话,会怎样??

@Documented:

  @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

注解的继承:

注解是如何继承的!

@Inherited:

  @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

  注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

  当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

自定义注解:

  使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

  定义注解格式:

 public @interface 注解名 {定义体}

  注解参数的可支持数据类型:

1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)

2.String类型

3.Class类型

4.enum类型

5.Annotation类型

6.以上所有类型的数组

Annotation类型里面的参数该怎么设定:

第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;

第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;

1

2

3

4

5

6

7

8

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.FIELD})

@Documented

@Inherited

public @interface Bind {

    int value() default 1;

    boolean canBeNull() default false;

}

这就是自定义注解的形式,我们用@interface 表明这是一个注解,Annotation只有成员变量,没有方法。Annotation的成员变量在Annotation定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。比如上面的value和canBeNull。

如果我们是自定义注解,则通过前面分析,我们自定义注解如果只存着源码中或者字节码文件中就无法发挥作用,而在运行期间能获取到注解才能实现我们目的,所以自定义注解中肯定是使用 @Retention(RetentionPolicy.RUNTIME)

使用反射获取注解信息

前面已经说了,Annotation是被动的元数据,永远不会有主动行为,所以我们需要通过使用反射,才能让我们的注解产生意义。

反射&注解的使用:

举例:获取retrofit上面的注解信息  demo:

第一步:定义GET注解

import static java.lang.annotation.ElementType.METHOD;

import java.lang.annotation.Documented;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

/**

* @author: pengcaihua

* @date: 2021/12/22

*/

@Retention(RetentionPolicy.RUNTIME)

@Target(METHOD)

@Documented

public @interface GET {

Stringvalue()default "";

}

第二步:使用运行时注解

public interface ApiService {

@GET("app/payOrder")

ObservablecreateMutilOrder(@QueryMap Map map);

    @POST("app/queryOrder")

// 请求地址可以放在这里

    ObservablepostRequest1();//Json代码数据返回的实体类

}

第三步:反射解析得到运行时注解

* 目的:获取得到方法的注解

* 1.得到当前类

* 2.知道当前方法

* 3.获取注解的内容

* @return

*/

public StringgetMethodAnotation() {

//获取类字节码

    Class cls = ApiService.class;

    try {

//得到方法,后面是方法类型的class

        Method method = cls.getMethod("createMutilOrder", Map.class);

        //通过方法得到方法上面的注解

        GET get = method.getAnnotation(GET.class);

        //通过注解,调用注解的接口得到值

        String value = get.value();

        Log.d("peng", "value:" + value);

    }catch (Exception e) {

e.printStackTrace();

    }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

泛型是怎么解析的,比如在retrofit中的泛型是怎么解析的

运行的时候,通过注解+反射

编译注解:注解+注解处理器

有类注解,方法注解,变量注解。

就需要先获取类,方法和变量,这个时候就必须通过反射!

编译期注解跟运行时注解

运行期注解(RunTime)利用反射去获取信息还是比较损耗性能的,对应@Retention(RetentionPolicy.RUNTIME)。

编译期(Compile time)注解,以及处理编译期注解的手段APT和Javapoet,对应@Retention(RetentionPolicy.CLASS)。 其中apt+javaPoet目前也是应用比较广泛,在一些大的开源库,如EventBus3.0+,页面路由 ARout、Dagger、Retrofit等均有使用的身影,注解不仅仅是通过反射一种方式来使用,也可以使用APT在编译期处理

获取注解对象

因为动态的生成了一个注解的对象,所以,只要“拿到”这个对象,就可以调用这个对象(注解)的方法来获取值。(例如上面调用value()方法就可以获得值)

获取注解对象,需要通过反射。只要拿到注解修饰的部分的反射对象,就可以拿到注解对象。

到这里,你应该已经明白了如何使用反射获取注解的信息,但你一定会困惑这么做有什么用呢?”动态“,”动态“,”动态“

这就是使用注解和反射最大的意义,我们可以动态的访问对象。

注解解析:相应的方法:

/**

 * 获取指定类型的注解

 */ 

public <A extends Annotation> A getAnnotation(Class<A> annotationType); 


/**

 * 获取所有注解,如果有的话

 */ 

public Annotation[] getAnnotations(); 


/**

 * 获取所有注解,忽略继承的注解

 */ 

public Annotation[] getDeclaredAnnotations(); 


/**

 * 指定注解是否存在该元素上,如果有则返回true,否则false

 */ 

public boolean isAnnotationPresent(Class<? extends Annotation> annotationType); 


/**

 * 获取Method中参数的所有注解

 */ 

public Annotation[][] getParameterAnnotations(); 

几个常用的方法:

field.isAnnotationPresent:这个类是否有主注解

BindPort port = field.getAnnotation(BindPort.class);  通过变量得到类

首先遍历循环所有的属性,如果当前属性被指定的注解所修饰,那么就将当前属性的值修改为注解中成员变量的值。

上面的代码中,找到被BindPort修饰的属性,然后将BindPort中value的值赋给该属性。

这里setAccessible(true)的使用时因为,我们在声明port变量时,其类型为private,为了确保可以访问这个变量,防止程序出现异常。

方法使用注解

上面对于类属性(成员变量)设定注解,可能还不能让你感受到注解&反射的优势,我们再来看一下类的方法使用注解会怎样。

我们还是先定义一个注解

1

2

3

4

5

\

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

参考:自定义注解实现工厂模式

https://blog.csdn.net/wuyuxing24/article/details/81139846

参考博客:非常不错

https://www.jianshu.com/p/d4978bbce12a

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容