# KSP 入门 在这篇指南中, 你将学习: * 如何向你的项目添加基于 KSP 的注解处理器. * 如何使用 KSP API 创建你自己的注解处理器. * 在哪里查找处理器生成的代码. ## 向你的项目添加基于 KSP 的处理器 要在你的项目中使用外部处理器, 请在 `build.gradle(.kts)` 文件的 [plugins {} 代码块](https://docs.gradle.org/current/userguide/plugins.html#sec:plugins_block) 中添加 KSP. 如果只有某个特定模块需要这个处理器, 请改为在该模块的 `build.gradle(.kts)` 文件中添加: Kotlin: ```KOTLIN // build.gradle.kts plugins { kotlin("jvm") version "2.4.20" id("com.google.devtools.ksp") version "2.3.10" } ``` Groovy: ```GROOVY // build.gradle plugins { id 'org.jetbrains.kotlin.jvm' version '2.4.20' id 'com.google.devtools.ksp' version '2.3.10' } ``` > **Tip:** > 要查找 KSP 的最新版本, 请查看 GitHub [Releases](https://github.com/google/ksp/releases). 在最顶层的 `dependencies {}` 代码块中, 添加你想要使用的处理器. 这个示例使用 [Moshi](https://github.com/square/moshi?tab=readme-ov-file#codegen), 其他处理器的添加方式也是一样的: Kotlin: ```KOTLIN // build.gradle.kts dependencies { ksp("com.squareup.moshi:moshi-kotlin-codegen:1.15.2") } ``` Groovy: ```GROOVY // build.gradle dependencies { ksp 'com.squareup.moshi:moshi-kotlin-codegen:1.15.2' } ``` `ksp(...)` 配置只会将处理器应用于应用程序的源代码. 如果要处理测试代码, 请使用 `kspTest(...)` 配置添加处理器. > **Note:** > `ksp(...)` 配置只能用于单平台项目. 关于如何对各个 Kotlin Multiplatform 编译目标和编译任务配置处理器, 详情请参见 [在 Kotlin Multiplatform 中使用 KSP](ksp-multiplatform.html). ## 创建你自己的处理器 按照以下步骤, 你将创建一个简单的注解处理器, 它会生成一个 `helloWorld()` 函数. 虽然在实际中用处不大, 但它演示了创建你自己的处理器和注解的基本方法. ### 向项目添加 KSP 创建一个新的 Kotlin 项目, 并添加 KSP plugin: 1. 在 IntelliJ IDEA 中, 选择 File | New | Project. 2. 在左侧列表中, 选择 Kotlin. 3. 选择 Gradle 作为构建系统, 然后点击 Create. ![创建新项目](images/ksp-new-project.png) 4. 向 `build.gradle(.kts)` 文件添加 KSP plugin: Kotlin: ```KOTLIN // build.gradle.kts plugins { kotlin("jvm") version "2.4.20" id("com.google.devtools.ksp") version "2.3.10" apply false } ``` Groovy: ```GROOVY // build.gradle plugins { id 'org.jetbrains.kotlin.jvm' version '2.4.20' id 'com.google.devtools.ksp' version '2.3.10' apply false } ``` ### 创建注解 在项目的根目录创建一个新模块, 并声明一个注解: 1. 选择 File | New | Module. 2. 在左侧列表中, 选择 Kotlin. 3. 填写以下项目, 然后点击 create: * Name: annotations * Build system: Gradle ![创建新模块](images/ksp-new-module.png) 4. 在这个模块中, 创建 `HelloWorldAnnotation.kt` 文件, 并声明一个注解, 名为 `HelloWorldAnnotation`: ```KOTLIN // annotations/src/main/kotlin/com/example/annotations/HelloWorldAnnotation.kt package com.example.annotations annotation class HelloWorldAnnotation ``` ### 创建并注册处理器 1. 在项目的根目录创建另一个模块, 名为 processor. 2. 在这个模块的 `build.gradle(.kts)` 文件中, 将 KSP API 和你声明的注解添加为依赖项: Kotlin: ```KOTLIN // processor/build.gradle.kts plugins { kotlin("jvm") } dependencies { implementation(project(":annotations")) implementation("com.google.devtools.ksp:symbol-processing-api:2.3.6") } ``` Groovy: ```GROOVY // processor/build.gradle plugins { id 'org.jetbrains.kotlin.jvm' } dependencies { implementation project ':annotations' implementation 'com.google.devtools.ksp:symbol-processing-api:2.3.6' } ``` 3. 在 `processor` 模块中, 创建一个新的 `HelloWorldProcessor.kt` 文件, 添加以下代码: ```KOTLIN // processor/src/main/kotlin/HelloWorldProcessor.kt class HelloWorldProcessor(val codeGenerator: CodeGenerator) : SymbolProcessor { // 1️⃣ process() 函数 override fun process(resolver: Resolver): List { resolver .getSymbolsWithAnnotation("com.example.annotations.HelloWorldAnnotation") .filter { it.validate() } .filterIsInstance() .forEach { it.accept(HelloWorldVisitor(), Unit) } return emptyList() } // 2️⃣ 访问器(Visitor) 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` 的顶部: ```KOTLIN // 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 ``` 我们来逐步了解这段代码: * 1️⃣ `process()` 函数包含处理器的主逻辑. 它获取所有带有 `HelloWorldAnnotation` 注解的符号, 并为每个符号调用 `HelloWorldVisitor`. `process()` 函数返回未处理的符号列表, 以便在后续轮次中进行处理. 在这个示例中, 它安全地返回 `emptyList()`. 详情请参见 [多轮处理](ksp-multi-round.html). * 2️⃣ 处理器使用访问器(Visitor)遍历 KSP 对 Kotlin 抽象语法树 (Abstract Syntax Tree, AST) 的视图. 在 `HelloWorldPocessor` 类中, `HelloWorldVisitor` 类是访问器(Visitor). 由于 `HelloWorldAnnotation` 只用于函数, 因此只覆盖了 `visitFunctionDeclaration()`. > **Tip:** > `KSVisitorVoid` 是 KSP 提供的访问器类之一, 你可以覆盖并调整它. 你也可以实现 [KSVisitor<D, R> 接口](https://github.com/google/ksp/blob/main/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSVisitor.kt), 创建你自己的访问器. * 3️⃣ `createNewFileFrom()` 创建 KSP 生成代码的文件. `createDependencyOn()` 使输出的文件依赖于使用注解的源代码文件. > **Tip:** > 关于 KSP 如何创建和管理文件的信息, 详情请参见 [CodeGenerator 接口](https://github.com/google/ksp/blob/main/api/src/main/kotlin/com/google/devtools/ksp/processing/CodeGenerator.kt) 的源代码. 4. 创建 `HelloWorldProcessorProvider.kt` 文件. 在这个文件中, 声明一个 `HelloWorldProcessorProvider` 类, 继承自 `SymbolProcessorProvider`: ```KOTLIN // 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) } } ``` 5. 注册处理器 Provider. 在 `resources/META-INF/services` 目录中, 创建 `com.google.devtools.ksp.processing.SymbolProcessorProvider` 文件, 并添加 Provider 的完全限定名称: ```TEXT ## processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider HelloWorldProcessorProvider ``` ### 使用你的处理器 现在你已经准备好, 可以测试你的处理器了. 按照以下步骤创建客户端模块, 让你的处理器根据带有注解的元素生成代码: 1. 在项目根目录创建一个模块, 名为 `app`. 2. 在这个模块的 `build.gradle(.kts)` 文件中: * 在 `plugins {}` 代码块中, 添加 KSP 插件. * 在 `dependencies {}` 代码块中, 添加你的处理器和注解. 例如: Kotlin: ```KOTLIN // app/build.gradle.kts plugins { kotlin("jvm") id("com.google.devtools.ksp") } dependencies { implementation(project(":annotations")) ksp(project(":processor")) } ``` Groovy: ```GROOVY // app/build.gradle plugins { id 'com.google.devtools.ksp' } dependencies { implementation project (':annotations') ksp project (':processor') } ``` 3. 在项目级的 `settings.gradle(.kts)` 文件中, 确认所有子模块都已自动包含: Kotlin: ```KOTLIN // settings.gradle.kts include("annotations") include("app") include("processor") ``` Groovy: ```GROOVY // settings.gradle include 'processor' include 'annotations' include 'app' ``` 4. 在 `app` 模块中, 创建 `Main.kt` 文件, 添加以下代码: ```KOTLIN // app/src/main/kotlin/Main.kt import com.example.annotations.HelloWorldAnnotation @HelloWorldAnnotation fun main() { helloWorld() } ``` > **Note:** > `main()` 函数调用了 `helloWorld()`, 尽管这个函数目前还不存在. 你的 IDE 会将 `helloWorld()` 高亮显示为未定义的引用. 这是预料中的行为: KSP 会在你构建和运行项目时生成 `helloWorld()` 函数. 5. 运行程序. 你会在控制台中看到 `helloWorld()` 函数的输出: ```TEXT Hello world from function generated by KSP ``` KSP 在 `GeneratedHelloWorld.kt` 文件中生成代码: ```TEXT app/build/generated/ksp/main/kotlin/GeneratedHelloWorld.kt ``` ### 查看项目结构 你的项目的最终文件结构应该如下所示: ```TEXT . ├── 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 ``` > **Tip:** > 你可能还有其他文件和目录. ## 下一步做什么? * 在 [KSP 代码仓库](https://github.com/google/ksp/tree/main/examples/hello-world) 中, 查看这个示例的完整代码. * 在 [KSP 代码仓库](https://github.com/google/ksp/tree/main/examples) 中, 查看更加复杂的实际示例. * 查看 [KSP 支持的库](ksp-overview.html#supported-libraries) 列表.