使用过 ButterKnife 的都知道,ButterKnife 中使用了注解的方式帮助我们简化代码的书写量,大大提高了开发效率,其实它就是使用了注解处理器的方式,帮助我们生成了相应的代码。
下面借用一个简单的例子描述一下注解处理器的使用:
public interface Person
-> public class Student
-> public class Teacher
上面是三个类的继承关系。
我们想创建一个工厂模式来创建Person的实现类,如:
public class PersonFactory {
public static Person create(String id) {
if (id == null) {
return null;
}
if ("Student".equals(id)) {
return new Student();
}
if ("Teacher".equals(id)) {
return new Teacher();
}
return null;
}
}
上面的代码是简单的工厂模式,如果我们又有了一个类想要实现Person就需要手动修改这个工厂类。
下面我们使用注解的方式自动创建工厂类,只需要关心实现类的内容就行了。
创建工厂注解 (module name: annotation)
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Factory {
// 该类在工厂类中标识
String id();
// 工厂类的类型
Class type();
}
实现思路:
1.FactoryAnnotatedClass 存放提取到 注解 Factory 的类信息,包含CanonicalName,SimpleName 等信息
2.FactoryGroupClasses 根据注解的type分组存放 FactoryAnnotatedClass 的信息
3.Map<String, FactoryGroupClasses> factoryClasses 分组后的 FactoryGroupClasses 的Map信息
4.根据factoryClasses使用 javapoet 生成单个的工厂类文件。
创建一个注解处理器 (module name: compile)
@AutoService(Processor.class)
public class FactoryProcessor extends AbstractProcessor {
private Map<String, FactoryGroupClasses> factoryClasses = new LinkedHashMap<>();
private Filer mFiler; // 文件相关的辅助类
private Elements mElementUtils; // 元素相关的辅助类
private Types mTypeUtils; // 成员相关的辅助类
private Messager mMessager; // 日志相关的辅助类
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler();
mElementUtils = processingEnv.getElementUtils();
mTypeUtils = processingEnv.getTypeUtils();
mMessager = processingEnv.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
try {
// 打印注解处理信息
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "processing");
// 查找项目中所有注解为 Factory 的类
for (Element element : roundEnvironment.getElementsAnnotatedWith(Factory.class)) {
if(element.getKind() != ElementKind.CLASS) {
continue;
}
// TypeElement:代表类信息
FactoryAnnotatedClass annotatedClass = new FactoryAnnotatedClass((TypeElement) element);
// 检测类是否合法
if(checkValidClass(annotatedClass)) {
String qualifiedGroupName = annotatedClass.getQualifiedGroupClassName();
FactoryGroupClasses groupClasses = factoryClasses.get(qualifiedGroupName);
if(groupClasses == null) {
groupClasses = new FactoryGroupClasses(qualifiedGroupName);
factoryClasses.put(qualifiedGroupName, groupClasses);
}
groupClasses.add(annotatedClass);
}
}
// 生成注解文件
for (FactoryGroupClasses factoryGroupClass : factoryClasses.values()) {
factoryGroupClass.generateCode(mElementUtils, mFiler);
}
factoryClasses.clear();
} catch (Exception ex) {
ex.printStackTrace();
error(null, ex.getMessage());
}
return true;
}
private boolean checkValidClass(FactoryAnnotatedClass annotatedClass) {
TypeElement typeElement = annotatedClass.getAnnotatedTypeElement();
if(!typeElement.getModifiers().contains(Modifier.PUBLIC) || typeElement.getModifiers().contains(Modifier.ABSTRACT)) {
return false;
}
TypeElement superClassElement = mElementUtils.getTypeElement(annotatedClass.getQualifiedGroupClassName());
if(superClassElement.getKind() == ElementKind.INTERFACE) {
if(!typeElement.getInterfaces().contains(superClassElement.asType())) {
return false;
}
} else {
TypeElement currentClass = typeElement;
while (true) {
TypeMirror superClassType = currentClass.getSuperclass();
if(superClassType.getKind() == TypeKind.NONE) {
return false;
}
if (superClassType.toString().equals(annotatedClass.getQualifiedGroupClassName())) {
break;
}
currentClass = (TypeElement) mTypeUtils.asElement(superClassType);
}
}
// non-parameter constructor
boolean newInstance = false;
for (Element enclosed : typeElement.getEnclosedElements()) {
if (enclosed.getKind() == ElementKind.CONSTRUCTOR) {
ExecutableElement constructorElement = (ExecutableElement) enclosed;
if (constructorElement.getParameters().size() == 0 && constructorElement.getModifiers().contains(Modifier.PUBLIC)) {
newInstance = true;
break;
}
}
}
return newInstance;
}
...
/**
* 指定哪些注解应该被注解处理器注册
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
// 将 Factory 注解添加到 注解处理器 处理注解的列表中
types.add(Factory.class.getCanonicalName());
return types;
}
}
// FactoryGroupClasses 的实现
public class FactoryGroupClasses {
private static final String SUFFIX = "Factory";
private String qualifiedClassName;
private Map<String, FactoryAnnotatedClass> classMap = new LinkedHashMap<>();
public FactoryGroupClasses(String className) {
this.qualifiedClassName = className;
}
public void add(FactoryAnnotatedClass cls) {
if (!classMap.containsKey(cls.getId())) {
classMap.put(cls.getId(), cls);
}
}
// 这里使用的是 square 公司的开源库 javapoet 实现生成代码文件
public void generateCode(Elements util, Filer filer) throws IOException {
TypeElement superClassElement = util.getTypeElement(qualifiedClassName);
String factoryClassName = superClassElement.getSimpleName() + SUFFIX;
// package info
PackageElement packageElement = util.getPackageOf(superClassElement);
String packageName = packageElement.isUnnamed() ? null : packageElement.getQualifiedName().toString();
// xxxFactory public create(String id)
MethodSpec.Builder method = MethodSpec.methodBuilder("create")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(String.class, "id")
.returns(TypeName.get(superClassElement.asType()));
// if(id == null) return null;
method.beginControlFlow("if (id == null)").addStatement("return null").endControlFlow();
// add if statement
for (FactoryAnnotatedClass cls : classMap.values()) {
method.beginControlFlow("if ($S.equals(id))", cls.getId())
.addStatement("return new $L()", cls.getAnnotatedTypeElement().getQualifiedName().toString())
.endControlFlow();
}
method.addStatement("return null");
TypeSpec typeSpec = TypeSpec.classBuilder(factoryClassName).addModifiers(Modifier.PUBLIC).addMethod(method.build()).build();
JavaFile.builder(packageName, typeSpec).build().writeTo(filer);
}
}
在app build.gradle中配置引用
dependencies {
...
compile project(':jknife')
annotationProcessor project(':jknife_compile')
}
Factory 注解的使用(module name: app)
@Factory(
id = "Student",
type = Person.class
)
public class Student implements Person {}
@Factory(
id = "Teacher",
type = Person.class
)
public class Teacher implements Person {}
在项目运行后编译器会自动运行注解处理器,在apt文件夹中相应的源码。
生成后的代码:
哈哈,我们可以自由的扩展相应的工厂类了。
附上我自己简单实现的 JKnife, 模拟 ButterKnife 可以生成相应的绑定代码,仅供学习使用。
// 代码中相关类型解释
// 按层级顺序
PackageElement 描述包的信息
-> TypeElement 描述类的信息
-> ExecutableElement 描述类中的方法信息
-> VariableElement 描述参数变量的信息
-> VariableElement 描述成员变量的信息
// 向下获取子层级的 element 列表
element.getEnclosedElements
// 向上获取父层级的 element
element.getEnclosingElement
Elements Element 工具类
->PackageElement getPackageOf(typeElement) // 获取TypeElement的包信息
// 获取TypeElement的类型信息
TypeName.get(typeElement.asType())
Types 工具类
->asElement(classType) // 获取类class的 TypeElement 信息