前面文章写了开始自定义APT之前需要了解的知识,刚开始接触自定义APT的小伙伴,可以看一下我前面的几篇文章:
自定义APT之:调试
自定义APT基础之:Element
自定义APT之:javapoet
自定义apt实战之一>Mapcreate
接着上篇文章,前面 我们自定义了apt用来生成key-value键值对。这章我们来创建抽象工厂。数据模型中有连个角色:工厂、产品,之前的关系属于生产。
先说说抽象工程相对于键值对反射的好处:
1.不需要反射,性能相对消耗相对较低;
2.低耦合,依赖关系由抽象工厂负责,而抽象工厂又是自动创建的。
3.扩展性强,高内聚。以后如果想要扩增产品,只需要新增一个产品类。不需要修改其他代码。
所以先定义三个注解:AbstractFactory(工厂)、Product(产品)、Produce(生产)。
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface AbstractFactory
{
/**
* 实现类的类名,类似厂名
* @return
*/
String name() default "AbstractFactoryImp";
}
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Product
{
/**
* 该类的映射,类似于产品编号
* @return
*/
String key();
/**
* 要转化为的基类,作用于多态(因为个类,最多有一个父类,但却可以实现多个接口,所以此处要定义该类型属于哪个基类的),类似于产品类型
* @return
*/
Class superClass();
}
@Target(ElementType.METHOD)
@Documented
@Retention(RetentionPolicy.CLASS)
public @interface Produce
{
}
按照上章所说加好依赖之后,在 开始新建processor前,我们先抽出一个BaseProcessor基类:
public class BaseProcessor extends AbstractProcessor
{
// 负责生成java文件
protected Filer mFiler;
// 负责打印编译日志
protected Messager mMessager;
// 默认包名
protected static String PACKAGE_NAME = "com.kim.map";
// 日志工具类
protected LogUtil mLogUtil;
protected Elements mElements;
@Override
public synchronized void init(ProcessingEnvironment processingEnv)
{
super.init(processingEnv);
mFiler = processingEnv.getFiler();
mMessager = processingEnv.getMessager();
mLogUtil = new LogUtil(mMessager);
mElements = processingEnv.getElementUtils();
// 用于 获取.gradle文件中配置的参数
Map<String, String> options = processingEnv.getOptions();
if (options != null && options.size() > 0){
for (Map.Entry<String, String> entry : options.entrySet())
{
if (entry.getKey().contains("kim.applicationId")){
PACKAGE_NAME = entry.getValue();
}
}
}
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
{
return false;
}
@Override
public Set<String> getSupportedAnnotationTypes()
{
Set<String> types = new LinkedHashSet<>();
return types;
}
@Override
public SourceVersion getSupportedSourceVersion()
{
return SourceVersion.latestSupported();
}
}
下面开始新建:FactoryProcessor
@AutoService(Processor.class)
public class FactoryProcessor extends BaseProcessor
{
}
还是按照之前的想法:
第一步:整理注解;
第二步:根据注解的描述生成java文件
为了方便更好的描述抽象工厂模型,这边我新建一个实体类:FactoryBean
/**
* 抽象工厂模型
*/
class FactoryBean
{
// 工厂
TypeElement factoryImp;
// 生产的方法
List<ExecutableElement> methods;
public TypeElement getFactoryImp()
{
return factoryImp;
}
public void setFactoryImp(TypeElement factoryImp)
{
this.factoryImp = factoryImp;
}
public List<ExecutableElement> getMethods()
{
return methods;
}
public void setMethods(List<ExecutableElement> methods)
{
this.methods = methods;
}
}
第一步:筛选注解
思路:所有产品是一个集合,以产品类型做为key。工厂是一个集合。新建全局变量:
// 所有要要处理的抽象工厂
private List<FactoryBean> mFactoryList = new ArrayList<>();
// <父类名, 子类集合>
private Map<String,List<TypeElement>> productsMap = new HashMap<>();
重写processor方法:
private boolean dealed;
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
{
try
{
// 因为编译会执行3次process方法,debug调试得到发现processor对象还是同一个,所以通过dealed来判断是否需要再执行
if (dealed) return false;
filterAnnotations(roundEnv);
Iterator<FactoryBean> iterator = mFactoryList.iterator();
while (iterator.hasNext()){
generateJavaFile(iterator.next());
}
dealed = true;
} catch (Exception e)
{
e.printStackTrace();
mLogUtil.e(e);
}
return true;
}
下面看filterAnnotations()方法:
/**
* 筛选注解
*/
private boolean filterAnnotations(RoundEnvironment roundEnv) throws ClassNotFoundException
{
Set<? extends Element> factoryElements = roundEnv.getElementsAnnotatedWith(AbstractFactory.class);
Set<? extends Element> productElements = roundEnv.getElementsAnnotatedWith(Product.class);
// 筛选产品
Iterator<? extends Element> productIterator = productElements.iterator();
while (productIterator.hasNext())
{
TypeElement productElement = (TypeElement) productIterator.next();
if (!ProcessorUtil.isValidClass(productElement, mMessager, Product.class.getName())){
continue;
}
// Product product = productElement.getAnnotation(Product.class);
// 获取要转化的类名
String superName = "null";
try
{
for (AnnotationMirror m: productElement.getAnnotationMirrors())
{
if (m.getAnnotationType().toString().equals(Product.class.getName())){
for (Map.Entry e : m.getElementValues().entrySet())
{
ExecutableElement key = (ExecutableElement) e.getKey();
if (key.getSimpleName().toString().equals("superClass")){
AnnotationValue value = (AnnotationValue) e.getValue();
superName = value.getValue().toString();
}
}
}
}
} catch (Exception e)
{
e.printStackTrace();
}
// 当前类名
// String childName = productElement.getQualifiedName().toString();
// 存入
List<TypeElement> childClassNames;
if (productsMap.containsKey(superName)){
childClassNames = productsMap.get(superName);
}else {
childClassNames = new ArrayList<>();
}
childClassNames.add(productElement);
productsMap.put(superName, childClassNames);
}
//生成抽象工厂类
Iterator<? extends Element> factoryIterator = factoryElements.iterator();
while (factoryIterator.hasNext()){
Element itemElement = factoryIterator.next();
// 判断当前类是否是抽象类或者接口
if (itemElement.getKind() != ElementKind.INTERFACE && !itemElement.getModifiers().contains(Modifier.ABSTRACT)){
mLogUtil.e("@AbstractFactory 只能作用于接口或者抽象类");
return false;
}
TypeElement factoryElement = (TypeElement) itemElement;
List<ExecutableElement> methodList = new ArrayList<>();
// 获取该类的所有抽象方法
for (Element element: factoryElement.getEnclosedElements())
{
if (element.getAnnotation(Produce.class) != null
&& element.getModifiers().contains(Modifier.ABSTRACT)
&& element.getModifiers().contains(Modifier.PUBLIC)
&& element.getKind().equals(ElementKind.METHOD)){
if (((ExecutableElement)element).getParameters().size() != 1 ||
!((ExecutableElement)element).getParameters().get(0).asType().toString().equals("java.lang.String"))
{
mLogUtil.e("@Produce 作用的方法必须有且只有一个java.lang.String类型参数");
return false;
}
methodList.add((ExecutableElement) element);
}
}
if (methodList.size() > 0){
FactoryBean factory = new FactoryBean();
factory.setFactoryImp(factoryElement);
factory.setMethods(methodList);
mFactoryList.add(factory);
}
}
return true;
}
这里需要注意:自定义的类.Class ,jvm中并没有加载,需要通过镜像.
下面看一下生成java文件的方法:generateJavaFile()
/**
* 生成java文件
*/
private void generateJavaFile(FactoryBean factoryBean){
// mLogUtil.d("-----generateJavaFile----");
AbstractFactory factory = factoryBean.getFactoryImp().getAnnotation(AbstractFactory.class);
String createClassName = factory.name();
String superClassName = factoryBean.getFactoryImp().getQualifiedName().toString();
// 新建一个类
TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder(createClassName);
typeSpecBuilder.addModifiers(Modifier.PUBLIC);
// typeSpecBuilder.addOriginatingElement()
ClassName superInterface = ClassName.get(ProcessorUtil.getPackageName(factoryBean.factoryImp), factoryBean.factoryImp.getSimpleName().toString());
// 增加实现接口
if (factoryBean.getFactoryImp().getKind() == ElementKind.INTERFACE){
typeSpecBuilder.addSuperinterface(superInterface);
} else {// 增加继承类
typeSpecBuilder.superclass(superInterface);
}
// 重写方法
for (ExecutableElement methodE : factoryBean.methods)
{
if (methodE.getParameters().size() == 0) continue;
MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(methodE.getSimpleName().toString());
// for (TypeParameterElement v : methodE.getTypeParameters())
// {
// ParameterSpec parameterSpec = ParameterSpec.builder(v.get)
// }
// 方法的返回类型
TypeMirror returnTypeMirror = methodE.getReturnType();
String param = methodE.getParameters().get(0).toString();
ParameterSpec parameterSpec = ParameterSpec.builder(String.class, param)
.build();
List<TypeElement> entities = productsMap.get(returnTypeMirror.toString());
if (entities == null || entities.size() == 0) continue;
TypeName textUtilName = ClassName.get("android.text","TextUtils");
for (int i = 0; i < entities.size(); i ++)
{
TypeElement element = entities.get(i);
String key = element.getAnnotation(Product.class).key();
TypeName backEntity = ClassName.get(ProcessorUtil.getPackageName(element), element.getSimpleName().toString());
if (i == 0){
methodSpecBuilder.beginControlFlow("if ($T.equals($S, $L))", textUtilName, key, param);
}else {
methodSpecBuilder.nextControlFlow("else if ($T.equals($S, $L))", textUtilName, key, param);
}
methodSpecBuilder.addStatement("return new $T()", backEntity);
}
methodSpecBuilder.endControlFlow()
.addStatement("return null");
MethodSpec methodSpec = methodSpecBuilder.returns(TypeName.get(returnTypeMirror))
.addParameter(parameterSpec)
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.build();
typeSpecBuilder.addMethod(methodSpec);
}
// 生成.java文件
JavaFile javaFile = JavaFile.builder(PACKAGE_NAME, typeSpecBuilder.build()).build();
try
{
javaFile.writeTo(mFiler);
} catch (Exception e)
{
e.printStackTrace();
mLogUtil.e(e);
}
}
是不是发现编译之后没有生成?这里还需要重写getSupportedAnnotationTypes方法,该方法获取注解时要用到。
@Override
public Set<String> getSupportedAnnotationTypes()
{
Set set = super.getSupportedAnnotationTypes();
set.add(AbstractFactory.class.getCanonicalName());
set.add(Produce.class.getCanonicalName());
set.add(Product.class.getCanonicalName());
return set;
}
最后我们来使用apt了:新建Factory,IProduct,Product1,Product2:
@AbstractFactory()
public interface Factory
{
@Produce
IProduct creatProduct(String key);
}
public interface IProduct
{}
@Product(key = "1", superClass = IProduct.class)
public class Product1 implements IProduct
{}
@Product(key = "2", superClass = IProduct.class)
public class Product2 implements IProduct
{}
最后编译完成后:ctrl + N,查找类:AbstractFactoryImp:
最后附上:源码