概念

注解处理器(Annotation Processor)是javac内置的一个用于编译时扫描和处理注解(Annotation)的工具。简单的说,在源代码编译阶段,通过注解处理器,我们可以获取源文件内注解(Annotation)相关内容。

用途

由于注解处理器可以在程序编译阶段工作,所以我们可以在编译期间通过注解处理器进行我们需要的操作。比较常用的用法就是在编译期间获取相关注解数据,然后动态生成.java源文件(让机器帮我们写代码),通常是自动产生一些有规律性的重复代码,解决了手工编写重复代码的问题,大大提升编码效率。

例子

Annotation Processor实质原理

** 编译期间根据注解(Annotation)获取相关数据 **

既然Annotation Processor是为了在编译期间获取注解(Annotation)相关内容,那么,具体的操作步骤要如何做呢:

Android Studio创建一个java library

自定义一个注解(Annotation),用于存储元数据

@Retention(RetentionPolicy.SOURCE)

@Target(ElementType.FIELD)

public @interface BindView {

int value() default -1;

}

创建一个自定义Annotation Processor继承于AbstractProcessor

package com.example;

@AutoService(Processor.class)

public class MyProcessor extends AbstractProcessor {

@Override

public synchronized void init(ProcessingEnvironment env){

}

@Override

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

@Override

public Set getSupportedAnnotationTypes() {

}

@Override

public SourceVersion getSupportedSourceVersion() {

}

}

@AutoService(Processor.class) :向javac注册我们这个自定义的注解处理器,这样,在javac编译时,才会调用到我们这个自定义的注解处理器方法。

AutoService这里主要是用来生成

META-INF/services/javax.annotation.processing.Processor文件的。如果不加上这个注解,那么,你需要自己进行手动配置进行注册,具体手动注册方法如下:

1.创建一个

META-INF/services/javax.annotation.processing.Processor文件,

其内容是一系列的自定义注解处理器完整有效类名集合,以换行切割:

com.example.MyProcessor

com.foo.OtherProcessor

net.blabla.SpecialProcessor

2.将自定义注解处理器和

META-INF/services/javax.annotation.processing.Processor打包成一个.jar文件。所以其目录结构大概如下所示:

MyProcessor.jar

- com

- example

- MyProcessor.class

- META-INF

- services

- javax.annotation.processing.Processor

*** 建议直接采用@AutoService(Processor.class)进行自定义注解处理器注册,简洁方便 ***

init(ProcessingEnvironment env):每个Annotation Processor必须***

有一个空的构造函数 *。编译期间,init()会自动被注解处理工具调用,并传入ProcessingEnviroment参数,通过该参数可以获取到很多有用的工具类: Elements , Types , Filer **等等

process(Set extends TypeElement> annoations, RoundEnvironment roundEnv):Annotation Processor扫描出的结果会存储进roundEnv中,可以在这里获取到注解内容,编写你的操作逻辑。注意,process()函数中不能直接进行异常抛出,否则的话,运行Annotation Processor的进程会异常崩溃,然后弹出一大堆让人捉摸不清的堆栈调用日志显示.

getSupportedAnnotationTypes(): 该函数用于指定该自定义注解处理器(Annotation Processor)是注册给哪些注解的(Annotation),注解(Annotation)指定必须是完整的包名+类名(eg:com.example.MyAnnotation)

getSupportedSourceVersion():用于指定你的java版本,一般返回:SourceVersion.latestSupported()。当然,你也可以指定具体java版本:

return SourceVersion.RELEASE_7;

经过前面3个步骤后,其实就已经算完成了自定义Annotation Processor。后面要做的就是在源码里面,在需要的地方写上我们自定义的注解就行了。

Demo

牢记Annotation Process的实质用处就是在编译时通过注解获取相关数据,

那么,在这个Demo里面,我们就直接在编译时打印出我们注解的数据的成员变量名,成员变量类,包装类类名,包名和注解元数据进行显示,然后将这些信息写入到一个.java文件中,这里我就简单的直接输出这些信息进行显示。

按照上面自定义注解处理的方法,我们操作如下:

创建一个java library,其gradle配置如下:

apply plugin: 'java'

targetCompatibility = '1.7'

sourceCompatibility = '1.7'

dependencies {

compile fileTree(include: ['*.jar'], dir: 'libs')

compile 'com.google.auto.service:auto-service:1.0-rc3'

}

自定义一个注解(Annotation),用于存储元数据

@Retention(RetentionPolicy.SOURCE)

@Target(ElementType.FIELD)

public @interface BindView {

int value() default -1;

}

创建一个自定义Annotation Processor继承于AbstractProcessor

@AutoService(Processor.class)

public class MyAnnotationProcessor extends AbstractProcessor {

private Filer mFiler;

private Messager mMessager;

private Elements mElementUtils;

@Override

public synchronized void init(ProcessingEnvironment processingEnvironment) {

super.init(processingEnvironment);

mFiler = processingEnvironment.getFiler();

mMessager = processingEnvironment.getMessager();

mElementUtils = processingEnvironment.getElementUtils();

}

@Override

public Set getSupportedAnnotationTypes() {

Set annotations = new LinkedHashSet<>();

annotations.add(BindView.class.getCanonicalName());

return annotations;

}

@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.latestSupported();

}

@Override

public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {

Set extends Element> bindViewElements = roundEnvironment.getElementsAnnotatedWith(BindView.class);

for (Element element : bindViewElements) {

//1.获取包名

PackageElement packageElement = mElementUtils.getPackageOf(element);

String pkName = packageElement.getQualifiedName().toString();

note(String.format("package = %s", pkName));

//2.获取包装类类型

TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

String enclosingName = enclosingElement.getQualifiedName().toString();

note(String.format("enclosindClass = %s", enclosingElement));

//因为BindView只作用于filed,所以这里可直接进行强转

VariableElement bindViewElement = (VariableElement) element;

//3.获取注解的成员变量名

String bindViewFiledName = bindViewElement.getSimpleName().toString();

//3.获取注解的成员变量类型

String bindViewFiledClassType = bindViewElement.asType().toString();

//4.获取注解元数据

BindView bindView = element.getAnnotation(BindView.class);

int id = bindView.value();

note(String.format("%s %s = %d", bindViewFiledClassType, bindViewFiledName, id));

//4.生成文件

createFile(enclosingElement, bindViewFiledClassType, bindViewFiledName, id);

return true;

}

return false;

}

private void createFile(TypeElement enclosingElement, String bindViewFiledClassType, String bindViewFiledName, int id) {

String pkName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();

try {

JavaFileObject jfo = mFiler.createSourceFile(pkName + ".ViewBinding", new Element[]{});

Writer writer = jfo.openWriter();

writer.write(brewCode(pkName, bindViewFiledClassType, bindViewFiledName, id));

writer.flush();

writer.close();

} catch (IOException e) {

e.printStackTrace();

}

}

private String brewCode(String pkName, String bindViewFiledClassType, String bindViewFiledName, int id) {

StringBuilder builder = new StringBuilder();

builder.append("package " + pkName + ";\n\n");

builder.append("//Auto generated by apt,do not modify!!\n\n");

builder.append("public class ViewBinding { \n\n");

builder.append("public static void main(String[] args){ \n");

String info = String.format("%s %s = %d", bindViewFiledClassType, bindViewFiledName, id);

builder.append("System.out.println(\"" + info + "\");\n");

builder.append("}\n");

builder.append("}");

return builder.toString();

}

private void note(String msg) {

mMessager.printMessage(Diagnostic.Kind.NOTE, msg);

}

private void note(String format, Object... args) {

mMessager.printMessage(Diagnostic.Kind.NOTE, String.format(format, args));

}

}

** 借助Messager,我们可以在编译时输出日志. **

使用注解,我们在Android工程中创建几个测试类,然后进行注解,如下所示:

public class MainActivity extends AppCompatActivity {

@BindView(R.id.tv)

TextView tv;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

}

rebuild一下,可以在Gradle Console窗口中看到打印结果:

result

可以看到,我们成功的在编译期间获取了我们注解的相关数据。只要拿到了数据,那么你自己想干嘛就自己去弄吧 _

最后,我们根据注解获取到的数据还生成了一个java文件,其生成路径:app\build\generated\source\apt\debug\com\yn\annotationprocessdemo\ViewBinding.java

具体内容如下:

package com.yn.annotationprocessdemo;

//Auto generated by apt,do not modify!!

public class ViewBinding {

public static void main(String[] args) {

System.out.println("android.widget.TextView tv = 2131427422");

}

}

附录:

@AutoService引入:

compile 'com.google.auto.service:auto-service:1.0-rc3'

app的gralde配置:

apply plugin: 'com.android.application'

android {

compileSdkVersion 24

buildToolsVersion "24.0.0"

defaultConfig {

applicationId "com.example.annotationprocessor"

minSdkVersion 15

targetSdkVersion 24

versionCode 1

versionName "1.0"

}

buildTypes {

release {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

}

compileOptions {

sourceCompatibility JavaVersion.VERSION_1_7

targetCompatibility JavaVersion.VERSION_1_7

}

//解决duplicate问题

packagingOptions {

exclude 'META-INF/services/javax.annotation.processing.Processor'

}

}

dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])

testCompile 'junit:junit:4.12'

compile 'com.android.support:appcompat-v7:24.0.0'

compile project(path: ':annotationprocessor')

}

JAVA 注解 processor_注解处理器(Annotation Processor)简析相关推荐

  1. Kotlin版注解处理器Annotation Processor

    文章目录 什么是注解 创建注解 什么是注解处理器 编写注解处理器 使用注解处理器生成代码 在Android项目中使用注解处理器 调试注解处理器 在处理器中记录日志和处理错误 分析种类.数据类型和可见性 ...

  2. java开发中Bean的Builder模式简析

    1.传统模式,通过构造器new出来 大量重载,添加属性,则需要添加新的构造方法,不利于拓展 public class Animal {private String eyes;private Strin ...

  3. Java中的注解(Annotation)处理器解析

    Java中的注解(Annotation)是一个很神奇的东西,特别现在有很多Android库都是使用注解的方式来实现的.一直想详细了解一下其中的原理.很有幸阅读到一篇详细解释编写注解处理器的文章.本文的 ...

  4. java编译时注解_简单介绍 Java 中的编译时注解

    1. 前言 上一篇 主要介绍了什么是 注解 (Annotation) 以及如何读取 运行时注解 中的数据, 同时用注解实现了简单的 ORM 功能. 这次介绍另一部分: 如何读取 编译时注解 ( Ret ...

  5. Java编译时注解处理器(Annotation Processor)详解

    上篇文章我们使用注解+反射实现了一个仿ButterKnife功能的示例.考虑到反射是在运行时完成的,多少会影响程序性能.因此,ButterKnife本身并非基于注解+反射来实现的,而是用Annotat ...

  6. 秒懂Android注解处理器(Android Annotation Processor)

    [版权申明]非商业目的可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/90734159 出自:shusheng007 文章目 ...

  7. Java注解 编译_Java注解(3)-注解处理器(编译期|RetentionPolicy.SOURCE)

    注解的处理除了可以在运行时通过反射机制处理外,还可以在编译期进行处理.在编译期处理注解时,会处理到不再产生新的源文件为止,之后再对所有源文件进行编译. Java5中提供了apt工具来进行编译期的注解处 ...

  8. 【java】详解java中的注解(Annotation)

    目录结构: contents structure [+] 什么是注解 为什么要使用注解 基本语法 4种基本元注解 重复注解 使用注解 运行时处理的注解 编译时处理的注解 1.什么是注解 用一个词就可以 ...

  9. Java语言使用注解处理器生成代码——第二部分:注解处理器

    原文作者:deors 原文地址:https://deors.wordpress.com/2011/10/08/annotation-processors/ 译文作者:Jianan - qinxiand ...

最新文章

  1. 2015年9月13日-9月15日课程作业(sed、awk)
  2. 软件开发的微信公众号分享
  3. Socket.io 深入理解
  4. QT学习笔记(四):Qt5+MSVC编译 中文字符显示乱码问题解决
  5. [流体输配管网] 使用 Matlab 绘制莫迪图
  6. 回公司无聊和小冰聊天,很好奇她到底怎么想的
  7. vmware上的ubuntu与window共享folder(shared folder disabled.)
  8. CentOS7和win7双系统启动项
  9. 唐宇迪学习笔记2:Python数据分析处理库——pandas
  10. mSystem:鸟枪法宏基因组测序之外我们还能做什么
  11. es创建索引和mapping
  12. php发送指令给易语言,易语言发送信息代码数字指令编程整理
  13. 元分析 | 精神分裂症患者认知功能的脑结构相关
  14. [Practical.Vim(2012.9)].Drew.Neil.Tip10学习摘要
  15. 电竞英雄联盟数据API接口 - 【近期赛事列表】API调用示例代码
  16. 读《Linux应急响应》笔记(未完待续)
  17. Java SSM毕设 公寓宿舍后勤管理系统(含源码+论文)
  18. 我的世界服务器修改速度,我的世界运算变速 TickrateChanger mod
  19. PHP 设计模式之最全面,最简单的讲解
  20. IDEA无法加载插件

热门文章

  1. Cryengine渲染引擎剖析
  2. 第八课:不一样的链表 linux链表设计哲学 5星级教程
  3. 原生App项目集成flutter混合开发详细指南
  4. 51CTO安全技术沙龙之“两小时成为反挂马达人”
  5. 长沙理工大学第十二届ACM大赛-重现赛I 主持人的烦恼 (sort)
  6. c语言字母的范围,C语言变量类型及其表示范围
  7. 世界十大高端接链环品牌排名,第一名你绝对想不到!
  8. if函数和and函数嵌套_在Excel中嵌套IF函数
  9. python中字典删除元素
  10. 手机音质变差_为什么手机听音乐音质很差