在 Kotlin 项目中使用注解处理器
注解处理器在编译期间分析你的源代码, 以生成样板代码, 验证用法, 或生成其他构件(artifact). Kotlin 支持两种方式来使用注解处理器:
kapt 编译器 plugin, 工作方式是, 从 Kotlin 源代码生成桩(stub)文件, 然后在这些桩(stub)上运行 Java 注解处理器. 这个额外的桩(stub)生成步骤会使构建时间变慢, 同时意味着 kapt 无法理解 Kotlin 特有的构造, 例如 扩展函数 或 null 安全.
kapt 同时支持 Maven 和 Gradle. 推荐用于所有的 Maven 项目, 以及那些使用尚未采用 KSP 的处理器库的 Gradle 项目, 例如 MapStruct.
KSP 框架, 通过 Kotlin 优先的 API 直接读取 Kotlin 源代码, 不需要生成桩. 它能够原生的理解 Kotlin 特有的功能, 构建速度比 kapt 更快.
目前, KSP 只对 Gradle 提供官方支持. 推荐用于编写自己的处理器, 以及与支持 KSP 的库 (例如 Dagger) 配合使用.
配合使用 kapt 与 Java 注解处理器
kapt 让你能够在 Kotlin 项目中使用既有的 Java 注解处理器, 不需要对处理器本身做任何修改.
下面的示例演示如何使用 MapStruct 注解处理器. MapStruct 会在编译期间生成 Java Bean 之间类型安全的 mapper 实现.
在你的构建文件中, 应用
kaptplugin, 并将 MapStruct 添加到dependencies部分:<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的kaptgoal 的执行, 添加到compile执行 之前.使用
aptMode选项, 配置 注解处理级别.
plugins { kotlin("kapt") version "2.4.0" } dependencies { implementation("org.mapstruct:mapstruct:1.6.3") kapt("org.mapstruct:mapstruct-processor:1.6.3") }plugins { id "org.jetbrains.kotlin.kapt" version "2.4.0" } dependencies { implementation "org.mapstruct:mapstruct:1.6.3" kapt "org.mapstruct:mapstruct-processor:1.6.3" }定义你的数据类和 mapper 接口:
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同伴对象来调用生成的实现: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 plugin, 并将 Dagger 添加到dependencies代码块:// build.gradle.kts plugins { kotlin("jvm") version "2.4.0" id("com.google.devtools.ksp") version "2.3.9" } dependencies { implementation("com.google.dagger:dagger:2.59.2") ksp("com.google.dagger:dagger-compiler:2.59.2") }// build.gradle plugins { id 'org.jetbrains.kotlin.jvm' version '2.4.0' id 'com.google.devtools.ksp' version '2.3.9' } dependencies { implementation 'com.google.dagger:dagger:2.59.2' ksp 'com.google.dagger:dagger-compiler:2.59.2' }使用 Dagger 注解, 对你的 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 }构建项目. Dagger 会在
build/generated/ksp目录中生成实现类, 例如DaggerAppComponent. 在你的代码中使用生成的类:fun main() { val appComponent = DaggerAppComponent.create() val userRepository = appComponent.userRepository() println("User: ${userRepository.getUser()}") // 输出结果为: User: John Doe }
关于 Dagger 对 KSP 的支持, 详情请参见它的 文档.
创建你自己的注解处理器
你可以使用 KSP API 编写自己的注解处理器, 在编译期间生成代码. 一个新的处理器需要 3 个模块:
一个
annotation模块, 用于声明自定义注解.一个
processor模块, 用于实现SymbolProcessor和SymbolProcessorProvider工厂类.SymbolProcessor包含主逻辑,SymbolProcessorProvider创建处理器, 并在META-INF/services/路径下注册 provider.一个
app模块, 用于应用 KSP plugin, 依赖于处理器, 并使用注解.
关于完整的逐步说明, 请参见 KSP 快速入门.