基本介绍
element
指的是一系列与之相关的接口
集合,它们位于javax.lang.model.element
包下面
以下是官方文档对element的定义
Represents a program element such as a package, class, or method. Each element represents a static, language-level construct (and not, for example, a runtime construct of the virtual machine)
即element是代表程序的一个元素,这个元素可以是:包、类/接口、属性变量、方法/方法形参、泛型参数。element是java-apt(编译时注解处理器)
技术的基础,因此如果要编写此类框架,熟悉element是必须的。
element及其子接口
- 各种element所代表的元素类型
通过上图可以看到element元素及其子接口所指代的元素,有点类似Type类型,Type的用法可以参考另一篇博客java Type详解,那么element接口族和Type接口族之间有什么区别呢?element所代表的元素只在编译期可见,用于保存元素在编译期的各种状态,而Type所代表的元素是运行期可见,用于保存元素在编译期的各种状态
简单的例子
要在编译期得到工程的
element
信息,我们需要采用apt技术(以Android开发为例,在Android studio中):
- 创建注解
创建注解(在单独的一个java module中)
//注解的声明周期声明为source,意即只在编译源文件的过程中有效
@Retention(RetentionPolicy.SOURCE)
//通常一个注解只注释类,方法,或者变量,这样可以使结构更清晰。这里为了演示不同的element,把这个注解的使用对象定义为类,变量,参数,方法
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.PARAMETER,ElementType.METHOD})
public @interface AAAAA {
String value();
}
- 在目标类中使用注解(主module)
package com.lu.aptdemo;
import com.lu.annotation.AAAAA;
/**
* @Author: luqihua
* @Time: 2018/6/20
* @Description: Test
*/
@AAAAA("this is a class")
public class Test<T> {
@AAAAA("this is a field")
String hello;
@AAAAA("this is a method")
public String say(@AAAAA("this is a parameter") String arg1) {
return "hello world";
}
}
- 创建处理器Processor(单独的一个java-module),需要用到下面三个库
implementation 'com.google.auto:auto-common:0.10'
implementation 'com.google.auto.service:auto-service:1.0-rc4'
implementation 'com.squareup:javapoet:1.11.1'
@AutoService(Processor.class)
public class TestProcessor extends AbstractProcessor {
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> set = new HashSet<>();
set.add(AAAAA.class.getCanonicalName());
return set;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//扫描整个工程 找出含有AAAAA注解的元素(包括类,)
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(AAAAA.class);
//由于编译器的输出无法打印到控制台,因此这里借助javapoet库把需要输出的信息写入到一个新的类
//这个是我封装的一个简单的工具
ProcessorTool.Builder builder = new ProcessorTool.Builder().setProcessingEnv(processingEnv);
for (Element element : elements) {
AAAAA aaaaa = element.getAnnotation(AAAAA.class);
if (element instanceof TypeElement) {
builder.addArgs(" TypeElement: " + aaaaa.value());
/*===============打印包信息=================*/
builder.addArgs("=============================打印包信息================================");
PackageElement packageElement = processingEnv.getElementUtils().getPackageOf(element);
builder.addArgs("packageElement: " + packageElement.getSimpleName().toString());
builder.addArgs("packageElement: " + packageElement.getQualifiedName());
builder.addArgs("=============================打印泛型信息================================");
List<? extends TypeParameterElement> typeParameters = ((TypeElement) element).getTypeParameters();
for (TypeParameterElement typeParameter : typeParameters) {
builder.addArgs(typeParameter.getSimpleName().toString());
}
builder.addArgs("=============================================================");
} else if (element instanceof ExecutableElement) {
builder.addArgs("ExecutableElement: " + aaaaa.value());
} else if (element instanceof VariableElement) {
builder.addArgs(" VariableElement: " + aaaaa.value());
}
}
builder.build().printLog();
return true;
}
}
//==========================
在主工程module的build->generate->source->apt->test->Logger.java
查看输出
package test;
import java.lang.String;
class Logger {
void test() {
String arg0=" TypeElement: this is a class";
String arg1="=============================打印包信息================================";
String arg2="packageElement.getSimpleName(): aptdemo";
String arg3="packageElement.getQualifiedName(): com.lu.aptdemo";
String arg4="=============================打印泛型信息================================";
String arg5="T";
String arg6="=============================================================";
String arg7=" VariableElement: this is a field";
String arg8="ExecutableElement: this is a method";
String arg9=" VariableElement: this is a parameter";
}
}
ProcessorTool类
public class ProcessorTool {
private ProcessingEnvironment processingEnv;
private List<String> args = new ArrayList<>();
public ProcessorTool(ProcessingEnvironment env) {
this.processingEnv = env;
}
public ProcessorTool addArgs(String arg) {
args.add(arg);
return this;
}
/**
* 用于打印log到文件
*/
public void printLog() {
TypeSpec.Builder builder = TypeSpec.classBuilder("Logger");
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("test");
int len = args.size();
for (int i = 0; i < len; i++) {
String arg = args.get(i);
methodBuilder.addStatement("$T arg" + i + "=$S", String.class, arg);
}
builder.addMethod(methodBuilder.build());
JavaFile javaFile = JavaFile.builder("test", builder.build())
.build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
}
element 接口方法
在
type
的介绍中我们可以通过type
的一系列方法获得元素信息,尤其是通过type我们可以在运行期间获取注解信息,再通过反射
和动态代理
实现AOP设计,而element
类既然在编译期可见,自然我们想到了在编译期获取元素的各类信息,结合apt
技术实现动态的代码生成。
- <R,P> R
accept(ElementVisitor<R,P> v, P
接收一个ElementVisitor类,它的作用类似于一个
if(element instanceof ExecutableElement)
则调用visitExecutable(ExecutableElement executableElement, Void aVoid)
;两个泛型一般写Void就行,如果需要接收accept方法的返回值,则根据返回值的类型定义R,P基本上是用不到的,写Void就行
示例代码
element.accept(new SimpleElementVisitor7<Void, Void>() {
@Override
public Void visitType(TypeElement typeElement, Void aVoid) {
return super.visitType(typeElement, aVoid);
//这是一个TypeElement
}
@Override
public Void visitExecutable(ExecutableElement executableElement, Void aVoid) {
return super.visitExecutable(executableElement, aVoid);
//这是一个executableElement
}
@Override
public Void visitPackage(PackageElement packageElement, Void aVoid) {
return super.visitPackage(packageElement, aVoid);
//这是一个PackageElement
}
}, null);
- TypeMirror
asType()
返回一个
TypeMirror
是元素的类型信息,包括包名,类(或方法,或参数)名/类型,在生成动态代码的时候,我们往往需要知道变量/方法参数的类型,以便写入正确的类型声明
示例代码
for (Element element : elements) {
if (element instanceof TypeElement) {
TypeName typeName = ClassName.get(element.asType());
TypeSpec typeSpec = TypeSpec.classBuilder("GenerateTest")
.addField(typeName, "test")
//添加泛型信息
.addTypeVariable(TypeVariableName.get(((TypeElement) element).getTypeParameters().get(0)))
.build();
try {
JavaFile.builder("com.test", typeSpec)
.build()
.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
}
//===================生成代码
package com.test;
//通过ClassName包装之后,在生成对应代码中会自动导入类型的包
import com.lu.aptdemo.Test;
class GenerateTest<T> {
Test<T> test;
}
- <A extends Annotation> A
getAnnotation(Class<A> annotationType)
根据传入的注解类型获取该元素上的注解
- List<? extends AnnotationMirror>
getAnnotationMirrors()
获取该元素上的注解的类型信息,
AnnotationMirror
类似于TypeMirror
- List<? extends Element>
getEnclosedElements()
返回该元素直接包含的子元素,通常对一个
PackageElement
而言,它可以包含TypeElement
;对于一个TypeElement
而言,它可能包含属性VariableElement
,方法ExecutableElement
,
示例代码
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(AAAAA.class);
//这个是我封装的一个简单的工具
ProcessorTool tool = new ProcessorTool(processingEnv);
for (Element element : elements) {
if (element instanceof TypeElement) {
for (Element element1 : element.getEnclosedElements()) {
tool.addArgs(element1.getSimpleName().toString());
}
}
}
tool.printLog();
//输出 Test类包含构造方法,属性Hello 方法say()
class Logger {
void test() {
String arg0="<init>";
String arg1="hello";
String arg2="say";
}
}
- Element
getEnclosingElement()
返回包含该element的父element,与上一个方法相反,
VariableElement
,方法ExecutableElement
的父级是TypeElemnt
,而TypeElemnt
的父级是PackageElment
示例代码
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(AAAAA.class);
//这个是我封装的一个简单的工具
ProcessorTool tool = new ProcessorTool(processingEnv);
for (Element element : elements) {
tool.addArgs(element.getSimpleName().toString()+".getEnclosingElement(): "+element.getEnclosingElement().getSimpleName().toString());
}
tool.printLog();
//输出代码
class Logger {
void test() {
String arg0="Test.getEnclosingElement(): aptdemo";
String arg1="hello.getEnclosingElement(): Test";
String arg2="say.getEnclosingElement(): Test";
String arg3="arg1.getEnclosingElement(): say";
}
}
- ElementKind
getKind()
返回element的类型,判断是哪种element
- Set<Modifier>
getModifiers()
获取修饰关键字,入public static final等关键字
- Name
getSimpleName()
获取名字,不带包名
- Name
getQualifiedName()
这个方法是element的子接口所带的方法,element本身并不指代具体的元素,因此没有改方法。获取全限定名,如果是类的话,包含完整的报名路径
ExecutableElement
- List<? extends VariableElement>
getParameters()
用于获取方法的参数元素,每个元素是一个
VariableElement
- TypeMirror
getReturnType()
获取方法元素的返回值,返回衣蛾
TypeMirror
表示
VariableElement
- Object
getConstantValue()
如果属性变量被
final
修饰,则可以使用该方法获取它的值