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

Posted ZhangJianIsAStark

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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";
    

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

以上是关于AbstractProcessor: 利用注解动态生成代码的主要内容,如果未能解决你的问题,请参考以下文章

javajavac 相关API JavaCompiler StandardJavaFileManager AbstractProcessor

Android APT注解处理器 ( 注解标注 与 初始化方法 )

升级你的Logger(注解+动态代理)

Android进阶之光学习记录——注解与依赖注入框架ButterKnife的尝试

Android进阶之光学习记录——注解与依赖注入框架ButterKnife的尝试

Android进阶之光学习记录——注解与依赖注入框架ButterKnife的尝试