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实现原理的更多相关文章
- Java学习:注解,反射,动态编译
狂神声明 : 文章均为自己的学习笔记 , 转载一定注明出处 ; 编辑不易 , 防君子不防小人~共勉 ! Java学习:注解,反射,动态编译 Annotation 注解 什么是注解 ? Annotat ...
- Java学习之注解篇
Java学习之注解篇 0x00 前言 续上篇文章,这篇文章就来写一下注解的相关内容. 0x01 注解概述 Java注解(Annotation)又称Java标注,是JDK5.0约会的一种注释机制. 和J ...
- 0035 Java学习笔记-注解
什么是注解 注解可以看作类的第6大要素(成员变量.构造器.方法.代码块.内部类) 注解有点像修饰符,可以修饰一些程序要素:类.接口.变量.方法.局部变量等等 注解要和对应的配套工具(APT:Annot ...
- java中的注解(Annotation)
转载:https://segmentfault.com/a/1190000007623013 简介 注解,java中提供了一种原程序中的元素关联任何信息.任何元素的途径的途径和方法. 注解是那些插入到 ...
- java学习之注解
0x00前言 1.注解是什么: (1)可以叫做注释类型,注解是一种引用数据类型,编译后也是生成class文件 (2)提供信息给编译器: 编译器可以利用注解来探测错误和警告信息 比如 @Override ...
- Java学习之==>注解
一.概述 关于注解,首先引入官方文档的一句话:Java 注解用于为 Java 代码提供元数据.作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的.接下我将从注解的定义. ...
- 注解Annotation实现原理与自定义注解例子
什么是注解? 对于很多初次接触的开发者来说应该都有这个疑问?Annontation是Java5开始引入的新特征,中文名称叫注解.它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metada ...
- java AOP使用注解@annotation方式实践
java AOP使用配置项来进行注入实践 AOP实际开发工作比较常用,在此使用注解方式加深对面向切面编程的理解 废话不多少,先看下面的实例代码 场景: 1.未满一岁的小孩,在执行方法之前打印:“ ...
- java基础篇---注解(Annotation)
一.概念 Annontation是Java5开始引入的新特征.中文名称一般叫注解.它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类.方法.成员变量等)进行关 ...
随机推荐
- VisualStudio2013 如何打开之前版本开发的(.vdproj )安装项目
当你的项目使用早于 visualstudio2013 的版本开发并且使用 Visual Studio Installer 制作安装项目时,在升级至 VS2013 后会发现新安装项目无法打开, VS20 ...
- OpenCASCADE Shape Location
OpenCASCADE Shape Location eryar@163.com Abstract. The TopLoc package of OpenCASCADE gives resources ...
- Android数据加密之Base64编码算法
前言: 前面学习总结了平时开发中遇见的各种数据加密方式,最终都会对加密后的二进制数据进行Base64编码,起到一种二次加密的效果,其实呢Base64从严格意义上来说的话不是一种加密算法,而是一种编码算 ...
- PHP好用但又容易忽略的小知识
1.PHP函数之判断函数是否存在 当我们创建了自定义函数,并且了解了可变函数的用法,为了确保程序调用的函数是存在的,经常会先使用function_exists判断一下函数是否存在.同样的method_ ...
- 火星坐标、百度坐标、WGS-84坐标相互转换及墨卡托投影坐标转经纬度JavaScript版
火星坐标 火星坐标是国家测绘局为了国家安全在原始坐标的基础上进行偏移得到的坐标,基本国内的电子地图.导航设备都是采用的这一坐标系或在这一坐标的基础上进行二次加密得到的.火星坐标的真实名称应该是GCJ- ...
- 面向对象相关知识点xmind
- HttpPost过程中使用的URLEncoder.encode(something, encode)
URLEncoder.encode("刘美美", "utf-8").toString() = %E5%88%98%E7%BE%8E%E7%B ...
- Mysql - 存储过程/自定义函数
在数据库操作中, 尤其是碰到一些复杂一些的系统, 不可避免的, 会用到函数/自定义函数, 或者存储过程. 实际项目中, 自定义函数和存储过程是越少越好, 因为这个东西多了, 也是一个非常难以维护的地方 ...
- linux系统内存爆满的解决办法!~
1.首先用free工具检查一下内存的使用情况: 这个是我的linux时时数据 Mem: 4046824 763620 3283204 9004 10284 61560 -/+buffers/cach ...
- sublime text 3 + python配置,完整搭建及常用插件安装
四年的时间,一直使用EmEditor编辑器进行Python开发,之前是做面向过程,只需要将一个单独的py文件维护好即可,用着也挺顺手,但是最近在做面向对象的开发,不同的py文件中相互关联较多,感觉单纯 ...