`

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中网络实现原理。

干我们这行,啥时候懈怠,就意味着长进的停止,长进的停止就意味着被淘汰,只能往前冲,直到凤凰涅槃的一天!
分享到:
评论

相关推荐

    注解Annotation实现原理与自定义注解例子.pdf

    注解Annotation实现原理与自定义注解例子 每当你创建描述符性质的类或者接口时,一旦其中包含重复性的工 作,就可以考虑使用注解来简化与自动化该过程。 Java提供了四种元注解,专门负责新注解的创建工作

    java1.5 annotation注释源代码

    Java 1.5 引入了一种新的元编程机制——注解(Annotation),极大地增强了代码的可读性和可维护性。注解是一种在代码中添加元数据的方式,它允许程序员在源代码上添加一些信息,这些信息可以被编译器或运行时环境...

    JAVA Annotation学习

    Java注解(Annotation)是Java语言的一个重要特性,它为元数据提供了强大的支持。元数据是关于数据的数据,可以提供额外的信息,这些信息虽然不是程序运行所必需的,但能够帮助编译器、JVM(Java虚拟机)或工具更好...

    Annotation--学习:JDK内建Annotation

    自定义注解需要相应的处理器来处理,这通常通过Java的注解处理工具(Annotation Processing Tool, apt)或Java Compiler API实现。处理器会在编译时自动触发,解析注解并执行相应逻辑。 6. **注解的应用场景** - ...

    Java Annotation注解技术

    Java Annotation注解技术是自Java SE 5.0版本引入的一种元编程机制,它允许程序员在源代码的各个层面(如类、方法、变量等)添加元数据,以供编译器、JVM或第三方工具在编译时或运行时进行处理。Annotation简化了...

    spring中注解的实现原理

    本文将深入探讨Spring中注解的实现原理,帮助你更好地理解和运用这些核心概念。 首先,让我们从注解的基础知识开始。注解在Java中是一种元数据,它提供了在代码中嵌入信息的方式,而这些信息可以被编译器或运行时...

    Java 注解Annotation实例上手文档

    Java注解(Annotation)自JDK 5.0引入以来,已经成为Java语言的重要特性之一,它为代码元数据提供了一种标准化的方式,使得编译器、工具和其他框架能够理解和处理这些元数据。在Java中,注解是一种元数据,可以被...

    Java annotation元注解原理实例解析

    Java Annotation元注解原理实例解析 Java annotation是Java语言中的一种元编程技术,用于在编译期或运行期提供元数据,以便简化代码、提高开发效率和程序的可读性。在这篇文章中,我们将深入探讨Java annotation的...

    使用Java自定义注解模拟实现SpringBoot相关注解.zip

    在Java编程中,注解(Annotation)是一种元数据,它提供了在代码中插入信息的方式,这些信息可以被编译器或运行时环境读取。...在学习和理解这些注解的工作原理时,这样的模拟实现是一个很好的实践。

    利用java反射、注解及泛型模拟ORM实现

    在Java编程中,反射、注解(Annotation)和泛型是三个非常重要的特性,它们各自在不同的场景下发挥着关键作用。这篇博文“利用java反射、注解及泛型模拟ORM实现”旨在探讨如何结合这三种技术来实现对象关系映射(ORM...

    java 自定义注解验证

    首先,我们需要理解Java注解的基本原理。注解是一种元数据,它提供了一种安全的方法来将信息附加到代码中,而不直接影响代码的执行。Java提供了内置的注解,如`@Override`、`@Deprecated`等,但自定义注解允许我们...

    Java基础之Annotation解读.docx

    Java注解(Annotation)是Java 5引入的一种元数据机制,用于向编译器、工具或运行时系统提供关于代码的附加信息。注解不是程序的一部分,不会直接影响代码的执行,但可以通过反射API来访问和处理这些信息,从而影响...

    Android Annotation 安卓注解实例

    通过这个"Android Annotation 实例"的压缩包,你可以深入学习如何在实际项目中运用这些注解,理解它们的工作原理,以及如何通过注解优化代码结构和提高开发效率。实践中,不断尝试并结合个人项目需求,你将更好地...

    学习Spring笔记_AOP_Annotation实现和XML实现

    通过阅读《学习Spring笔记_AOP_Annotation实现和XML实现》,你可以更好地理解Spring AOP的工作原理,以及如何在实际项目中灵活运用。无论是注解还是XML配置,都是为了提高代码的可读性和可维护性,使我们能够更专注...

    Java注解与反射原理说明

    Java注解(Annotation)是Java语言提供的一种元数据机制,允许程序员在代码中嵌入信息,这些信息可以被编译器、JVM或其他工具在编译时或运行时读取和处理。注解的主要用途包括代码的自我描述、编译器检查、运行时的...

    学习Spring笔记_Annotation(注解)_Autowired_Qualifier

    这篇学习Spring笔记将深入探讨这两个注解的用法、原理以及它们在实际开发中的应用场景。 `@Autowired`注解是Spring框架提供的一种依赖注入(Dependency Injection,DI)机制,用于自动装配Bean。它可以根据类型或...

    Java+Annotation.pdf

    ### Java注解(Annotation)详解 #### 一、引言 在Java开发过程中,经常会遇到需要为代码添加元数据的情况,比如标记一个方法为废弃、指定一个类的配置信息等。这种需求催生了Java注解(Annotation)的概念。Java ...

    浙大软件学院培训课件java reflection annotation

    6. 注解处理器:理解APT(Annotation Processing Tool)的工作原理,编写自定义注解处理器实现代码生成。 7. 在Spring框架中使用反射和注解:探讨Spring框架如何利用反射和注解实现依赖注入、AOP(面向切面编程)等...

    Gradle中如何自定义实现Java注解处理器

    在本篇文章中,我们将深入探讨如何在Gradle项目中自定义实现Java注解处理器。 首先,我们需要了解注解处理器的工作原理。Java的注解处理器是在JDK的`javax.annotation.processing`包中定义的,主要由`Processor`...

Global site tag (gtag.js) - Google Analytics