美文网首页
再谈JAVA注解和反射

再谈JAVA注解和反射

作者: zizi192 | 来源:发表于2018-07-13 16:07 被阅读0次

    最近在研究阿里开源的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注解的妙用

    相关文章

      网友评论

          本文标题:再谈JAVA注解和反射

          本文链接:https://www.haomeiwen.com/subject/oeljpftx.html