# 在 Kotlin 项目中使用注解处理器
> **TL;DR**
> * 对以下情况, 请使用 [kapt](kapt.html): * 你有一个 Maven 项目. * 你有一个 Gradle 项目, 但需要的 Java 注解处理器还不支持 KSP. [查看支持的库列表](ksp-overview.html#supported-libraries).
>
> * 对以下情况, 请使用 [KSP](ksp-overview.html): * 你有一个 Gradle 项目, 并且需要的 Java 注解处理器支持 KSP. * 你希望创建自己的注解处理器.
注解处理器在编译期间分析你的源代码, 以生成样板代码, 验证用法, 或生成其他构件(artifact). Kotlin 支持两种方式来使用注解处理器:
* [kapt 编译器 plugin](#use-kapt-with-java-annotation-processors), 工作方式是, 从 Kotlin 源代码生成桩(stub)文件, 然后在这些桩(stub)上运行 Java 注解处理器. 这个额外的桩(stub)生成步骤会使构建时间变慢, 同时意味着 kapt 无法理解 Kotlin 特有的构造, 例如 [扩展函数](extensions.html) 或 [null 安全](null-safety.html). kapt 同时支持 Maven 和 Gradle. 推荐用于所有的 Maven 项目, 以及那些使用尚未采用 KSP 的处理器库的 Gradle 项目, 例如 [MapStruct](https://mapstruct.org/).
* [KSP 框架](#use-ksp-in-gradle-projects), 通过 Kotlin 优先的 API 直接读取 Kotlin 源代码, 不需要生成桩. 它能够原生的理解 Kotlin 特有的功能, 构建速度比 kapt 更快. 目前, KSP 只对 Gradle 提供官方支持. 推荐用于编写自己的处理器, 以及与支持 KSP 的库 (例如 [Dagger](https://dagger.dev/)) 配合使用.
## 配合使用 kapt 与 Java 注解处理器
[kapt](kapt.html) 让你能够在 Kotlin 项目中使用既有的 Java 注解处理器, 不需要对处理器本身做任何修改.
下面的示例演示如何使用 [MapStruct](https://mapstruct.org/) 注解处理器. MapStruct 会在编译期间生成 Java Bean 之间类型安全的 mapper 实现.
1. 在你的构建文件中, 应用 `kapt` plugin, 并将 MapStruct 添加到 `dependencies` 部分:
Maven:
```XML
11
1.6.3
org.mapstruct
mapstruct
${mapstruct.version}
org.jetbrains.kotlin
kotlin-maven-plugin
${kotlin.version}
true
kapt
kapt
src/main/kotlin
src/main/java
stubs
org.mapstruct
mapstruct-processor
${mapstruct.version}
```
* 将来自 `kotlin-maven-plugin` 的 `kapt` goal 的执行, 添加到 `compile` 执行 之前.
* 使用 `aptMode` 选项, 配置 [注解处理模式](kapt.html).
Gradle Kotlin:
```KOTLIN
plugins {
kotlin("kapt") version "2.4.20"
}
dependencies {
implementation("org.mapstruct:mapstruct:1.6.3")
kapt("org.mapstruct:mapstruct-processor:1.6.3")
}
```
Gradle Groovy:
```GROOVY
plugins {
id "org.jetbrains.kotlin.kapt" version "2.4.20"
}
dependencies {
implementation "org.mapstruct:mapstruct:1.6.3"
kapt "org.mapstruct:mapstruct-processor:1.6.3"
}
```
2. 定义你的数据类和 mapper 接口:
```KOTLIN
import org.mapstruct.Mapper
import org.mapstruct.factory.Mappers
data class UserDto(val id: Long, val firstName: String, val lastName: String)
data class UserEntity(val id: Long, val firstName: String, val lastName: String)
@Mapper
interface UserMapper {
fun toDto(entity: UserEntity): UserDto
fun toEntity(dto: UserDto): UserEntity
companion object : UserMapper by Mappers.getMapper(UserMapper::class.java)
}
```
3. 构建项目. MapStruct 会在生成的源代码目录中生成 `UserMapperImpl` 类. 使用 `UserMapper` 同伴对象来调用生成的实现:
```KOTLIN
fun main() {
val entity = UserEntity(id = 1L, firstName = "John", lastName = "Doe")
val dto = UserMapper.toDto(entity)
println(dto)
// 输出结果为: UserDto(id=1, firstName=John, lastName=Doe)
}
```
## 在 Gradle 项目中使用 KSP
使用 [KSP](ksp-overview.html), 你可以在 Gradle 项目中使用既有的注解处理器, 也可以创建自己的处理器, 根据源代码中的注解来生成代码.
### 配合使用 KSP 与 Java 注解处理器
对于 Gradle 项目, 请将 KSP 与兼容的注解处理器配合使用. KSP 比 kapt 更快, 并且能够原生理解 Kotlin 特有的功能. 请查看 [已支持 KSP 的库列表](ksp-overview.html#supported-libraries).
下面的示例演示如何使用 [Dagger](https://dagger.dev/), 这是一个编译期间依赖注入框架, 它根据依赖图生成连接代码.
1. 在你的 `build.gradle(.kts)` 文件中, 应用 KSP plugin, 并将 Dagger 添加到 `dependencies` 代码块:
Kotlin:
```KOTLIN
// build.gradle.kts
plugins {
kotlin("jvm") version "2.4.20"
id("com.google.devtools.ksp") version "2.3.10"
}
dependencies {
implementation("com.google.dagger:dagger:2.59.2")
ksp("com.google.dagger:dagger-compiler:2.59.2")
}
```
Groovy:
```GROOVY
// build.gradle
plugins {
id 'org.jetbrains.kotlin.jvm' version '2.4.20'
id 'com.google.devtools.ksp' version '2.3.10'
}
dependencies {
implementation 'com.google.dagger:dagger:2.59.2'
ksp 'com.google.dagger:dagger-compiler:2.59.2'
}
```
> **Tip:**
> 要查找 KSP 的最新版本, 请查看 GitHub 上的 [Releases](https://github.com/google/ksp/releases) 页面.
2. 使用 Dagger 注解, 对你的 Kotlin 类进行标注:
```KOTLIN
import javax.inject.Inject
import javax.inject.Singleton
import dagger.Component
import dagger.Module
import dagger.Provides
@Singleton
class UserRepository @Inject constructor() {
fun getUser(): String = "John Doe"
}
@Module
class AppModule {
@Provides
@Singleton
fun provideUserRepository(): UserRepository = UserRepository()
}
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
fun userRepository(): UserRepository
}
```
3. 构建项目. Dagger 会在 `build/generated/ksp` 目录中生成实现类, 例如 `DaggerAppComponent`. 在你的代码中使用生成的类:
```KOTLIN
fun main() {
val appComponent = DaggerAppComponent.create()
val userRepository = appComponent.userRepository()
println("User: ${userRepository.getUser()}")
// 输出结果为: User: John Doe
}
```
关于 Dagger 对 KSP 的支持, 详情请参见它的 [文档](https://dagger.dev/dev-guide/ksp.html).
### 创建你自己的注解处理器
你可以使用 KSP API 编写自己的注解处理器, 在编译期间生成代码. 一个新的处理器需要 3 个模块:
* 一个 `annotation` 模块, 用于声明自定义注解.
* 一个 `processor` 模块, 用于实现 `SymbolProcessor` 和 `SymbolProcessorProvider` 工厂类. `SymbolProcessor` 包含主逻辑, `SymbolProcessorProvider` 创建处理器, 并在 `META-INF/services/` 路径下注册 provider.
* 一个 `app` 模块, 应用 KSP plugin, 依赖于处理器, 并使用注解.
关于完整的逐步说明, 请参见 [KSP 快速入门](ksp-quickstart.html#create-your-own-processor).
## 下一步做什么?
* [了解 kapt 配置](kapt.html)
* [开始使用 KSP](ksp-quickstart.html)
* [了解如何从 kapt 迁移到 KSP](ksp-kapt-migration.html)