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

版权声明:转载请注明:http://blog.csdn.net/gaugamela/article https://blog.csdn.net/Gaugamela/article/details/79694302

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

编译时注解的核心依赖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";
    }
}

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

展开阅读全文

没有更多推荐了,返回首页