自定义apt实战之一>抽象工厂

7 篇文章 1 订阅

前面文章写了开始自定义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:
在这里插入图片描述

最后附上:源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值