java注解以及自定义运行时注解

前言:
Android中比较常用的框架使用了注解技术
ButterKnife,Dagger2通过编译时注解处理技术在编译时期通过apt生成代码完成注入
EventBus通过注解+反射的机制开发的组件间进行通讯的框架,
Retrofit通过注解+动态代理来搭建了一个框架,使我们只需要调用一个方法就能请求一个API
所以说如果想要去理解这些开源框架是如何实现的,那么注解技术是必不可少的需要我们去了解的.

通过如下几个小节:

Untitled Diagram(3).png

注解的概念

什么是注解

是java5提供了一种安全的类似注释的机制,她提供了一种安全的类似与注视的机制,用于为java程序提供元数据,为程序的元素添加(类,变量,方法)直接明了的说明.注解不会影响代码的执行.也可以理解为一种接口,程序可以通过反射或者apt等一些注解处理框架来对注解进行使用,程序通过注解对象来获取元数据.


总结来讲注解就是java提供的一种对程序中任何元素(类,方法,变量)和任何元数据关联的一种途径.

什么是元数据

元数据是用来描述数据的数据.通俗的一点来讲,元数据就是用来描述代码之间的关系,或者代码与资源之间的关系.
1.元数据是以标签的形式存放在java代码中
2.元数据描述的信息属于类型安全的
3.元数据需要编译编译器之外的工具而外的处理用来生成java代码

java以及android中常见的注解

@Override 用于覆盖超类提供的方法时使用,如果为覆盖方法,则编译器会给一个异常.

//正确覆盖父类的方法
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  }

//方法名写错   错误覆盖父类的方法,编译器会给出错误异常.
  @Override
  protected void onXXX(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 }

@Deprecated 用于标注过时的类或者方法.与javadoc中的deprecated不同,他是编译器所能辨识的注解,而javadoc中的是javadoc所识别的注解用于表示,这个元素过期的原因,以及建议替代的元素

    /**
     * @deprecated Implement {@link #onStartCommand(Intent, int, int)} instead.
     */
    @Deprecated
    public void onStart(Intent intent, int startId) {
    }

@SuppressWarnning 用于忽略编译器检查到的警告

Android 中常见的注解

@NoneNull 检查不允许元素为null
@CallSuper 重写父类方法时必须调用父类中的方法.
@StringRes LayoutRes 用于标识资源的类型,比如需要接受一个字符资源的id就可以用@StringRes标注,这是如果传入的为图片id编译器给出警告
@WorkThread @UIThread 比如 可以制定方法在UI或工作线程,如果错误则给出警告.

那么我们如何根据自己的需求来自定义一个注解呢?

注解类:

在自定义注解时创建@interface表示创建的是一个注解类
在注解类中定义注解方法只能用public 或默认修饰 并且方法中不能有参数. 返回类型为注解参数类型,而方法名为注解参数名,通过defult来定义一个默认的注解参数的值.同时java提供了4种元注解来标识我们自定义注解的功能.

元注解:
是一种标注注解的注解
java提供了4种元注解来标识我们自定义注解的功能.

@Target 用于标注注解的使用范围 java提供了TYPE,METHOD,FIELD等可参数
TYPE:表示该注解能用于类,接口,枚举
METHOD:方法
FIELD:成员变量

@Retention 标注注解生命周期 也就是描述注解在什么范围生效分为三种参数:
Source:编译有效,只在源文件中有效,比如java提供的三个注解都是Source
Class:在编译时有效,注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
RUNTIME:在jvm中有效,也就是运行时有效,只有RUNTIME标注过的注解才能通过反射机制获取到Annotations.

@Document 将注解包含在javadoc中

@Inherited 子类默认继承父类的注解

来看下自定义注解的简单使用方式,这里先定义3个运行时注解:

  // 适用类、接口(包括注解类型)或枚举
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ClassInfo {
    String value();
}
// 适用field属性,也包括enum常量
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FieldInfo {
    int[] value();
}
// 适用方法
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodInfo {
    String name() default "long";
    String data();
    int age() default 27;
}

这3个注解分别适用于不同的元素,并都带有不同的属性,在使用注解是需要设置这些属性值。

再定义一个测试类来使用这些注解:

/**
 * 测试运行时注解
 */
@ClassInfo("Test Class")
public class TestRuntimeAnnotation {

    @FieldInfo(value = {1, 2})
    public String fieldInfo = "FiledInfo";

    @FieldInfo(value = {10086})
    public int i = 100;

    @MethodInfo(name = "BlueBird", data = "Big")
    public static String getMethodInfo() {
        return TestRuntimeAnnotation.class. 

();
    }
}

使用还是很简单的,最后来看怎么在代码中获取注解信息:

/**
 * 测试运行时注解
 */
private void _testRuntimeAnnotation() {
    StringBuffer sb = new StringBuffer();
    Class<?> cls = TestRuntimeAnnotation.class;
    Constructor<?>[] constructors = cls.getConstructors();
    // 获取指定类型的注解
    sb.append("Class注解:").append("\n");
    ClassInfo classInfo = cls.getAnnotation(ClassInfo.class);
    if (classInfo != null) {
        sb.append(Modifier.toString(cls.getModifiers())).append(" ")
                .append(cls.getSimpleName()).append("\n");
        sb.append("注解值: ").append(classInfo.value()).append("\n\n");
    }

    sb.append("Field注解:").append("\n");
    Field[] fields = cls.getDeclaredFields();
    for (Field field : fields) {
        FieldInfo fieldInfo = field.getAnnotation(FieldInfo.class);
        if (fieldInfo != null) {
            sb.append(Modifier.toString(field.getModifiers())).append(" ")
                    .append(field.getType().getSimpleName()).append(" ")
                    .append(field.getName()).append("\n");
            sb.append("注解值: ").append(Arrays.toString(fieldInfo.value())).append("\n\n");
        }
    }

    sb.append("Method注解:").append("\n");
    Method[] methods = cls.getDeclaredMethods();
    for (Method method : methods) {
        MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);
        if (methodInfo != null) {
            sb.append(Modifier.toString(method.getModifiers())).append(" ")
                    .append(method.getReturnType().getSimpleName()).append(" ")
                    .append(method.getName()).append("\n");
            sb.append("注解值: ").append("\n");
            sb.append("name: ").append(methodInfo.name()).append("\n");
            sb.append("data: ").append(methodInfo.data()).append("\n");
            sb.append("age: ").append(methodInfo.age()).append("\n");
        }
    }

Log.d(TAG,sb.toString());
}

运行结果如下图:

2017-08-10 18:09:01屏幕截图.png

这个自定义运行时注解是很简单的例子,有很多优秀的开源项目都有使用运行时注解来处理问题,有兴趣可以找一些来研究。因为涉及到反射,所以运行时注解的效率多少会受到影响,现在很多的开源项目使用的是编译时注解,关于编译时注解下一章来介绍。

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

推荐阅读更多精彩内容