注解是 Java 5引入,用来为类、方法、字段和参数等Java结构 提供额外信息 的机制

@Override 仅对 Java编译器 有用,为Java编译器 引用一条新的编译规则 ,编译完成后,它的使命也结束了

Java的注解机制允许开发人员 自定义注解 ,这些自定义注解同样可以为Java编译器 添加编译规则

这种功能需要由开发人员提供,并且以 插件的形式 接入Java编译器中,这些插件被称之为 注解处理器

除了 引入新的编译规则 外,注解处理器还可以用于 修改已有的Java源文件 (不推荐)和 生成新的Java源文件

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.SOURCE)

public @interface Override {

}

// 元注解@Target:用来限定目标注解所能标注的Java结构

// 元注解@Retention:用来限定目标注解的生命周期

// SOURCE:源代码,一旦源代码被编译为字节码,注解便会被擦除

// CLASS:源代码+字节码

// RUNTIME:源代码+字节码+运行时

原理 + 用途

编译过程

将源文件解析为 抽象语法树

调用已注册的 注解处理器

如果该过程 生成了新的源文件 ,编译器将重复第1、2步

每次重复成为 一轮

第一轮解析处理的是输入至编译器中的已有源文件

当注解处理器不再生成新的源文件,将进入最后一轮

生成 字节码

主要用途

定义编译规则,并检查被编译的源文件

修改已有源代码,涉及Java编译器的内部API,不推荐

生成新的源代码

定义编译规则

import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.FIELD})

@Retention(RetentionPolicy.SOURCE)

public @interface CheckGetter {

}

CheckGetter的目的:遍历被标注的类中的实例字段,并检查有没有对应的getter方法

实现注解处理器

Processor接口

public interface Processor {

void init(ProcessingEnvironment processingEnv);

Set getSupportedAnnotationTypes();

SourceVersion getSupportedSourceVersion();

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

...

}

init:注解处理器的初始化代码

不采用 构造器 的原因是在Java编译器中, 注解处理器实例是通过反射生成的

因此每个注解处理器类都需要定义一个 无参构造器

通常来说不需要声明任何构造器,而是 依赖于Java编译器自动插入一个无参构造器

具体的初始化代码,放入 init 方法中

getSupportedAnnotationTypes:返回该注解处理器 所支持的注解类型

getSupportedSourceVersion:返回该注解处理器 所支持的Java版本 ,通常需要 与Java编译器的版本一致

process:最为 关键 的注解处理方法

AbstractProcessor抽象类

AbstractProcessor 实现了 init , getSupportedAnnotationTypes 和 getSupportedSourceVersion 方法

它的子类可以通过 @SupportedAnnotationTypes 和 @SupportedSourceVersion 注解来声明所支持的 注解类型 以及 Java版本

import javax.annotation.processing.*;

import javax.lang.model.SourceVersion;

import javax.lang.model.element.*;

import javax.lang.model.util.ElementFilter;

import javax.tools.Diagnostic;

import java.util.Set;

@SupportedAnnotationTypes("CheckGetter")

@SupportedSourceVersion(SourceVersion.RELEASE_8)

public class CheckGetterProcessor extends AbstractProcessor {

@Override

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

// annotations:该注解处理器所能处理的注解类型

// roundEnv:囊括当前轮生成的抽象语法树RoundEnvironment

// roundEnv.getElementsAnnotatedWith(CheckGetter.class) -> 获取所有被@CheckGetter注解的类

for (TypeElement annotatedClass : ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(CheckGetter.class))) {

for (VariableElement field : ElementFilter.fieldsIn(annotatedClass.getEnclosedElements())) {

if (!containsGetter(annotatedClass, field.getSimpleName().toString())) {

processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,

String.format("getter not fund for '%s.%s'.", annotatedClass.getSimpleName(),

field.getSimpleName()));

}

}

}

return false;

}

private static boolean containsGetter(TypeElement typeElement, String name) {

// 拼接getter名称

String getter = "get" + name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();

for (ExecutableElement executableElement : ElementFilter.methodsIn(typeElement.getEnclosedElements())) {

if (!executableElement.getModifiers().contains(Modifier.STATIC)

&& executableElement.getSimpleName().toString().equals(getter)

&& executableElement.getParameters().isEmpty()) {

return true;

}

}

return false;

}

}

process方法涉及处理各种不同类型的Element,分别指代Java程序的各个 结构

// package me.zhongmingmao.advanced.annotation; // PackageElement

@CheckGetter

public class Foo { // TypeElement

int a; // VariableElement

static int b; // VariableElement

Foo() { // ExecutableElement

}

void setA( // ExecutableElement

int newA // VariableElement

) {

}

}

Java结构之间也存在从属关系, TypeElement.getEnclosedElements() 将获取 字段,构造器和方法

注册

在将注解处理器编译成class文件后,就可以将其 注册为Java编译器的插件 ,并用来 处理其他源代码

$ ll

total 24

-rw-r--r-- 1 zhongmingmao staff 147B 1 7 22:53 CheckGetter.java

-rw-r--r-- 1 zhongmingmao staff 2.0K 1 7 22:54 CheckGetterProcessor.java

-rw-r--r-- 1 zhongmingmao staff 316B 1 7 22:55 Foo.java

$ javac CheckGetter.java CheckGetterProcessor.java

$ javac -cp . -processor CheckGetterProcessor Foo.java

错误: getter not fund for 'Foo.a'.

错误: getter not fund for 'Foo.b'.

2 个错误

$ ll

total 40

-rw-r--r-- 1 zhongmingmao staff 385B 1 7 22:57 CheckGetter.class

-rw-r--r-- 1 zhongmingmao staff 147B 1 7 22:53 CheckGetter.java

-rw-r--r-- 1 zhongmingmao staff 3.1K 1 7 22:57 CheckGetterProcessor.class

-rw-r--r-- 1 zhongmingmao staff 2.0K 1 7 22:54 CheckGetterProcessor.java

-rw-r--r-- 1 zhongmingmao staff 316B 1 7 22:55 Foo.java

修改与生成源代码

注解处理器并 不能真正地修改源代码

实际修改的是 由Java源代码生成的抽象语法树

在其中修改已有的树节点或者插入新的树节点,从而使生成的字节码发生变化

对抽象语法树的修改涉及了 Java编译器的内部API ,很可能 随版本变更而失效 ,不推荐

样例:lombok

相对于修改源代码,用注解处理器 生成源代码更为常用

转载请注明出处:http://zhongmingmao.me/2019/01/07/jvm-advanced-annotation-processor/

访问原文「 JVM进阶 -- 浅谈注解处理器 」获取最佳阅读体验并参与讨论

java 注解处理器的作用_JVM进阶 -- 浅谈注解处理器相关推荐

  1. Java架构师成长之道之浅谈计算机系统架构

    Java架构师成长之道之浅谈计算机系统架构 Java架构师成长之旅 1.1 信息技术发展趋势 目前信息技术主要经历了互联网.移动互联网以及以大数据.云计算.人工智能和区块链为代表的新兴技术三个阶段.而 ...

  2. 浅谈blackfin537处理器中sport口时分复用的理解

    浅谈blackfin537处理器中sport口时分复用的理解 基于自动缓冲DMA模式下的Sport口的多通道操作   农民伯伯 zhangtaolaosan3@126.com 1, 基础知识 在给出例 ...

  3. java 线程aba,浅谈Java中ABA问题及避免,浅谈javaaba避免

    浅谈Java中ABA问题及避免,浅谈javaaba避免 本文主要研究的是关于Java中ABA问题及避免的相关内容,具体如下. 在<Java并发实战>一书的第15章中有一个用原子变量实现的并 ...

  4. 注解@resource的作用_Bean基于Annotation(注解)的装配方式

    在Spring中,尽管使用XML配置文件可以实现Bean的装配工作,但如果应用中有很多Bean时,会导致XML配置文件过于臃肿,给后续的维护和升级工作带来一定的困难.为此,Spring提供了对Anno ...

  5. java程序的装载与检查_浅谈Java类型装载、连接与初始化

    类型装载.连接与初始化 Java虚拟机通过装载.连接和初始化一个Java类型,使该类型可以被正在运行的Java程序所使用.其中装载就是把二进制形式的Java class文件读入Java虚拟机中去;连接 ...

  6. java接口与类相同不同_浅谈java的接口和C++虚类的相同和不同之处

    C++虚类相当于java中的抽象类,与接口的不同之处是: 1.一个子类只能继承一个抽象类(虚类),但能实现多个接口 2.一个抽象类可以有构造方法,接口没有构造方法 3.一个抽象类中的方法不一定是抽象方 ...

  7. java中修饰常量的事_浅谈java中的声明常量为什么要用static修饰

    今天定义一个类常量,想着也只有这个类可以用到,就没用static关键字修饰.结果sonar代码检查提示: Rename this field "PERSON_TYPE_USER" ...

  8. java编译异常和运行时异常_浅谈异常结构图、编译期异常和运行期异常的区别...

    异常处理一般有2种方式,要么捕获异常try-catch,要么抛出异常throws 如果一个方法后面抛出一个运行时期异常(throws RuntimeException),调用者无须处理 如果一个方法后 ...

  9. java多态主要体现在哪方面,浅谈java多态的实现主要体现在哪些方面

    thinking in java3中的多态 People are often confused by other, non-object-oriented features of Java, like ...

  10. java对比swith和if的不同,浅谈选择结构if语句和switch语句的区别

    1.选择结构if语句格式及其使用 A:if语句的格式: if(比较表达式1) { 语句体1; }else if(比较表达式2) { 语句体2; }else if(比较表达式3) { 语句体3; } . ...

最新文章

  1. arm32 linux 内存分布,gcc代码反汇编查看内存分布[2]: arm-linux-gcc
  2. 7步搞定Python数据可视化,业界大牛出品教程,Jupyter、Colab都有在线版
  3. IP地址分类:静态/动态/公共/私有
  4. poj 3723 Conscription (并查集)
  5. Leetcode 142. 环形链表 II (每日一题 20210902)
  6. java long 溢出_关于溢出:Java乘法运算行为
  7. TextArea换行 滚动条
  8. Typora markdown公式换行等号对齐_Typora编写博客格式化文档的最佳软件
  9. linux下用gtk+写比赛赌博GUI小游戏
  10. 24.TCP/IP 详解卷1 --- TCP 的未来和性能
  11. angular js的元素指令
  12. Centos7安装Fail2Ban并利用163邮箱发送邮件提醒功能
  13. 《反欺骗的艺术》读后感
  14. Sketchup2019安装包安装教程
  15. 入门级概述光学相干层析(OCT)原理
  16. 计算机二级mysql上机考试操作步骤_计算机二级mysql上机操作题怎么做
  17. 借助Jackson的JsonTypeInfo注解实现多态类的解析
  18. 每日一诗词 —— 越人歌
  19. python - 输入某年某月某日,判断这一天是这一年的第几天?
  20. 比金蝶1000元服务还强的SQL语句

热门文章

  1. latex 引用文献cite, citet,citep的区别
  2. 181201每日一句
  3. 文献管理三剑客之endnote broken attachments
  4. opencv Basic Drawing
  5. Atitit 软件开发体系法规大全v2.docx Atitit 软件开发体系大全 目录 1. 基本法(类似宪法) 1 2. 行政法 1 3. 流程法民商法 2 3.1. Ui提升法 2 3.2. 功
  6. Atitit sdk封装的艺术 艾提拉著 1. 重要模块8个 1 1.1. Collections集合,core,net,io,Script,sql,text,fp 1 1.2. 全部模块25
  7. Atitit 格式转换的艺术 以excel转换txt为例
  8. Atitit 图像处理知识点  知识体系 知识图谱
  9. Atitit vod click event design flow  视频点播系统点击事件文档
  10. paip.提升效率--僵尸代码的迷思