使用Ksp实现简单注解处理器

682 阅读1分钟

第一步 创建一个工程以及两个module

创建app工程以及两个module,分别叫annotation以及processor。在annotation中仅完成目标注解的创建,在processor中主要处理识别出工程中的所有带有注解的方法或者类,并在编译过程中生成对应的文件。

image.png

第二步 处理annotation的module

1、依赖处理(也可以使用kotlin,本工程使用的是java)

plugins {
    id 'java-library'
}

apply from: rootProject.file('publish.gradle')
java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

2、注解处理

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface JavaHandlerAnnotation {
    String methodName() default "";
}

第三步 处理processor的module

1、依赖处理

apply plugin: 'java-library'
apply plugin: 'kotlin'
sourceSets.main {
    java.srcDirs("src/main/java")
}
dependencies {
    implementation project(path: ':lib_annotation')
    implementation ('com.squareup:javapoet:1.13.0')
    implementation("com.squareup:kotlinpoet:1.12.0")
    implementation("com.squareup:kotlinpoet-ksp:1.11.0")
    implementation("com.squareup:kotlinpoet-metadata:1.11.0")
    implementation("com.google.devtools.ksp:symbol-processing-api:1.7.20-1.0.6")
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

需要引入annotation的module,加入javapoet、kotlinpote等依赖包

2、创建注解处理器

JavaHandlerKspProcessorProvider : SymbolProcessorProvider

提供注解处理器的provider,return的内容就是接下来创建的注解处理器

撰写注解处理器,举例说明


class JavaHandlerSymbolProcessor(
    private val logger: KSPLogger,
    private val codeGenerator: CodeGenerator,
    options: Map<String, String>
) : SymbolProcessor {

    private var init = false

    @OptIn(KotlinPoetKspPreview::class)
    override fun process(resolver: Resolver): List<KSAnnotated> {
        logger.warn("------------JavaHandlerKspStart----------")
        val symbol = resolver.getSymbolsWithAnnotation(JavaHandlerAnnotation::class.qualifiedName!!)
        val elements = symbol.filterIsInstance<KSFunctionDeclaration>().toList()
        if (init) {
            return emptyList()
        }
        val list = mutableListOf<String>()
        if (elements.isNotEmpty()) {
            elements.forEach {
                list.add(it.qualifiedName!!.getShortName())
            }
            // 生成方法信息
            val companion = TypeSpec.companionObjectBuilder().addProperty(
                PropertySpec.builder("methodList", String::class)
                        //初始化值
                    .initializer("%S", list.joinToString(separator = ","))
                    .addAnnotation(JvmStatic::class)
                    .build()
            ).build()

            // 生成对应的类

            val file =
                FileSpec.builder(packageName, className)
                    .addType(
                        TypeSpec.classBuilder(
                            ClassName(
                                packageName,
                                className
                            )
                        //增加注释
                        ).addKdoc("hhhhhhhhhhhhhhhhhhhhhhhhhhhh")
                            .addType(companion).build()
                    ).build()
            logger.warn("------------JavaHandlerKspStart list size:${list.size}----------")
            init = true
            file.writeTo(codeGenerator, true, emptyList())
        }
        logger.warn("------------JavaHandlerKspStart end----------")
        return emptyList()
    }
}

接下来对于生成文件的kotlinpote一些api解释:

类对象 说明

MethodSpec 代表一个构造函数或方法声明 TypeSpec 代表一个类,接口,或者枚举声明 FieldSpec 代表一个成员变量,一个字段声明 JavaFile 包含一个顶级类的Java文件 ParameterSpec 用来创建参数 AnnotationSpec 用来创建注解 ClassName 用来包装一个类 TypeName 类型,如在添加返回值类型是使用 TypeName.VOID

通配符: %S 字符串,如:%S, ”hello” %T 类、接口,如:%T, MainActivity

3、注册注解处理器 在此目录下创建自己的注解处理器 resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider

com.vivo.space.jsdetect.JavaHandlerKspProcessorProvider

第四步 处理整个工程的依赖

1、首先是整个工程的build.gradle文件添加依赖


classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:1.7.20-1.0.6"

2、app模块下的build.gradle文件


plugins {
    id 'com.android.application'
    id 'com.google.devtools.ksp'
    id 'kotlin-android'

}

implementation project(path: ':lib_annotation')
implementation project(path: ':lib_processor')
ksp project(':lib_processor')

android.sourceSets.all { it.java.srcDir("build/generated/ksp/${it.name}/kotlin/") }

android.sourceSets是编译的apk中是否要加入此目录下生成的文件