JAVA 注解 processor_注解处理器(Annotation Processor)简析
概念
注解处理器(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)简析相关推荐
- Kotlin版注解处理器Annotation Processor
文章目录 什么是注解 创建注解 什么是注解处理器 编写注解处理器 使用注解处理器生成代码 在Android项目中使用注解处理器 调试注解处理器 在处理器中记录日志和处理错误 分析种类.数据类型和可见性 ...
- java开发中Bean的Builder模式简析
1.传统模式,通过构造器new出来 大量重载,添加属性,则需要添加新的构造方法,不利于拓展 public class Animal {private String eyes;private Strin ...
- Java中的注解(Annotation)处理器解析
Java中的注解(Annotation)是一个很神奇的东西,特别现在有很多Android库都是使用注解的方式来实现的.一直想详细了解一下其中的原理.很有幸阅读到一篇详细解释编写注解处理器的文章.本文的 ...
- java编译时注解_简单介绍 Java 中的编译时注解
1. 前言 上一篇 主要介绍了什么是 注解 (Annotation) 以及如何读取 运行时注解 中的数据, 同时用注解实现了简单的 ORM 功能. 这次介绍另一部分: 如何读取 编译时注解 ( Ret ...
- Java编译时注解处理器(Annotation Processor)详解
上篇文章我们使用注解+反射实现了一个仿ButterKnife功能的示例.考虑到反射是在运行时完成的,多少会影响程序性能.因此,ButterKnife本身并非基于注解+反射来实现的,而是用Annotat ...
- 秒懂Android注解处理器(Android Annotation Processor)
[版权申明]非商业目的可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/90734159 出自:shusheng007 文章目 ...
- Java注解 编译_Java注解(3)-注解处理器(编译期|RetentionPolicy.SOURCE)
注解的处理除了可以在运行时通过反射机制处理外,还可以在编译期进行处理.在编译期处理注解时,会处理到不再产生新的源文件为止,之后再对所有源文件进行编译. Java5中提供了apt工具来进行编译期的注解处 ...
- 【java】详解java中的注解(Annotation)
目录结构: contents structure [+] 什么是注解 为什么要使用注解 基本语法 4种基本元注解 重复注解 使用注解 运行时处理的注解 编译时处理的注解 1.什么是注解 用一个词就可以 ...
- Java语言使用注解处理器生成代码——第二部分:注解处理器
原文作者:deors 原文地址:https://deors.wordpress.com/2011/10/08/annotation-processors/ 译文作者:Jianan - qinxiand ...
最新文章
- 2015年9月13日-9月15日课程作业(sed、awk)
- 软件开发的微信公众号分享
- Socket.io 深入理解
- QT学习笔记(四):Qt5+MSVC编译 中文字符显示乱码问题解决
- [流体输配管网] 使用 Matlab 绘制莫迪图
- 回公司无聊和小冰聊天,很好奇她到底怎么想的
- vmware上的ubuntu与window共享folder(shared folder disabled.)
- CentOS7和win7双系统启动项
- 唐宇迪学习笔记2:Python数据分析处理库——pandas
- mSystem:鸟枪法宏基因组测序之外我们还能做什么
- es创建索引和mapping
- php发送指令给易语言,易语言发送信息代码数字指令编程整理
- 元分析 | 精神分裂症患者认知功能的脑结构相关
- [Practical.Vim(2012.9)].Drew.Neil.Tip10学习摘要
- 电竞英雄联盟数据API接口 - 【近期赛事列表】API调用示例代码
- 读《Linux应急响应》笔记(未完待续)
- Java SSM毕设 公寓宿舍后勤管理系统(含源码+论文)
- 我的世界服务器修改速度,我的世界运算变速 TickrateChanger mod
- PHP 设计模式之最全面,最简单的讲解
- IDEA无法加载插件