再谈JAVA注解和反射

最近在研究阿里开源的ARouter框架,碰到了较多注解的使用。结合ButterKnife开源框架的使用,越来越体会到熟练使用注解的重要性。故针对注解再次进行学习和总结,重要的是辅助代码来验证。

Annotation注解

Annontation是Java5开始引入的新特征,中文名称叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。

JDK内置注解

常用的标准注解有:

  1. @Override,主要用于在子类中覆盖父类中的方法
  2. @Deprecated,用来标志被弃用的代码,编译器会进行警告
  3. @SuppressWarnings,用于忽略编译器的警告信息。
    相信大家在日常工作中经常使用以上三种注解。

元注解

开发人员可以根据自己的需要自定义注解,这时需要用到@Retention, @Target, @Inherited, @Documented这几个元注解。

  1. @Rentention定义注解的保留级别。取值范围是RetentionPolicy类型,源码定义如下:
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

包括SOURCE:在代码编写阶段存在,编译时被丢弃。如@Override
CLASS:编译阶段注解保留在class文件中,VM运行时不需要保留。一般用来动态生成java代码,如ARouter,ButterKnife框架
RUNTIME:编译阶段注解保留在class文件中,VM运行时仍保留注解。一般与反射结合使用,如Retrofit。

  1. @Target定义注解可以用于什么对象。取值范围是ElementType类型,源码定义如下:
public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

其中常用的类型有TYPE:类、接口(包括注解)、枚举
FIELD:成员变量
METHOD:成员方法
PARAMETER:方法参数
ANNOTATION_TYPE:注解的注解

  1. @Documented标志注解将包含在Javadoc中

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

关于每种元注解的具体定义,可以在Android Studio中方便的查看源码文件。reading the fuck code!

自定义注解

开发人员可以使用上面的元注解来定义自己的注解。示例如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeAnno {
    String value();

    boolean isAlive() default false;
}

使用@interface定义注解,类名即为注解名。自定义注解中只能定义方法,所有的方法均没有参数,也没有修饰符(默认是public&abstract修饰符),方法的返回值是基本类型、String、Classs、Annotation、Enum或者对应的一位数组。方法可以通过default设置默认值。

注解使用时,如果只有一个方法,则可直接“注解名(值)”来使用;如果有多个方法,则需要依次赋值“注解名(方法名=值,方法名=值...)”。如果方法使用default赋值,则不必在使用时显示赋值。

    @RuntimeAnno(value="11111")
    @RuntimeAnno("22222")
    @RuntimeAnno(value="33333", isAlive = true)

典型的注解定义如下图:


Override注解定义
ButterKnife BindView注解定义
ButterKnife OnClick注解定义

其中,ButterKnife的OnClick注解使用了自定义注解ListenerClass,具体定义可参考ButterKnife源码。

运行时(RUNTIME)注解的解析

以Method为例,参考Method的源码,可以看到有如下几个方法:

    /**
     * {@inheritDoc}
     * @throws NullPointerException  {@inheritDoc}
     * @since 1.5
     */
    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        return super.getAnnotation(annotationClass);
    }

    /**
     * {@inheritDoc}
     * @since 1.5
     */
    public Annotation[] getDeclaredAnnotations()  {
        return super.getDeclaredAnnotations();
    }

    /**
     * {@inheritDoc}
     * @since 1.5
     */
    @Override
    public Annotation[][] getParameterAnnotations() {
        // Android-changed: This is handled by Executable.
        return super.getParameterAnnotationsInternal();
    }

其中getAnnotation(AnnotationName.class) 可获取该 Target 某个 Annotation 的信息;因为一个 Target 可以被多个 Annotation 修饰.
getAnnotations() 则可获取该 Target 所有 Annotation. getParameterAnnotations可以获取方法的所有参数的注解,返回值是一个二维数组。

其他Target,如Filed,Class,获取Annotation的方法可参考java源码。

高版本的jdk还有isAnnotationPresent(AnnotationName.class) 方法,来判断Target 是否被某个 Annotation 修饰。

编译时(CLASS)注解解析

编译器解析编译时注解,需要做的是1、自定义类继承AbstractProcessor;2、重写process函数。具体使用方法,可参考网上的其他文档。

ButterKnife就是使用编译时注解,生成JAVA文件。

注解的作用

1、JDK标准注解,可用来生成JavaDoc,如@param @return;可用来进行代码检查,如@Override,@Deprecated。
2、目前较多框架使用注解,来自动生成代码或替代配置文件的功能。注解可以使编码更简洁,学习注解可以理解使用开源框架,甚至自定义框架来解决问题。
3、注解和反射配合使用,可以使用简洁的代码实现复杂的功能,笔者目前的网络库入参就采用了注解,动态代理和反射技术。可以参考反射注解与动态代理综合使用,该文档写的简单易懂。

参考文档:
注解Annotation实现原理与自定义注解例子
反射注解与动态代理综合使用
框架开发之Java注解的妙用

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,392评论 25 707
  • 前言 现在在我们构建自己或公司的项目中,或多或少都会依赖几个流行比较屌的第三方库,比如:Butter Knife、...
    戴定康阅读 3,927评论 0 17
  • 时间的齿轮转到了2018年,回首往昔,不知道你的感受是满足呢?还是懊悔? 我想,大多数人都是悔不当初的,奈何被岁月...
    诺feiyang阅读 271评论 0 0
  • 互联网赚钱的套路,就是用内容把精准客户引导到个人微信上,然后用催眠系统,加上激活系统来快速变现成交。 微信我们称之...
    知聊阅读 324评论 0 0
  • 兑为口,为说,不同的语言,不同语气给对方的不一样的感觉,互相交流,互相赞叹,我们把向善的说出来。 兑还有喜悦的意思...
    菜问妈妈阅读 162评论 0 0