gpt4 book ai didi

Java注解和注解解析器深耕,架构师必会

转载 作者:qq735679552 更新时间:2022-09-27 22:32:09 24 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章Java注解和注解解析器深耕,架构师必会由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

Java注解和注解解析器深耕,架构师必会

什么是元数据(metadata)

  。

元数据由metadata译来,所谓的元数据就是“关于数据的数据”,更通俗的说就是描述数据的数据,对数据及信息资源的描述性信息.比如说一个文本文件,有创建时间,创建人,文件大小等数据,这都可以理解为是元数据. 。

在java中,元数据以标签的形式存在java代码中,它的存在并不影响程序代码的编译和执行,通常它被用来生成其它的文件或运行时知道被运行代码的描述信息。java当中的javadoc和注解都属于元数据. 。

什么是注解(Annotation)?

  。

注解是从java 5.0开始加入,可以用于标注包,类,方法,变量等.比如我们常见的@Override,再或者Android源码中的@hide,@systemApi,@privateApi等 。

对于@Override,多数人往往都是知其然而不知其所以然,今天我就来聊聊Annotation背后的秘密,开始正文. 。

元注解就是定义注解的注解,是java提供给我们用于定义注解的基本注解.在java.lang.annotation包中我们可以看到目前元注解共有以下几个

  1. @Retention 。

  2. @Target 。

  3. @Inherited 。

  4. @Documented 。

  5. @interface 。

下面我们将集合@Override注解来解释着5个基本注解的用法. 。

@interface

@interface是java中用于声明注解类的关键字.使用该注解表示将自动继承java.lang.annotation.Annotation类,该过程交给编译器完成. 。

因此我们想要定义一个注解只需要如下做即可,以@Override注解为例 。

  1. public @interface Override { 

需要注意:在定义注解时,不能继承其他注解或接口

@Retention

@Retention:该注解用于定义注解保留策略,即定义的注解类在什么时候存在(源码阶段 or 编译后 or 运行阶段).该注解接受以下几个参数: RetentionPolicy.SOURCE,RetentionPolicy.CLASS,RetentionPolicy.RUNTIME ,其具体使用及含义如下

Java注解和注解解析器深耕,架构师必会

来看一下@Override注解的保留策略

  1. @Retention(RetentionPolicy.SOURCE) 
  2. public @interface Override { 

这表明@Override注解只在源码阶段存在,javac在编译过程中去去掉该注解. 。

@Target

该注解用于定义注解的作用目标,即注解可以用在什么地方,比如是用于方法上还是用于字段上,该注解接受以下参数

Java注解和注解解析器深耕,架构师必会

以@Override为例,不难看出其作用目标为方法

  1. @Target(ElementType.METHOD) 
  2. public @interface Override { 

到现在,通过@interface,@Retention,@Target已经可以完整的定义一个注解,来看@Override完整定义

  1. @Target(ElementType.METHOD) 
  2. @Retention(RetentionPolicy.SOURCE) 
  3. public @interface Override { 

@Inherited

默认情况下,我们自定义的注解用在父类上不会被子类所继承.如果想让子类也继承父类的注解,即注解在子类也生效,需要在自定义注解时设置@Inherited.一般情况下该注解用的比较少. 。

@Documented

该注解用于描述其它类型的annotation应该被javadoc文档化,出现在api doc中. 。

比如使用该注解的@Target会出出现在api说明中. 。

  1. @Documented 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Target(ElementType.ANNOTATION_TYPE) 
  4. public @interface Target { 
  5.  
  6.  
  7.  
  8.  
  9.     ElementType[] value(); 

借助@Interface,@Target,@Retention,@Inherited,@Documented这五个元注解, 我们就可以自定义注解了,其中前三个注解是任何一个注解都必备具备的. 。

自定义注解

  。

格式

  1. public @interface 注解名 {定义体} 

定义体就是方法的集合,每个方法实则是声明了一个配置参数.方法的名称作为配置参数的名称,方法的返回值类型就是配置参数的类型.和普通的方法不一样,可以通过default关键字来声明配置参数的默认值. 。

需要注意

此处只能使用public或者默认的defalt两个权限修饰符 。

配置参数的类型只能使用基本类型(byte,boolean,char,short,int,long,float,double)和String,Enum,Class,annotation 。

对于只含有一个配置参数的注解,参数名建议设置中value,即方法名为value 。

配置参数一旦设置,其参数值必须有确定的值,要不在使用注解的时候指定,要不在定义注解的时候使用default为其设置默认值,对于非基本类型的参数值来说,其不能为null. 。

像@Override这样,没有成员定义的注解称之为标记注解. 。

注解处理器

  。

上面我们已经学会了如何定义注解,要想注解发挥实际作用,需要我们为注解编写相应的注解处理器.根据注解的特性,注解处理器可以分为运行时注解处理和编译时注解处理器.运行时处理器需要借助反射机制实现,而编译时处理器则需要借助APT来实现. 。

无论是运行时注解处理器还是编译时注解处理器,主要工作都是读取注解及处理特定注解,从这个角度来看注解处理器还是非常容易理解的. 。

注解处理器是(Annotation Processor)是javac的一个工具,用来在编译时扫描和编译和处理注解(Annotation)。你可以自己定义注解和注解处理器去搞一些事情。一个注解处理器它以Java代码或者(编译过的字节码)作为输入,生成文件(通常是java文件)。这些生成的java文件不能修改,并且会同其手动编写的java代码一样会被javac编译。看到这里加上之前理解,应该明白大概的过程了,就是把标记了注解的类,变量等作为输入内容,经过注解处理器处理,生成想要生成的java代码.

运行时注解处理器(不建议使用)

熟悉java反射机制的同学一定对java.lang.reflect包非常熟悉,该包中的所有api都支持读取运行时Annotation的能力,即属性为@Retention(RetentionPolicy.RUNTIME)的注解. 。

在java.lang.reflect中的AnnotatedElement接口是所有程序元素的(Class,Method)父接口,我们可以通过反射获取到某个类的AnnotatedElement对象,进而可以通过该对象提供的方法访问Annotation信息,常用的方法如下

Java注解和注解解析器深耕,架构师必会

运行时注解处理器的编写本质上就是通过反射获取注解信息,随后进行其他操作。编译一个运行时注解处理器就是这么简单。运行时注解通常多用于参数配置类模块.

编译时注解处理器 。

不同于运行时注解处理器,编写编译时注解处理器(Annotation Processor Tool). 。

APT用于在编译时期扫描和处理注解信息.一个特定的注解处理器可以以java源码文件或编译后的class文件作为输入,然后输出另一些文件,可以是.java文件,也可以是.class文件,但通常我们输出的是.java文件.(注意:并不是对源文件修改).如果输出的是.java文件,这些.java文件回合其他源码文件一起被javac编译. 。

你可能很纳闷,注解处理器是到底是在什么阶段介入的呢?好吧,其实是在javac开始编译之前,这也就是通常我们为什么愿意输出.java文件的原因. 。

注解最早是在java 5引入,主要包含apt和com.sum.mirror包中相关mirror api,此时apt和javac是各自独立的。从java 6开始,注解处理器正式标准化,apt工具也被直接集成在javac当中.

我们还是回到如何编写编译时注解处理器这个话题上,编译一个编译时注解处理主要分两步:

1、继承AbstractProcessor,实现自己的注解处理器 。

2、注册处理器,并打成jar包 。

首先来看一下一个标准的注解处理器的格式:

  1. public class MyAnnotationProcessor extends AbstractProcessor { 
  2.  
  3.  
  4.     @Override 
  5.     public Set<String> getSupportedAnnotationTypes() { 
  6.         return super.getSupportedAnnotationTypes(); 
  7.     } 
  8.  
  9.  
  10.     @Override 
  11.     public SourceVersion getSupportedSourceVersion() { 
  12.         return super.getSupportedSourceVersion(); 
  13.     } 
  14.  
  15.  
  16.     @Override 
  17.     public synchronized void init(ProcessingEnvironment processingEnv) { 
  18.         super.init(processingEnv); 
  19.     } 
  20.  
  21.  
  22.     @Override 
  23.     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 
  24.         return false
  25.     } 

Java注解和注解解析器深耕,架构师必会

编写一个注解处理器首先要对ProcessingEnvironment和RoundEnvironment非常熟悉。接下来我们一览这两个类的风采.首先来看一下ProcessingEnvironment类:

  1. public interface ProcessingEnvironment { 
  2.  
  3.  
  4.     Map<String,String> getOptions(); 
  5.  
  6.  
  7.     //Messager用来报告错误,警告和其他提示信息 
  8.     Messager getMessager(); 
  9.  
  10.  
  11.     //Filter用来创建新的源文件,class文件以及辅助文件 
  12.     Filer getFiler(); 
  13.  
  14.  
  15.     //Elements中包含用于操作Element的工具方法 
  16.     Elements getElementUtils(); 
  17.  
  18.  
  19.      //Types中包含用于操作TypeMirror的工具方法 
  20.     Types getTypeUtils(); 
  21.  
  22.  
  23.     SourceVersion getSourceVersion(); 
  24.  
  25.  
  26.     Locale getLocale(); 

Element 。

element表示一个静态的,语言级别的构件。而任何一个结构化文档都可以看作是由不同的element组成的结构体,比如XML,JSON等.

对于java源文件来说, Element代表程序元素:包,类,方法都是一种程序元素 ,他同样是一种结构化文档:

  1. package com.closedevice;             //PackageElement 
  2. public class Main{                  //TypeElement 
  3.     private int x;                  //VariableElement 
  4.     private Main(){                 //ExecuteableElement 
  5.     } 
  6.     private void print(String msg){ //其中的参数部分String msg为TypeElement 
  7.     } 

Java注解和注解解析器深耕,架构师必会

TypeMirror 。

这三个类也需要我们重点掌握:

DeclaredType代表声明类型:类类型还是接口类型,当然也包括参数化类型,比如Set<String>,也包括原始类型 。

TypeElement代表类或接口元素,而DeclaredType代表类类型或接口类型.

TypeMirror代表java语言中的类型.Types包括基本类型,声明类型(类类型和接口类型),数组,类型变量和空类型。也代表通配类型参数,可执行文件的签名和返回类型等。TypeMirror类中最重要的是getKind()方法,该方法返回TypeKind类型,为了方便大家理解,这里附上其源码:

  1. public enum TypeKind { 
  2.     BOOLEAN,BYTE,SHORT,INT,LONG,CHAR,FLOAT,DOUBLE,VOID,NONE,NULL,ARRAY,DECLARED,ERROR,  TYPEVAR,WILDCARD,PACKAGE,EXECUTABLE,OTHER,UNION,INTERSECTION; 
  3.     public boolean isPrimitive() { 
  4.         switch(this) { 
  5.         case BOOLEAN: 
  6.         case BYTE: 
  7.         case SHORT: 
  8.         case INT: 
  9.         case LONG: 
  10.         case CHAR: 
  11.         case FLOAT: 
  12.         case DOUBLE: 
  13.             return true
  14.         default
  15.             return false
  16.         } 
  17.     } 

简单来说,Element代表源代码,TypeElement代表的是源码中的类型元素,比如类。虽然我们可以从TypeElement中获取类名,TypeElement中不包含类本身的信息,比如它的父类,要想获取这信息需要借助TypeMirror,可以通过Element中的asType()获取元素对应的TypeMirror.

然后来看一下RoundEnvironment,这个类比较简单,一笔带过

  1. public interface RoundEnvironment { 
  2.     boolean processingOver(); 
  3.      //上一轮注解处理器是否产生错误 
  4.     boolean errorRaised(); 
  5.      //返回上一轮注解处理器生成的根元素 
  6.     Set<? extends Element> getRootElements(); 
  7.    //返回包含指定注解类型的元素的集合 
  8.     Set<? extends Element> getElementsAnnotatedWith(TypeElement a); 
  9.     //返回包含指定注解类型的元素的集合 
  10.     Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a); 

Filer 。

Filer用于注解处理器中创建新文件,由于Filer用起来实在比较麻烦,后面我们会使用javapoet简化我们的操作. 。

打包注解处理器的时候需要一个特殊的文件 javax.annotation.processing.Processor 在 META-INF/services 路径下 。

Java注解和注解解析器深耕,架构师必会

新建项目必要配置:

  1. //javapoet代码生成框架 
  2. implementation  'com.squareup:javapoet:1.8.0'  
  3. //注解处理器 
  4. implementation 'com.google.auto.service:auto-service:1.0-rc6' 
  5. annotationProcessor  'com.google.auto.service:auto-service:1.0-rc6' 

编译时注解demo示例地址:https://gitee.com/yutg/apt.git 。

项目结构 。

  1. --apt-demo 
  2. ----bindview-annotation(Java Library)//注解定义 
  3. ----bindview-api(Android Library)//定义SDK接口方法 
  4. ----bindview-compiler(Java Library)//注解处理器相关操作及生成java文件 
  5. ----app(Android App) 

原文地址:https://mp.weixin.qq.com/s?__biz=MzA3NzMxODEyMQ==&mid=2666458602&idx=1&sn=2db54eddc29fcef190eb89dc298715b7&utm_source=tuicool&utm_medium=referral 。

最后此篇关于Java注解和注解解析器深耕,架构师必会的文章就讲到这里了,如果你想了解更多关于Java注解和注解解析器深耕,架构师必会的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com