AbstractProcessor: 利用注解动态生成代码

按照处理时期,注解分为两种类型,一种是运行时注解,另一种是编译时注解。

编译时注解的核心依赖APT(Annotation Processing Tools)实现,对应的处理流程为:
在某些代码元素上(如类型、函数、字段等)添加注解;
编译时编译器会检查AbstractProcessor的子类,
然后将添加了注解的所有元素都传递到该类的process函数中;
使得开发人员可以在编译器进行相应的处理。
例如,根据注解生成新的Java类,
这也就是EventBus,Retrofit,Dragger等开源库的基本原理。

本篇博客就从一个简单的例子入手,
看看如何利用AbstractProcessor和注解来动态生成代码。


一、创建Java Library
Java API已经提供了扫描源码并解析注解的框架,
我们只需要继承AbstractProcessor类来实现解析注解相关的逻辑。
因此,我们一般需要自己创建一个Java Library。

考虑到兼容性问题,在Java Library对应的build.gradle中可以添加如下字段:

sourceCompatibility = "1.7"
targetCompatibility = "1.7"

之后,我们就可以简单的创建一个注解:

package com.zhangjian;

/**
 * @author zhangjian on 18-3-23.
 */

public @interface CustomAnnotation {
}

然后创建对应的注解处理器:

//指定该注解处理器可以解决的类型,需要完整的包名+类命
@SupportedAnnotationTypes("com.zhangjian.CustomAnnotation")
//指定编译的JDK版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class CustomAnnotationProcessor extends AbstractProcessor{

    //这里就是处理注解的process函数
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        //创建动态代码,实际上就是创建一个String, 写入到文件里
        //然后文件会被解释为.class文件

        StringBuilder builder = new StringBuilder()
                .append("package com.zhangjian.annotationprocessor.generated;\n\n")
                .append("public class GeneratedClass {\n\n")
                .append("\tpublic String getMessage() {\n")
                .append("\t\treturn \"");

        //获取所有被CustomAnnotation修饰的代码元素
        for (Element element : roundEnvironment.getElementsAnnotatedWith(CustomAnnotation.class)) {
            String objectType = element.getSimpleName().toString();
            builder.append(objectType).append(" exists!\\n");
        }

        builder.append("\";\n")
                .append("\t}\n")
                .append("}\n");

        //将String写入并生成.class文件
        try {
            JavaFileObject source = processingEnv.getFiler().createSourceFile(
                    "com.zhangjian.annotationprocessor.generated.GeneratedClass");

            Writer writer = source.openWriter();
            writer.write(builder.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            //
        }

        return true;
    }
}

接着,需要在Java Module的main目录下,创建出resources目录;
在resources目录下创建出META-INF目录;
并在META-INF目录下创建出services目录。
最后,在services目录下创建出名为javax.annotation.processing.Processor的文件。
在其中申明我们的注解处理器:

com.zhangjian.CustomAnnotationProcessor

二、使用Java Library
创建完Java Library后,我们就可以使用了。
在app module对应的build.gradle中,添加类似如下语句:

task processorTask(type: Exec) {
    //将编译出的java library对应的jar包,复制到app modulelibs
    commandLine 'cp', '../processor/build/libs/processor.jar', 'libs/'
}

processorTask.dependsOn(':processor:build')
preBuild.dependsOn(processorTask)

此外,还需要指定依赖文件并考虑兼容性,需要在build.gradle中添加:

android {
    ............
    defaultConfig {
        ........
        //由于我们是自己创建的annotationProcessor, 且在编译时使用
        //因此需要在此申明
        //不过这个字段在高版本的gradle中已经是deprecated了
        javaCompileOptions {
            annotationProcessorOptions {
                includeCompileClasspath true
            }
        }
    }
    ............
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}

dependencies {
    ..........
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    annotationProcessor "com.neenbedankt.gradle.plugins:android-apt:1.8"
}

如上注释,在高版本的gradle中建议以如下方式,引入自定义的annotationProcessor:

dependencies {
    ........
    annotationProcessor files("libs/processor.jar")
}

接下来我们就可以在代码中使用注解了:

/**
 * @author zhangjian
 */
@CustomAnnotation
public class MainActivity extends AppCompatActivity {
    @Override
    @CustomAnnotation
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //GeneratedClass是动态生成的
        Log.v("Test", new GeneratedClass().getMessage());
    }
}

我们rebuild一下app module就可以看到对应动态生成的代码,
在build/source/apt目录下:

package com.zhangjian.annotationprocessor.generated;

public class GeneratedClass {
    public String getMessage() {
        return "MainActivity exists!\nonCreate exists!\n";
    }
}

三、总结
至此,我们大概了解如何利用注解动态生成代码了。
按照套路来,应该还是比较容易理解和使用的。

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值