12.4.2 KSP 快速入门
原文链接: https://kotlinlang.org/docs/ksp-quickstart.html
12.4.2 KSP 快速入门
在本指南中,你将学到:
- 如何把基于 KSP 的注解处理器添加到项目中。
- 如何使用 KSP API 创建你自己的注解处理器。
- 在哪里找到处理器生成的代码。
在项目中接入基于 KSP 的处理器
要在项目中使用外部处理器,请在你的 build.gradle(.kts) 文件的 plugins {} 块中添加 KSP。如果该处理器只在某个特定模块中需要,请把它添加到该模块的 build.gradle(.kts) 文件中:
Kotlin
1
2
3
4
5
6
| // build.gradle.kts
plugins {
kotlin("jvm") version "2.4.20"
id("com.google.devtools.ksp") version "2.3.10"
}
|
Groovy
1
2
3
4
5
6
| // build.gradle
plugins {
id 'org.jetbrains.kotlin.jvm' version '2.4.20'
id 'com.google.devtools.ksp' version '2.3.10'
}
|
提示: 要查找 KSP 的最新版本,请查看 GitHub 上的 Releases。
在顶层的 dependencies {} 块中,添加你想使用的处理器。本示例使用 Moshi,但其他处理器的做法相同:
Kotlin
1
2
3
4
5
| // build.gradle.kts
dependencies {
ksp("com.squareup.moshi:moshi-kotlin-codegen:1.15.2")
}
|
Groovy
1
2
3
4
5
| // build.gradle
dependencies {
ksp 'com.squareup.moshi:moshi-kotlin-codegen:1.15.2'
}
|
ksp(...) 配置只把处理器应用于应用源码。要处理测试源码,请使用 kspTest(...) 配置添加处理器。
注意: ksp(...) 配置仅在单平台项目中可用。要了解如何为各个 Kotlin Multiplatform 目标和编译配置处理器,请参阅多平台项目中的 KSP。
创建你自己的处理器
按照以下步骤操作,你将创建一个简单的注解处理器,它会生成一个 helloWorld() 函数。虽然它在实践中没什么用,但展示了创建自定义处理器和注解的基础知识。
把 KSP 添加到项目
创建一个新的 Kotlin 项目并添加 KSP 插件:
- 在 IntelliJ IDEA 中,选择 File | New | Project。
- 在左侧列表中选择 Kotlin。
- 选择 Gradle 作为构建系统并点击 Create。

- 把 KSP 插件添加到
build.gradle(.kts) 文件:
Kotlin
1
2
3
4
5
6
| // build.gradle.kts
plugins {
kotlin("jvm") version "2.4.20"
id("com.google.devtools.ksp") version "2.3.10" apply false
}
|
Groovy
1
2
3
4
5
6
| // build.gradle
plugins {
id 'org.jetbrains.kotlin.jvm' version '2.4.20'
id 'com.google.devtools.ksp' version '2.3.10' apply false
}
|
创建注解
在项目根目录创建一个新模块,并声明一个注解:
- 选择 File | New | Module。
- 在左侧列表中选择 Kotlin。
- 填写以下字段并点击 create:
- Name:annotations
- Build system:Gradle

- 在该模块中创建一个
HelloWorldAnnotation.kt 文件,并声明一个名为 HelloWorldAnnotation 的注解:
1
2
3
4
5
| // annotations/src/main/kotlin/com/example/annotations/HelloWorldAnnotation.kt
package com.example.annotations
annotation class HelloWorldAnnotation
|
创建并注册处理器
- 在项目根目录再创建一个名为 processor 的模块。
- 在该模块的
build.gradle(.kts) 文件中,把 KSP API 和你声明的注解添加为依赖:
Kotlin
1
2
3
4
5
6
7
8
9
10
| // processor/build.gradle.kts
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":annotations"))
implementation("com.google.devtools.ksp:symbol-processing-api:2.3.6")
}
|
Groovy
1
2
3
4
5
6
7
8
9
10
| // processor/build.gradle
plugins {
id 'org.jetbrains.kotlin.jvm'
}
dependencies {
implementation project ':annotations'
implementation 'com.google.devtools.ksp:symbol-processing-api:2.3.6'
}
|
- 在 processor 模块中创建一个新的
HelloWorldProcessor.kt 文件并添加以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| // processor/src/main/kotlin/HelloWorldProcessor.kt
class HelloWorldProcessor(val codeGenerator: CodeGenerator) : SymbolProcessor {
// 1️⃣ process() 函数
override fun process(resolver: Resolver): List<KSAnnotated> {
resolver
.getSymbolsWithAnnotation("com.example.annotations.HelloWorldAnnotation")
.filter { it.validate() }
.filterIsInstance<KSFunctionDeclaration>()
.forEach { it.accept(HelloWorldVisitor(), Unit) }
return emptyList()
}
// 2️⃣ 访问者
inner class HelloWorldVisitor : KSVisitorVoid() {
override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
createNewFileFrom(function).use { file ->
file.write(
"""
fun helloWorld(): Unit {
println("Hello world from function generated by KSP")
}
""".trimIndent()
)
}
}
}
// 3️⃣ createNewFileFrom() 函数
private fun createNewFileFrom(function: KSFunctionDeclaration): OutputStream {
return codeGenerator.createNewFile(
dependencies = createDependencyOn(function),
packageName = "",
fileName = "GeneratedHelloWorld"
)
}
// 3️⃣ createDependencyOn() 函数
private fun createDependencyOn(function: KSFunctionDeclaration): Dependencies {
return Dependencies(aggregating = false, function.containingFile!!)
}
}
// 用于把字符串写入 OutputStream 的实用函数
fun OutputStream.write(string: String): Unit {
this.write(string.toByteArray())
}
|
添加 IDE 建议的 import。请确保从 com.google.devtools.ksp.processing 导入 Resolver 和 Dependencies 类。或者,把以下几行复制到 HelloWorldProcessor.kt 的顶部:
1
2
3
4
5
6
7
8
9
10
11
| // processor/src/main/kotlin/HelloWorldProcessor.kt
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSVisitorVoid
import com.google.devtools.ksp.validate
import java.io.OutputStream
|
Import 语句
我们来逐段分析这段代码:
- 1️⃣
process() 函数包含处理器的主要逻辑。它获取所有带有 HelloWorldAnnotation 注解的符号,并为每一个调用 HelloWorldVisitor。
process() 函数返回一个未处理符号的列表,供后续轮次处理。在本示例中,它安全地返回 emptyList()。更多信息请参阅多轮处理。
- 2️⃣ 处理器通过访问者来遍历 KSP 对 Kotlin 抽象语法树(AST)的视图。在
HelloWorldPocessor 类内部,HelloWorldVisitor 类就是访问者。由于 HelloWorldAnnotation 只用在函数上,因此只重写了 visitFunctionDeclaration()。
提示: KSVisitorVoid 是 KSP 提供的访问者类之一,你可以重写并改造它。你也可以通过实现 KSVisitor<D, R> 接口创建自己的访问者。
- 3️⃣
createNewFileFrom() 创建 KSP 生成代码时所用的文件。createDependencyOn() 让输出文件依赖于使用该注解的源文件。
提示: 要进一步了解 KSP 如何创建和管理文件,请查看 CodeGenerator 接口的源代码
- 创建一个
HelloWorldProcessorProvider.kt 文件。在其中声明一个继承自 SymbolProcessorProvider 的 HelloWorldProcessorProvider 类:
1
2
3
4
5
6
7
8
9
10
11
| // processor/src/main/kotlin/HelloWorldProcessorProvider.kt
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
class HelloWorldProcessorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return HelloWorldProcessor(environment.codeGenerator)
}
}
|
- 注册该处理器提供者。在
resources/META-INF/services 目录中创建一个 com.google.devtools.ksp.processing.SymbolProcessorProvider 文件,并添加该提供者的完全限定名:
1
2
3
| ## processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
HelloWorldProcessorProvider
|
使用你的处理器
现在你可以测试你的处理器了。按以下步骤创建一个客户端模块,让你的处理器根据带注解的元素生成代码:
- 在项目根目录创建一个名为
app 的模块。 - 在该模块的
build.gradle(.kts) 文件中:
- 把 KSP 插件添加到
plugins {} 块。 - 把你的处理器和注解添加到
dependencies {} 块。
例如:
Kotlin
1
2
3
4
5
6
7
8
9
10
11
| // app/build.gradle.kts
plugins {
kotlin("jvm")
id("com.google.devtools.ksp")
}
dependencies {
implementation(project(":annotations"))
ksp(project(":processor"))
}
|
Groovy
1
2
3
4
5
6
7
8
9
10
| // app/build.gradle
plugins {
id 'com.google.devtools.ksp'
}
dependencies {
implementation project (':annotations')
ksp project (':processor')
}
|
- 在项目级的
settings.gradle(.kts) 文件中,确保所有子模块都被自动包含:
Kotlin
1
2
3
4
5
| // settings.gradle.kts
include("annotations")
include("app")
include("processor")
|
Groovy
1
2
3
4
5
| // settings.gradle
include 'processor'
include 'annotations'
include 'app'
|
- 在
app 模块中创建一个 Main.kt 文件并添加以下代码:
1
2
3
4
5
6
7
8
| // app/src/main/kotlin/Main.kt
import com.example.annotations.HelloWorldAnnotation
@HelloWorldAnnotation
fun main() {
helloWorld()
}
|
注意: main() 函数调用了 helloWorld(),尽管这个函数还不存在。你的 IDE 会把 helloWorld() 高亮为未定义的引用。这是预期行为:在你构建并运行项目时,KSP 会生成 helloWorld() 函数。
- 运行该程序。你会在控制台中看到
helloWorld() 函数的输出:
1
| Hello world from function generated by KSP
|
KSP 会在 GeneratedHelloWorld.kt 文件中生成代码:
1
| app/build/generated/ksp/main/kotlin/GeneratedHelloWorld.kt
|
查看项目结构
项目最终的文件结构应如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| .
├── app
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── kotlin
│ └── Main.kt
├── annotations
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── kotlin
| └── com
| └── example
| └── annotations
| └── HelloWorldAnnotation.kt
├── processor
│ ├── build.gradle.kts
│ └── src
│ └── main
│ ├── kotlin
│ │ ├── HelloWorldProcessor.kt
│ │ └── HelloWorldProcessorProvider.kt
│ └── resources/META-INF/services
| └── com.google.devtools.ksp.processing.SymbolProcessorProvider
├── build.gradle.kts
└── settings.gradle.kts
|
项目结构
提示: 你可能还有额外的文件和目录。
接下来做什么?