Element
Element在中文的API文档中的解释为
javax.lang.model.element 表示一个程序元素,比如包、类或者方法。每个元素都表示一个静态的语言级构造(不表示虚拟机的运行时构造)。
简单的讲我们可以将一个类看成类似HTML文档样,一个类是最外层的根节点,类变量和实例变量是根节点下的一个子节点,方法是另一个根节点,而方法中的变量又可以看成这个方法的子节点,依次类推下去。通过面向对象的方法我们将这个节点抽象出一个Element类,这个类就提取出变量节点或者方法节点的一些相同的属性,Element就作为一个顶级的父类。
基于上面的思想我们可以看一看Element的继承关系
- TypeElement
我们可以简单的将Type类比为Class ,因此我们可以将TypeElement看做成类节点,这个类型的Element中包含该类节点的一些必要的信息,比如类的全限定名,该类的父类等信息。通过该类的一些方法我们也可以推断出TypeElement所代表的含义。
| 方法摘要 |
| List<? extends TypeMirror>
| **getInterfaces**()
返回直接由此类实现或直接由此接口扩展的接口类型。 |
| NestingKind
| **getNestingKind**()
返回此类型元素的嵌套种类 (nesting kind)。 |
| Name
| **getQualifiedName**()
返回此类型元素的完全限定名称。 |
| TypeMirror
| **getSuperclass**()
返回此类型元素的直接超类。 |
| List<? extends TypeParameterElement>
| **getTypeParameters**()
按照声明顺序返回此类型元素的形式类型参数。 |
- VariableElement
VariableElement 在API中的解释为表示一个字段、enum
常量、方法或构造方法参数、局部变量或异常参数。我们可以简单的将VariableElement对应于一个Field。VariableElement提供的方法有
| Object
| **getConstantValue**()
如果此变量是一个被初始化为编译时常量的 static final
字段,则返回此变量的值。 |
- ExecutableElement
可执行的Element 对应于一个可以执行的方法,对应于API文档中的解释是表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注释类型元素。他提供的方法有
| AnnotationValue
| **getDefaultValue**()
如果此 executable 是一个注释类型元素,则返回默认值。 |
| List<? extends VariableElement>
| **getParameters**()
返回此 executable 的形参。 |
| TypeMirror
| **getReturnType**()
返回此 executable 的返回类型。 |
| List<? extends TypeMirror>
| **getThrownTypes**()
按声明顺序返回此方法或构造方法的 throws
子句中所列出的异常和其他 throwable。 |
| List<? extends TypeParameterElement>
| **getTypeParameters**()
按照声明顺序返回此 executable 的形式类型参数。 |
| boolean
| **isVarArgs**()
如果此方法或构造方法接受可变数量的参数,则返回 true
,否则返回 false
。 |
public class NameCheckProcesser extends AbstractProcessor {
private Filer filerUtils; // 文件写入
private Elements elementUtils; // 操作Element 的工具类
private Messager messagerUtils; // Log 日志
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filerUtils = processingEnv.getFiler();
elementUtils = processingEnv.getElementUtils();
messagerUtils = processingEnv.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
// messagerUtils.printMessage(Kind.WARNING, "processor start-------");
// roundEnv.getRootElements() 貌似只会返回有该注解的类
// Set<? extends Element> rootElements = roundEnv.getRootElements();
// 返回有 NameCheck 注解的Element
Set<? extends Element> elementsAnnotatedWith = roundEnv
.getElementsAnnotatedWith(NameCheck.class);
for (Element element : elementsAnnotatedWith) {
messagerUtils.printMessage(Kind.WARNING, "processor start-------"
+ element.getSimpleName());
// element.getKind() 返回该Element的类型,常见的有
// 1. ElementKind.CLASS 一个类
// 2.ElementKind.FIELD 一个字段
// 3. ElementKind.METHOD 一个方法
if (element.getKind() == ElementKind.CLASS) {
// class 类型
messagerUtils.printMessage(Kind.WARNING, " class 类型 same : "
+ ((TypeElement) (element)).getSimpleName()
+ " / qname:"
+ ((TypeElement) (element)).getQualifiedName()
.toString());
List<? extends Element> enclosedElements = element
.getEnclosedElements();
for (Element element2 : enclosedElements) {
messagerUtils.printMessage(Kind.WARNING, "class 分装的类型:"
+ element2.getSimpleName());
}
} else if (element.getKind() == ElementKind.FIELD) {
// 字段类型
messagerUtils.printMessage(Kind.WARNING, "字段类型-------"
+ element.getSimpleName());
// 封装我的类型
TypeElement enclosingElement = (TypeElement) element
.getEnclosingElement();
messagerUtils.printMessage(Kind.WARNING,
"getEnclosingElement :"
+ enclosingElement.getQualifiedName()
.toString());
} else if (element.getKind() == ElementKind.METHOD) {
// 方法类型
messagerUtils.printMessage(Kind.WARNING, " 方法类型 same : "
+ ((ExecutableElement) (element)).getSimpleName());
ExecutableElement element2 = (ExecutableElement) element;
// 如果是方法则得到方法的参数,方法的返回值
List<? extends VariableElement> parameters = element2
.getParameters();
List<? extends TypeParameterElement> typeParameters = element2
.getTypeParameters();
/*
* messagerUtils.printMessage( Kind.WARNING, "size:" +
* parameters.size() + ":" + typeParameters.size());
*/
for (int i = 0; i < parameters.size(); i++) {
messagerUtils.printMessage(Kind.WARNING, "方法的参数名称是 : "
+ parameters.get(i).getSimpleName() + "/ 参数的类型是:"
+ parameters.get(i).asType().toString());
}
TypeMirror returnType = element2.getReturnType();
messagerUtils.printMessage(Kind.WARNING,
"返回值的类是" + returnType.toString());
}
}
return false;
}
}
上面的代码主要分为四个部分
- 在init()函数中完成一些变量的初始化的工作
filerUtils = processingEnv.getFiler();
elementUtils = processingEnv.getElementUtils();
messagerUtils = processingEnv.getMessager();
-在Processor()函数中进行了三部分的判断
- TypeElement
当一个Element 是TypeElement 时我们主要关心他的全限定名继承的类或实现的接口等信息,这些我们都可以通过对于的方法获取到。
- VariableElement
当一个Element是VariableElement是,我们也可以通过相应的方法获取相应的信息
- ExecutableElement
当一个Element对应的是一个方法时,我们关心的是方法的参数和参数的类型,我们可以通过以下的方法获取
// 如果是方法则得到方法的参数
List<? extends VariableElement> parameters = element2
.getParameters();
for (int i = 0; i < parameters.size(); i++) {
String paramterType =parameters.get(i).asType().toString(); // 返回的形式例如 java.lang.String messagerUtils.printMessage(Kind.WARNING, "方法的参数名称是 : "
+ parameters.get(i).getSimpleName() + "/ 参数的类型是:"
+paramterType);
}
将上面的代码运用在Test.java上
package cn.imzhushang.jvm.annotation;
@NameCheck(nameChenckTest = 1)
public class Test {
@NameCheck(nameChenckTest = 1)
private int Aaa;
@NameCheck(nameChenckTest = 1)
public Long TestC(int arg1, Long l) {
return 1L;
}
}
执行的结果是
关于在自定义注解器运行的问题
1.在Java程序中
完成我们的注解处理器的最后一个步骤是打包并注册。这样Java编译器或者其它工具才能够找到这个注解处理器。
注册处理器最简单的方法就是利用标准的Java服务机制:
* 将你的注解处理器打包进一个Jar包。
* 在这个Jar包中要包含一个文件夹META-INF/services。
* 在这个文件夹中要包含一个名为javax.annotation.processing.Processor的文件。
* 在这个文件里面将Jar包中所有注解处理器的完整类名写进去,每个类名一行。
Java编译器或者其它工具将会在所有声明的classpath路径下查找这个文件,并用于注册处理器。
然后执行命令
javac -cp testannotation.jar Test.java