Java中的APT的工作过程

APT即Annotatino Processing Tool, 他的作用是处理代码中的注解, 用来生成代码, 换句话说, 这是用代码生成代码的工具, 减少boilerplate代码.

我们通过一个简单的例子来简单APT的工作过程, 因为本文demo不设计ide及gradle等, 请注意包名及import问题.

根据上一篇博客Java中的自定义注解, 首先设计一个自定义注解MyAnnotation.

  1. package com.example;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Retention(RetentionPolicy.SOURCE) // 只保留到编译阶段
  7. @Target(ElementType.TYPE) // 可用于类, 接口..
  8. public @interface MyAnnotation {
  9. }

下面来看一下我们的主角, Processor:

  1. package com.example;
  2. import javax.annotation.processing.AbstractProcessor;
  3. import javax.annotation.processing.ProcessingEnvironment;
  4. import javax.annotation.processing.RoundEnvironment;
  5. import javax.lang.model.SourceVersion;
  6. import javax.lang.model.element.TypeElement;
  7. import java.util.HashSet;
  8. import java.util.Set;
  9. public class MyProcessor extends AbstractProcessor {
  10. // Processor初始化回调
  11. @Override
  12. public synchronized void init(ProcessingEnvironment processingEnv) {
  13. super.init(processingEnv);
  14. System.out.println("MyProcessor init");
  15. }
  16. // processor处理过程的回调, 如果需要生成代码, 就在这个方法中写. 这个demo暂时不演示代码生成.
  17. @Override
  18. public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
  19. System.out.println("process");
  20. return false;
  21. }
  22. @Override
  23. public Set<String> getSupportedAnnotationTypes() {
  24. // 在此处声明该processor支持的注解类型
  25. Set<String> set = new HashSet<>();
  26. set.add(MyAnnotation.class.getCanonicalName());
  27. return set;
  28. }
  29. @Override
  30. public SourceVersion getSupportedSourceVersion() {
  31. return SourceVersion.latestSupported();
  32. }
  33. }

那么我们如何把这个apt注册给javac呢? 我们将项目以常规的模式打包, 但是在META-INF目录中加入一个services文件夹, 在其中创建一个名为javax.annotation.processing.Processor的文件, 以文本将processor的完整名字写进去, 如果有多个processor, 换行即可.

javax.annotation.processing.Processor的内容:

  1. com.example.MyProcessor

最终jar包的结构:

  1. mp.jar // jar包名字随意起
  2. com
  3. example
  4. MyProcess.class
  5. MyAnnotation.class
  6. META-INF
  7. services
  8. javax.annotation.processing.Processor
  9. MANIFEST.MF

测试

测试的例子很简单:

  1. @MyAnnotation
  2. public class Sample {
  3. public static void main(String[] args) {
  4. System.out.printf("Hello, World!");
  5. }
  6. }

我们用javac编译这个文件

  1. $ javac -cp mp.jar Sample.java
  2. MyProcessor init
  3. process
  4. process

可以看到, 我们的Process已经生成了, 但是process过程输出了两次, 原因可以参考下图:

process的过程会进行两边, 我们代码生成的过程应该在第一遍, 因为第二次processor的过程应当负责做一些清理的工作, 某些打包工具可能不会编译在第二阶段生成的.java源文件.

  1. if (!roundEnv.processingOver()) { ... }

Java中的APT的工作过程的更多相关文章

  1. Java中的HashMap的工作原理是什么?

    问答题23 /120 Java中的HashMap的工作原理是什么? 参考答案 Java中的HashMap是以键值对(key-value)的形式存储元素的.HashMap需要一个hash函数,它使用ha ...

  2. java中可定制的序列化过程 writeObject与readObject

    来源于:[http://bluepopopo.iteye.com/blog/486548] 什么是writeObject 和readObject?可定制的序列化过程 这篇文章很直接,简单易懂.尝试着翻 ...

  3. Java中子类对象初始化的过程

    Java中的继承机制看似简单,实际上包含了很多细节.最近在刷题过程中屡屡跳坑,于是自己仔细再学习了一下Java中子类初始化的细节,与大家分享. class Father { Father(){}; } ...

  4. Java中对象方法的调用过程&动态绑定(Dynamic Binding)

    Java面向对象的最重要的一个特点就是多态, 而多态当中涉及到了一个重要的机制是动态绑定(Dynamic binding). 之前只有一个大概的概念, 没有深入去了解动态绑定的机理, 直到很多公司都问 ...

  5. Java中在时间戳计算的过程中遇到的数据溢出问题

    背景 今天在跑定时任务的过程中,发现有一个任务在设置数据的查询时间范围异常,出现了开始时间戳比结束时间戳大的奇怪现象,计算时间戳的代码大致如下. package com.lingyejun.authe ...

  6. java中new一个对象的执行过程及类的加载顺序

    1,new一个对象时代码的执行顺序 (1)加载父类(以下序号相同,表明初始化是按代码从上到下的顺序来的) 1.为父类的静态属性分配空间并赋于初值 1.执行父类静态初始化块; (2)加载子类 2.为子类 ...

  7. Java中传参的值传递和引用传递问题(转)

    今天遇到了一个java程序,需要用参数来返回值(虽然最后用另一种方法实现了),在网上看到这样一篇文章,很受启发. 本文章来自于http://hi.baidu.com/xzhilie/blog/item ...

  8. 夯实Java基础系列13:深入理解Java中的泛型

    目录 泛型概述 一个栗子 特性 泛型的使用方式 泛型类 泛型接口 泛型通配符 泛型方法 泛型方法的基本用法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型方法总结 泛型上下边界 泛型常见面试 ...

  9. [转]Java中子类调用父类构造方法的问题分析

    在Java中,子类的构造过程中,必须调用其父类的构造函数,是因为有继承关系存在时,子类要把父类的内容继承下来,通过什么手段做到的? 答案如下:    当你new一个子类对象的时候,必须首先要new一个 ...

随机推荐

  1. 九度oj 题目1060:完数VS盈数

    题目1060:完数VS盈数 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:6461 解决:2426 题目描述: 一个数如果恰好等于它的各因子(该数本身除外)子和,如:6=3+2+1.则称其 ...

  2. [luoguP1056] 排座椅(sort + 模拟)

    传送门 nc题,一直sort就过了 代码 #include <cstdio> #include <iostream> #include <algorithm> #d ...

  3. poj 1456

    #include<stdio.h> #include<string.h> #include<stdlib.h> #define N 10010 #define in ...

  4. bzoj 1430 小猴打架 prufer 性质

    小猴打架 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 709  Solved: 512[Submit][Status][Discuss] Descri ...

  5. JS基础:函数

    函数声明和函数表达式 在 JS 中定义函数的方式有两种:一种是函数声明,一种是函数表达式. 例如: //函数声明 function fun() { ... } //函数表达式 var f = func ...

  6. Ftp启动与关闭

    //启动 service vsftpd start //关闭 service vsftpd stop 查看进程 ps -ef | grep ftp root : ? :: /usr/sbin/vsft ...

  7. IntentService用于服务中开启子线程的自动关闭

    package com.pingyijinren.test; import android.app.IntentService; import android.content.Intent; impo ...

  8. 快速幂取模模板 && 51nod 1013 3的幂的和

    #include <iostream> #include <cstdio> #include <cmath> #include <vector> #in ...

  9. Android GIS开发系列-- 入门季(13)Gdal简单写个shp文件

    Gdal是用来读写栅格与矢量数据的,在Gdal官网,可以下载相关的资源进行平台的编译.其实Arcgis底层也是用Gdal来读取shp文件的,那在Android中可以直接读写shp文件吗,是可以的.这里 ...

  10. CentOS系统下Hadoop、Hbase、Zookeeper安装配置

    近期给一个项目搭建linux下的大数据处理环境,系统是CentOS 6.3.主要是配置JDK.安装Tomcat,Hadoop.HBase和Zookeeper软件.博主在Hadoop这方面也是新手.配置 ...