6.1.5 在 Kotlin 项目中使用注解处理器
原文链接: https://kotlinlang.org/docs/jvm-annotation-processors.html
6.1.5 在 Kotlin 项目中使用注解处理器
- 在以下情况使用 kapt:* 你有一个 Maven 项目。* 你有一个 Gradle 项目,但所需的 Java 注解处理器尚不支持 KSP。查看受支持库的列表。* 在以下情况使用 KSP:* 你有一个 Gradle 项目,并且所需的 Java 注解处理器支持 KSP。* 你想创建自己的注解处理器。
注解处理器在编译期分析你的源代码,以生成样板代码、校验用法或产出其他构件。Kotlin 支持两种使用注解处理器的方式:
- kapt 编译器插件的工作方式是从 Kotlin 源代码生成桩文件,然后对这些桩文件运行 Java 注解处理器。这个额外的生成桩步骤会让构建更慢,也意味着 kapt 无法理解 Kotlin 特有的结构,例如扩展函数或空安全。
kapt 同时支持 Maven 和 Gradle。建议所有 Maven 项目以及使用尚未采用 KSP 的处理器库(例如 MapStruct)的 Gradle 项目都使用它。
- KSP 框架通过 Kotlin 优先的 API 直接读取 Kotlin 源代码,而无需生成桩文件。它原生理解 Kotlin 特有特性,构建速度比 kapt 更快。
目前,KSP 只对 Gradle 提供官方支持。建议在编写自己的处理器以及使用 KSP 兼容库(例如 Dagger)时使用它。
用 kapt 配合 Java 注解处理器
kapt 让你可以在 Kotlin 项目中使用现有的 Java 注解处理器,而无需对处理器本身做任何修改。
下面的例子演示如何使用 MapStruct 注解处理器,它在编译期生成 Java bean 之间的类型安全映射实现。
- 在构建文件中应用
kapt 插件,并把 MapStruct 添加到 dependencies 部分:
Maven
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
| <properties>
<kotlin.compiler.jvmTarget>11</kotlin.compiler.jvmTarget>
<mapstruct.version>1.6.3</mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
</dependencies>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<id>kapt</id>
<goals>
<goal>kapt</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>src/main/kotlin</sourceDir>
<sourceDir>src/main/java</sourceDir>
</sourceDirs>
<aptMode>stubs</aptMode>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</execution>
</executions>
</plugin>
|
- 把
kotlin-maven-plugin 中 kapt goal 的执行添加在 compile 执行之前。 - 使用
aptMode 选项配置注解处理的模式。
Gradle Kotlin
1
2
3
4
5
6
7
8
| 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
1
2
3
4
5
6
7
8
| 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"
}
|
- 定义你的数据类和映射器接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| 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)
}
|
- 构建项目。MapStruct 会在生成的源文件目录中生成
UserMapperImpl 类。使用 UserMapper 伴生对象调用生成的实现:
1
2
3
4
5
6
| 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,你可以在 Gradle 项目中使用现有的注解处理器,并创建自己的、根据源代码中注解生成代码的处理器。
用 KSP 配合 Java 注解处理器
对于 Gradle 项目,请把 KSP 与兼容的注解处理器一起使用。KSP 比 kapt 更快,并且能原生理解 Kotlin 特有特性。请参阅已支持 KSP 的库列表。
下面的例子演示如何使用 Dagger,这是一个编译期依赖注入框架,会为你的依赖图生成装配代码。
- 在你的
build.gradle(.kts) 文件中应用 KSP 插件,并把 Dagger 添加到 dependencies 块:
Kotlin
1
2
3
4
5
6
7
8
9
10
11
| // 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
1
2
3
4
5
6
7
8
9
10
11
| // 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'
}
|
提示: 要查找 KSP 的最新版本,请查看 GitHub 的 Releases 页面。
- 用 Dagger 注解标注你的 Kotlin 类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| 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
}
|
- 构建项目。Dagger 会生成实现类,例如
build/generated/ksp 目录中的 DaggerAppComponent。在代码中使用生成的类:
1
2
3
4
5
6
| fun main() {
val appComponent = DaggerAppComponent.create()
val userRepository = appComponent.userRepository()
println("User: ${userRepository.getUser()}")
// User: John Doe
}
|
关于 Dagger 对 KSP 支持的更多信息,请参阅其文档。
创建你自己的注解处理器
你可以使用 KSP API 编写自己的注解处理器,在编译期生成代码。一个新的处理器需要三个模块:
- 一个
annotation 模块,用于声明自定义注解。 - 一个
processor 模块,用于实现 SymbolProcessor 和 SymbolProcessorProvider 工厂。SymbolProcessor 包含主要逻辑,而 SymbolProcessorProvider 负责创建处理器并在 META-INF/services/ 路径中注册提供者。 - 一个
app 模块,应用 KSP 插件、依赖于该处理器并使用该注解。
完整的分步说明请参阅 KSP 快速入门。
接下来学什么