APT可以根据注解,在编译时生成代码。
1.?? ?创建两个java library
?
2.?? ?依赖
factory-compiler的依赖
dependencies {
implementation 'com.google.auto.service:auto-service:1.0-rc4'
implementation project(':annotation')
implementation 'com.squareup:javapoet:1.10.0'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
}
?app的
implementation project(':annotation')
annotationProcessor project(':factory-compiler')
3.?? ?创建注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Factory {
Class type();
String id();
}
4.?? ?注解使用
在app中创建接口,还有对应实现类
public interface IShape {
void show();
}
@Factory(type = IShape.class, id = "Circle")
public class Circle implements IShape {
@Override
public void show() {
System.out.println("Circle");
}
}
@Factory(type = IShape.class, id = "Rectangle")
public class Rectangle implements IShape {
@Override
public void show() {
System.out.println("Rectangle");
}
}
在MainActivity中使用
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IShapeFactory iShapeFactory = new IShapeFactory();
IShape circle = iShapeFactory.create("Circle");
TextView tv = findViewById(R.id.tv);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
circle.show();
}
});
}
@Override
protected void onResume() {
super.onResume();
}
}
此时我们还没有IShapeFactory这个类,需要用APT在编译生成。
5.?? ?实现APT
首先我们创建两个实体类
public class FactoryAnnotatedClass {
private TypeElement typeElement;
private String canonicalName;
private String simpleTypeName;
private final String id;
public FactoryAnnotatedClass(TypeElement classElement) {
this.typeElement = classElement;
Factory annotation = classElement.getAnnotation(Factory.class);
id = annotation.id();
try { // 该类已经被编译
Class<?> clazz = annotation.type();
canonicalName = clazz.getCanonicalName();
simpleTypeName = clazz.getSimpleName();
} catch (MirroredTypeException mte) {// 该类未被编译
DeclaredType classTypeMirror = (DeclaredType) mte.getTypeMirror();
TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();
canonicalName = classTypeElement.getQualifiedName().toString();
simpleTypeName = classTypeElement.getSimpleName().toString();
}
}
……get方法
}
public class FactoryGroup {
private static final String SUFFIX = "Factory";
public final String qualifiedClassName;
private final Map<String, FactoryAnnotatedClass> itemsMap = new LinkedHashMap<>();
public FactoryGroup(String qualifiedClassName) {
this.qualifiedClassName = qualifiedClassName;
}
public void add(FactoryAnnotatedClass toInsert) {
itemsMap.put(toInsert.getId(), toInsert);
}
public void generateCode(Elements elementUtils, Filer filer) {
TypeElement superClassName = elementUtils.getTypeElement(qualifiedClassName);
String factoryClassName = superClassName.getSimpleName() + SUFFIX;
PackageElement pkg = elementUtils.getPackageOf(superClassName);
String packageName = pkg.isUnnamed() ? null : pkg.getQualifiedName().toString();
MethodSpec.Builder method = MethodSpec.methodBuilder("create")
.addModifiers(Modifier.PUBLIC)
.addParameter(String.class, "id")
.returns(TypeName.get(superClassName.asType()));
method.beginControlFlow("if (id == null)")
.addStatement("throw new IllegalArgumentException($S)", "id is null!")
.endControlFlow();
for (FactoryAnnotatedClass item : itemsMap.values()) {
method.beginControlFlow("if ($S.equals(id))", item.getId())
.addStatement("return new $L()", item.getTypeElement().getQualifiedName().toString())
.endControlFlow();
System.out.println("QualifiedName "+item.getTypeElement().getQualifiedName().toString());
}
method.addStatement("throw new IllegalArgumentException($S + id)", "Unknown id = ");
TypeSpec typeSpec = TypeSpec
.classBuilder(factoryClassName)
.addModifiers(Modifier.PUBLIC)
.addMethod(method.build())
.build();
try {
if (packageName != null) {
JavaFile.builder(packageName, typeSpec).build().writeTo(filer);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
最主要的就是generateCode方法,里面利用了JavaPoet生成代码。
接下来创建一个类继承AbstractProcessor,主要实现下面两个方法。
@AutoService(Processor.class)
public class FactoryProcessor extends AbstractProcessor{
private final Map<String, FactoryGroup> factoryClasses = new LinkedHashMap<>();
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotations = new LinkedHashSet<>();
annotations.add(Factory.class.getCanonicalName());
return annotations;
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
for (TypeElement element:set){
// 通过RoundEnvironment获取到所有被@Factory注解的对象
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(element);
for (Element annotatedElement : elements) {
TypeElement typeElement = (TypeElement) annotatedElement;
FactoryAnnotatedClass annotatedClass = new FactoryAnnotatedClass(typeElement);
FactoryGroup factoryGroup = factoryClasses.get(annotatedClass.getCanonicalName());
if (factoryGroup == null) {
String qualifiedGroupName = annotatedClass.getCanonicalName();
factoryGroup = new FactoryGroup(qualifiedGroupName);
factoryClasses.put(qualifiedGroupName, factoryGroup);
}
factoryGroup.add(annotatedClass);
}
for (FactoryGroup factoryGroup : factoryClasses.values()) {
factoryGroup.generateCode(processingEnv.getElementUtils(), processingEnv.getFiler());
}
return true;
}
return false;
}
}
process方法会被多次调用,方法参数中的set是我们注解类所对应的TypeElement对象,仅当set不为空时,我们才去操作。
?然后调用RoundEnvironment的getElementsAnnotatedWith方法获取到使用了改注解的类的TypeElement对象(既Circle、Rectangle)。
然后我们把这两个TypeElement对象封装成FactoryAnnotatedClass对象,存放到factoryGroup中,简单说factoryGroup就是用来存放使用了同一个注解类的对象,为了方便我们创建多个注解一起使用所以又创建了一个map对象(既factoryClasses)来存放factoryGroup,然后我们调用generateCode方法生成对应java文件。
?在generateCode方法中,我们通过获取Circle、Rectangle类注解的id,来生成代码,rebuild之后即可看到对应文件。
?
优点:
1、当我们需要再创建一个实现类时,只需要给实现类标上注解就可以,不想要去添加工厂类的代码。如果是自己编写的工厂类则需要再加一段代码。
2、APT>反射,解析注解可以不需要用反射的方式,APT在调式期间就可以解析。
JavaPoet使用文档: https://github.com/aheckl/javapoet
|