大家好,又见面了,我是你们的朋友全栈君。
在开始之前,我们首先申明一个非常重要的问题:我们并不讨论那些在运行时(Runtime)通过反射机制运行处理的注解,而是讨论在编译时(Compile time)处理的注解。注解处理器是一个在javac中的,用来编译时扫描和处理的注解的工具。可以为特定的注解,注册自己的注解处理器。
一个注解的注解处理器,以Java代码(或者编译过的字节码)作为输入,生成文件(通常是.java文件)作为输出。可以生成Java代码,这些生成的Java代码是在生成的.java文件中,所以不能修改已经存在的Java类,例如向已有的类中添加方法。这些生成的Java文件,会同其他普通的手动编写的Java源代码一样被javac编译。
虚处理器AbstractProcessor
我们首先看一下处理器的API。每一个处理器都是继承于AbstractProcessor,如下所示:
public class MyProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment env){ }
@Override
public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }
@Override
public Set<String> getSupportedAnnotationTypes() { }
@Override
public SourceVersion getSupportedSourceVersion() { }
}
- init(ProcessingEnvironment env): 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements,Types和Filer。
- process(Set<? extends TypeElement> annotations, RoundEnvironment env): 这相当于每个处理器的主函数main()。 在这里写扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让查询出包含特定注解的被注解元素。
- getSupportedAnnotationTypes(): 这里必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,在这里定义你的注解处理器注册到哪些注解上。
- getSupportedSourceVersion(): 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()。然而,如果有足够的理由只支持Java 6的话,也可以返回SourceVersion.RELEASE_6。推荐使用前者。
举一个简单例子
自动生成一个bean的结构文件
把
public class Student {
public String stu_name;
public String stu_id;
public int stu_age;
}
转换为
{class:"com.robert.processor.Student",
fields:
{
stu_name:"java.lang.String",
stu_id:"java.lang.String",
stu_age:"java.lang.Integer"
}
}
首先声明注解
package com.robert.processor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.FIELD, ElementType.TYPE })
@Retention(RetentionPolicy.CLASS)
public @interface Serialize {
}
将注解加到Student类上
@Serialize
public class Student
定义自己的解析器
package com.robert.processor;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
public class MyProcessor extends AbstractProcessor {
// 元素操作的辅助类
Elements elementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementUtils = processingEnv.getElementUtils();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 获得被该注解声明的元素
Set<? extends Element> elememts = roundEnv.getElementsAnnotatedWith(Serialize.class);
TypeElement classElement = null;// 声明类元素
List<VariableElement> fields = null;// 声明一个存放成员变量的列表
// 存放二者
Map<String, List<VariableElement>> maps = new HashMap<String, List<VariableElement>>();
// 遍历
for (Element ele : elememts) {
// 判断该元素是否为类
if (ele.getKind() == ElementKind.CLASS) {
classElement = (TypeElement) ele;
maps.put(classElement.getQualifiedName().toString(), fields = new ArrayList<VariableElement>());
} else if (ele.getKind() == ElementKind.FIELD) // 判断该元素是否为成员变量
{
VariableElement varELe = (VariableElement) ele;
// 获取该元素封装类型
TypeElement enclosingElement = (TypeElement) varELe.getEnclosingElement();
// 拿到key
String key = enclosingElement.getQualifiedName().toString();
fields = maps.get(key);
if (fields == null) {
maps.put(key, fields = new ArrayList<VariableElement>());
}
fields.add(varELe);
}
}
for (String key : maps.keySet()) {
if (maps.get(key).size() == 0) {
TypeElement typeElement = elementUtils.getTypeElement(key);
List<? extends Element> allMembers = elementUtils.getAllMembers(typeElement);
if (allMembers.size() > 0) {
maps.get(key).addAll(ElementFilter.fieldsIn(allMembers));
}
}
}
generateFile(maps);
return true;
}
private void generateFile(Map<String, List<VariableElement>> maps) {
File dir = new File(MyProcessor.class.getResource("/").getPath());
if (!dir.exists())
dir.mkdirs();
// 遍历map
for (String key : maps.keySet()) {
// 创建文件
File file = new File(dir, key.replaceAll("\\.", "_") + ".txt");
try {
/**
* 编写文件内容
*/
FileWriter fw = new FileWriter(file);
fw.append("{").append("class:").append("\"" + key + "\"").append(",\n ");
fw.append("fields:\n {\n");
List<VariableElement> fields = maps.get(key);
for (int i = 0; i < fields.size(); i++) {
VariableElement field = fields.get(i);
fw.append(" ").append(field.getSimpleName()).append(":")
.append("\"" + field.asType().toString() + "\"");
if (i < fields.size() - 1) {
fw.append(",");
fw.append("\n");
}
}
fw.append("\n }\n");
fw.append("}");
fw.flush();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> set = super.getSupportedAnnotationTypes();
if (set == null) {
set = new HashSet<>();
}
set.add("com.robert.processor.Serialize");
return set;
}
}
我们经常使用的ButterKnife这个框架就很好的使用了AbstractProcessor
Butter Knife 是 Android 视图字段和方法绑定,使用注解处理来生成样板代码。后面做详细说明。
欢迎扫描二维码,关注个人公众号
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/140841.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...