annotationProcessor、kapt、KSP都是注解处理工具,用于在编译时处理注解并生成代码。
一、annotationProcessor
annotationProcessor是Java注解工具。详细的使用可以参考我之前写的一篇文章。
二、kapt
kapt (Kotlin Annotation Processing Tool) 是Kotlin编译器的注解处理工具,它的作用是使Kotlin项目能够使用为Java设计的注解处理器。因为Kotlin编译器本身不能把Kotlin源码当成Java源码来交给Java注解处理器处理。
关于它的原理,以下是ChatGPT的回答,大家可以参考:
kapt 在编译流程中插入一个“桥”步骤,典型流程为:
1. Kotlin 源码 → 生成 Java stub(或 classfile 抽象信息):kapt 会把 Kotlin 的类/接口等生成对应的 Java 风格“存根(stubs)”,这些 stub 表示类结构、注解与签名,但不包含 Kotlin 特定实现细节。
2.使用 Java Annotation Processing API(javax.annotation.processing) 在这些 stub 上运行现有的 Java 注解处理器(annotation processors)。处理器可以读取注解、类型信息,生成 Java 源文件(或其他资源)。
3.kapt 把注解处理器生成的源文件合并回编译流程:这些生成的 Java 源会被 javac 编译并最终与 Kotlin 编译产物一起打包。
4.最终产物包含 Kotlin 编译结果与注解处理器生成并编译的类。
另外:如果是纯kotlin项目,建议优先使用KSP,因为kapt会增加编译时间。
三、KSP
接下来要讲的就是我们今天的主角——KSP (Kotlin Symbol Processing) 是专门为Kotlin设计的编译期符号处理工具。说的直白点就是为Kotlin设计的注解处理工具。
先看看KSP的用法,拿Room数据库的使用方式来举例。
(1)将KSP插件添加到项目中
首先,在主项目的build.gradle.kts
文件中声明 KSP 插件。请务必选择与项目的 Kotlin 版本一致的 KSP 版本。可以在 KSP GitHub 页面上找到版本列表。
注意 :KSP 版本的前一部分必须与 build 中使用的 Kotlin 版本一致。例如,如果您使用的是 Kotlin2.0.21
,则 KSP 版本必须是2.0.21-x.y.z
版本之一。
plugins { id("com.google.devtools.ksp") version "2.0.21-1.0.27" apply false }
然后,在模块级 build.gradle.kts
文件中启用 KSP:
plugins { id("com.google.devtools.ksp") }
(2)将注解处理器替换为 KSP
dependencies { ksp("androidx.room:room-compiler:2.5.0") }
以上就是KSP的使用,是不是很简单。
那接下来就尝试自己实现一个自定义注解和注解处理器吧。
四、自定义注解和注解处理器
实现一个BindView
的功能,跟我之前写的这篇文章类似。
(1)创建lib_annotations
项目,并创建BindView
注解。

build.gradle.kts
中只保留如下配置,其余配置都删掉。

BindView
注解:
//保留的意思 只在编译时用 @Retention(AnnotationRetention.SOURCE) //作用范围 @Target(AnnotationTarget.FIELD) annotation class BindView(val value: String)
(2)创建lib_processor
项目,并创建注解处理器BindProcessor
。
build.gradle.kts
配置如下
//以下是toml文件中的定义 kotlin = "2.2.20" ksp = "2.2.20-2.0.2" kotlinpoet = "2.2.0" [libraries] ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinpoet" } [plugins] kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } //以下是build.gradle.kts中的配置 plugins { kotlin("jvm") alias(libs.plugins.ksp) } dependencies { compileOnly(libs.ksp.api) implementation(libs.kotlinpoet.ksp) implementation(project(":lib_annotations")) }
其中kotlinpoet
是方便我们创建Kotlin
源文件的。
BindProcessor
源码:
class BindProcessor( private val codeGenerator: CodeGenerator, private val logger: KSPLogger ) : SymbolProcessor { override fun process(resolver: Resolver): List<KSAnnotated> { // 查找带 @BindView 的类 val bindViewSymbols = resolver.getSymbolsWithAnnotation(BindView::class.qualifiedName!!) val ret = bindViewSymbols.filter { !it.validate() }.toList() bindViewSymbols .filter { it.validate() } .filterIsInstance<KSPropertyDeclaration>() .groupBy { it.parentDeclaration } .forEach { classDeclaration, properties -> if (classDeclaration is KSClassDeclaration) { generateBindingClass(classDeclaration, properties, resolver) } } return ret } private fun generateBindingClass( classDeclaration: KSClassDeclaration, properties: List<KSPropertyDeclaration>, resolver: Resolver ) { val packageName = classDeclaration.packageName.asString() val className = "${classDeclaration.simpleName.asString()}ViewBinding" logger.info("Generating binding class: $packageName.$className") // val bindingClass = TypeSpec.classBuilder(className) // .addFunction(generateBindFunction(classDeclaration, properties)) // .build() val companionObject = TypeSpec.companionObjectBuilder() .addFunction(generateBindFunction(classDeclaration, properties)) .build() val bindingClass = TypeSpec.classBuilder(className) .addType(companionObject) .build() val file = FileSpec.builder(packageName, className) .addType(bindingClass) .build() file.writeTo(codeGenerator, Dependencies(true, *resolver.getAllFiles().toList().toTypedArray())) } private fun generateBindFunction( classDeclaration: KSClassDeclaration, properties: List<KSPropertyDeclaration> ): FunSpec { val targetClassName = ClassName( classDeclaration.packageName.asString(), classDeclaration.simpleName.asString() ) val bindFunction = FunSpec.builder("bind") .addParameter("target", targetClassName) properties.forEach { property -> val bindViewAnnotation = property.annotations.first { it.shortName.asString() == "BindView" } logger.info("arguments--->${bindViewAnnotation.arguments}") val resourceId = bindViewAnnotation.arguments.first { it.name?.asString() == "value" }.value as String val propertyName = property.simpleName.asString() // 生成绑定代码:target.propertyName = target.findViewById(resourceId) bindFunction.addStatement("target.%L = target.findViewById(R.id.%L)", propertyName, resourceId) } return bindFunction.build() } } class BindProcessorProvider : SymbolProcessorProvider { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { return BindProcessor(environment.codeGenerator, environment.logger) } }
上面注解处理器主要作用是遍历项目中使用了BindView
注解的成员变量,然后生成XXXViewBinding
类,并创建静态方法bind(activity)
。
(3)注册provider
,有两种注册的方式。
注册方式1:在 resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
中写入实现类的全名。

注册方式2:使用AutoService
注解。
引入依赖:
[libraries] autoService = "com.google.auto.service:auto-service-annotations:1.1.1" autoService-ksp = "dev.zacsweers.autoservice:auto-service-ksp:1.2.0" dependencies { implementation(libs.autoService) ksp(libs.autoService.ksp) }
在Provider
类上面加上AutoService
注解:
@AutoService(SymbolProcessorProvider::class) class BindProcessorProvider : SymbolProcessorProvider { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { return BindProcessor(environment.codeGenerator, environment.logger) } }
以上两种方式任选一种即可。
执行./gradlew assembleDebug --info
命令,在如下目录就能看到自动生成的文件。

(4)在app
主项目中引入注解和注解处理器。
build.gradle.kts
中配置如下:
plugins { alias(libs.plugins.ksp) } dependencies { ksp(project(":lib_processor")) implementation(project(":lib_annotations")) }
MainActivity
中使用注解:
class MainActivity : AppCompatActivity() { @BindView("titleTv") var textView: TextView? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) MainActivityViewBinding.bind(this) textView?.text = "呵呵" } }
通过以上,我们学习到:
·KSP基础:了解了如何创建 SymbolProcessor 和 SymbolProcessorProvider
·注解处理:学习如何解析注解参数和生成代码
·KotlinPoet使用:掌握代码生成库的基本用法
原创不易,转载请注明出处:https://www.longdw.com