编译时注解:AbstractProcessor javapoe自动生成代码
前沿:AbstractProcessor介绍
AbstractProcessor,是一个抽象类,该类实现了接口Processor。
抽象类AbstractProcessor以及接口Processor都是位于包javax.annotation.processing中。该接口中定义的所有类、接口都是与实现注解处理器相关的。
现行比较流行的是注解式框架,AOP解耦:获取控件,检查权限,检查登陆状态等等许多操作,类似于市面上的ButterKnife,Retrofit,Dragger,EventBus等等都选择使用注解来配置。注解又分为两种类型,一种是运行时注解,另一种是编译时注解。运行时注解因为性能原因一般不会在项目中过多的使用。比较有代表的注解控件框架xutils和ButterKnife,xutils使用的是运行时注解,ButterKnife使用的编译时注解。
RetentionPolicy源码见:sdk文档
- RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃,是3种生命周期中最短的注解;
- 编译时注解(RetentionPolicy.CLASS),作用域class字节码上,生命周期只有在编译器间有效。
- RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, //在原文件中有效,被编译器丢弃。
CLASS, //在class文件有效,可能会被虚拟机忽略。
RUNTIME; //在运行时有效。
private RetentionPolicy() {
}
}
本章内容不主要介绍编译时注解,这个仿照ButterKnife挺详细 :编译时注解
本章主要介绍,在编译时注解时怎么用代码写代码,没错你没看错。
在这里用到了开源项目: javapoet
javapoet用法:
常用类:
- MethodSpec 代表一个构造函数或方法声明。
- TypeSpec 代表一个类,接口,或者枚举声明。
- FieldSpec代表一个成员变量,一个字段声明。
- JavaFile包含一个*类的Java文件。
占位符: - $S for Strings
- $T for Types
- $N for Names(我们自己生成的方法名或者变量名等等)
生成文件:
第一种生成方法:
private void generateHelloworld() throws IOException{
//main代表方法名
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC,Modifier.STATIC)//Modifier 修饰的关键字
.addParameter(String[].class, "args") //添加string[]类型的名为args的参数
//其实就是添加了System,out.println("Hello World");
.addStatement("$T.out.println($S)", System.class,"Hello World")
.build();
//HelloWorld是类名
TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.FINAL,Modifier.PUBLIC)
.addMethod(main) //在类中添加方法
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", typeSpec)
.build();
javaFile.writeTo(System.out);
}
1. for循环:
MethodSpec main = MethodSpec.methodBuilder("main")
.addCode(""
+ "int total = 0;\n"
+ "for (int i = 0; i < 10; i++) {\n"
+ " total += i;\n"
+ "}\n")
.build();
2. for循环第二种方式:
MethodSpec main = MethodSpec.methodBuilder("main")
.addStatement("int total = 0")
.beginControlFlow("for (int i = 0; i < 10; i++)")
.addStatement("total += i")
.endControlFlow()
.build();
computeRange方法
private MethodSpec computeRange(String name, int from, int to, String op) {
return MethodSpec.methodBuilder(name)
.returns(int.class)
.addStatement("int result = 0")
.beginControlFlow("for (int i = " + from + "; i < " + to + "; i++)")
.addStatement("result = result " + op + " i")
.endControlFlow()
.addStatement("return result")
.build();
}
TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.FINAL,Modifier.PUBLIC)
.addMethod(computeRange("multiply10to20", 10, 20, "*"))
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", typeSpec)
.build();
javaFile.writeTo(System.out);
生成代码:
package com.example.helloworld;
public final class HelloWorld {
int multiply10to20() {
int result = 0;
for (int i = 10; i < 20; i++) {
result = result * i;
}
return result;
}
}
添加import导入路径 addStaticImport:
JavaFile.builder("com.example.helloworld", hello)
.addStaticImport(hoverboard, "createNimbus")
.addStaticImport(namedBoards, "*")
.addStaticImport(Collections.class, "*")
.build();
就会导入这些文件
import static com.mattel.Hoverboard.Boards.*;
import static com.mattel.Hoverboard.createNimbus;
import static java.util.Collections.*;
类中添加变量 FieldSpec:
FieldSpec android = FieldSpec.builder(String.class, "android")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
.initializer("$S + $L", "Lollipop v.", 5.0d)
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC)
.addField(android)
.addField(String.class, "robot", Modifier.PRIVATE, Modifier.FINAL)
.build();
效果:
public class HelloWorld {
private final String android = "Lollipop v." + 5.0;
private final String robot;
}
- MethodSpec .addJavadoc(“XXX”) 方法上面添加注释
- MethodSpec.constructorBuilder() 构造器
- MethodSpec.addAnnotation(Override.class); 方法上面添加注解
- TypeSpec.enumBuilder(“XXX”) 生成一个XXX的枚举
- TypeSpec.interfaceBuilder(“HelloWorld”)生成一个HelloWorld接口 ==!
例如编译时注解:
Filer mFiler = processingEnv.getFiler();
//创建bindView方法
MethodSpec.Builder bindViewMethod = MethodSpec.methodBuilder("bindView")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addParameter(TypeName.get(mTypeElement.asType()), "host")
.addParameter(TypeName.OBJECT, "source")
.addParameter(TypeUtil.PROVIDER, "finder");
//bindView中添加查找控件对象方法
for (BindViewField field : mFields) {
bindViewMethod.addStatement("int id=host.getResources().getIdentifier($S, \"id\",host.getPackageName())", field.getFieldName());
//根据属性名获取id
bindViewMethod.addStatement("host.$N = ($T)(finder.findView(source, id))", field.getFieldName(), ClassName.get(field.getFieldType()));
}
//创建unBindView方法
MethodSpec.Builder unBindViewMethod = MethodSpec.methodBuilder("unBindView")
.addModifiers(Modifier.PUBLIC)
.addParameter(TypeName.get(mTypeElement.asType()), "host")
.addAnnotation(Override.class);
//方法中的实现
for (BindViewField field : mFields) {
unBindViewMethod.addStatement("host.$N = null", field.getFieldName());
}
//创建类
TypeSpec injectClass = TypeSpec.classBuilder(mTypeElement.getSimpleName() + "$$ViewBinder")
.addModifiers(Modifier.PUBLIC)
.addSuperinterface(ParameterizedTypeName.get(TypeUtil.BINDER, TypeName.get(mTypeElement.asType())))
.addMethod(bindViewMethod.build())
.addMethod(unBindViewMethod.build())
.build();
String packageName = mElements.getPackageOf(mTypeElement).getQualifiedName().toString();
JavaFile.builder(packageName, injectClass).build().writeTo(mFiler);
感谢各位大神文章帮助:参考文章