java annotation apt_注解和APT

注解

一种用于解释java代码的标签,用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。

注解种类:

元注解

java自带注解

自定义注解

自定义注解:

语法格式

public @interface MyAnnotation {

}

元注解:用于修饰自定义注解

Retention:说明自定义注解的声明周期

RetentionPolicy.RUNTIME // 注解保留到程序运行时

RetentionPolicy.CLASS // 注解保留到编译

RetentionPolicy.SOURCE // 注解保留到源码阶段(编译后.class文件不会有这个注解)

Documented:文档注解,将自定义注解的元素包含到javadoc文档中(不常用)

Target:描述注解的使用范围

TYPE, // 类、接口、枚举类

FIELD, // 成员变量(包括:枚举常量)

METHOD, // 成员方法

PARAMETER, // 方法参数

CONSTRUCTOR, // 构造方法

LOCAL_VARIABLE, // 局部变量

ANNOTATION_TYPE, // 注解类

PACKAGE, // 可用于修饰:包

TYPE_PARAMETER, // 类型参数,JDK 1.8 新增

TYPE_USE // 使用类型的任何地方,JDK 1.8 新增

Inherited:使自定义注解具有继承性

@Inherited

public @interface InheritAnnotation {

}

@InheritAnnotation

public class Person {

}

class Man extends Person{

}

Man类具有InheritAnnotation注解属性;

Repeatable:表示这个注解可以重复使用

public @interface Persons {

Person[] value();

}

@Repeatable(Persons.class)

public @interface Person {

String value() default "";

}

@Person(value = "222")

@Person(value = "111")

class RepeatableAnnotation{

}

注解的属性:

注解只有成员变量,没有方法。需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型,默认值需要用 default 关键值指定

public @interface Person {

String name();

String position() default "普工";

int age();

boolean isMale();

}

注解属性的提取(运行时注解提取):

使用反射提取类的注解信息;

定义一个Employee注解,用来存放员工的基本信息

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface Employee {

String name();

String position() default "普工";

int age();

boolean isMale();

}

两个员工类,tony,amy,分别指定他们注解的值

@Employee(name = "tony甲",position = "经理",age = 26,isMale = true)

public class Tony {

}

@Employee(name = "amy红",position = "HR",age = 30,isMale = false)

class Amy{

}

通过反射获取他们注解所带的值

public static void main(String[] args) {

Tony tony = new Tony();

Amy amy = new Amy();

Class tonyClass = tony.getClass();

Employee tonyClassAnnotation = (Employee) tonyClass.getAnnotation(Employee.class);

System.out.println(tonyClassAnnotation);

Class amyClass = amy.getClass();

Annotation[] amyClassAnnotations = amyClass.getAnnotations();

Employee amyClassAnnotation = (Employee) amyClassAnnotations[0];

System.out.println(amyClassAnnotation);

}

16e065ead11f

运行结果

注解的作用:

提供信息给编译器:编译器可以利用注解来探测错误和警告信息

编译阶段的处理:软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。

运行时的处理: 某些注解可以在程序运行的时候接受代码的提取值得注意的是,注解不是代码本身的一部分。

APT (编译期注解提取)

APT(Annotation Processing Tool 的简称),可以在代码编译期解析注解,并且生成新的 Java 文件,减少手动的代码输入。

下面介绍APT技术的几个重要概念

AbstractProcess 注解处理器:一个抽象类,用于自定义注解处理器,需要继承这个类,并实现它的四个方法;

AutoService 处理器注册服务:谷歌提供的一个自动注册注解处理器的服务,省去写MATA文件的步骤

JavaPoet 代码生成工具:一个第三方的API,通过这个API可以生成模板类型的java代码

AbstractProcess 和 AutoService 实现编译期解析注解

我们需要创建两个java Module annotation和processors

16e065ead11f

注意这两个都是java Module,annotation用来存放我们的自定义注解,processor用来存放我们的自定义注解处理器;app是我们的工程项目,他们的依赖关系是app依赖annotation和processors,processors依赖annotation;

创建自定义注解,在项目中引用注解

16e065ead11f

16e065ead11f

自定义注解

16e065ead11f

在项目中使用注解

创建自定义注解处理器

首先引入谷歌的自动注册服务,AutoService

implementation 'com.google.auto.service:auto-service:1.0-rc4'

implementation 'com.google.auto:auto-common:0.10'

16e065ead11f

直接添加一个autoService注解就可

自定义注解处理器

@AutoService(Processor.class)

public class MyProcessor extends AbstractProcessor {

@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

}

/**

* 这相当于每个处理器的主函数main(),你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。

* 输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素

* @param annotations 请求处理的注解类型

* @param roundEnv 有关当前和以前的信息环境

* @return 如果返回 true,则这些注解已声明并且不要求后续 Processor 处理它们;

* 如果返回 false,则这些注解未声明并且可能要求后续 Processor 处理它们

*/

@Override

public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {

System.out.println("-----process():-------");

// 通过我们指定的注解返回与这个注解绑定的element集合

Set extends Element> annotationElements = roundEnv.getElementsAnnotatedWith(MyAnnotation.class);

// 遍历当前element集合

for (Element element : annotationElements){

// 通过element的种类将他转成element的实现类

if (element.getKind() == ElementKind.CLASS){

TypeElement typeElement = (TypeElement) element;

// 通过element的具体的实现类获取注解的值

System.out.println(typeElement.getAnnotation(MyAnnotation.class).value());

}

}

return false;

}

// 规定这个注解处理器处理的注解

@Override

public Set getSupportedAnnotationTypes() {

Set set = new LinkedHashSet<>();

set.add(MyAnnotation.class.getCanonicalName());

return set;

}

//指定java版本

@Override

public SourceVersion getSupportedSourceVersion() {

return super.getSupportedSourceVersion();

}

}

下面重点介绍Element接口

Element 代表程序的元素,在注解处理过程中,编译器会扫描所有的Java源文件,并将源码中的每一个部分都看作特定类型的 Element。它可以代表包、类、接口、方法、字段等多种元素种类,具体看getKind()方法中所指代的种类,每个Element 代表一个静态的、语言级别的构件。

/**

* 返回此元素的种类:包、类、接口、方法、字段...,如下枚举值

* PACKAGE, ENUM, CLASS, ANNOTATION_TYPE, INTERFACE,

* ENUM_CONSTANT, FIELD, PARAMETER, LOCAL_VARIABLE,

* EXCEPTION_PARAMETER,METHOD, CONSTRUCTOR, STATIC_INIT,

* INSTANCE_INIT, TYPE_PARAMETER, OTHER, RESOURCE_VARIABLE;

*/

ElementKind getKind();

getKind()用于获取这个Element的具体种类,这些种类都存放在枚举类ElementKind中

Element接口有五个默认实现类:

PackageElement: 表示一个包程序元素

TypeElement: 表示一个类或接口程序元素

VariableElement: 表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数

ExecutableElement: 表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注解类型元素

TypeParameterElement: 表示一般类、接口、方法或构造方法元素的泛型参数

在process()中,我们通过当前环境信息RoundEnvironment和我们指定要解析的注解拿到和这个注解相关的Element集合,再遍历element集合通过它的Kind将它转成具体的Element实现类,然后通过这个实现类去获取注解;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值