Use annotation processors in Kotlin projects
Annotation processors analyze your source code at compile time to generate boilerplate code, validate usage, or produce other artifacts. Kotlin supports two ways to work with annotation processors:
The kapt compiler plugin works by generating stub files from Kotlin source code and then running the Java annotation processors on those stubs. This extra stub-generation step makes the build time slower and means kapt can't understand Kotlin-specific constructs, such as extension functions or null safety.
kapt supports both Maven and Gradle. It's recommended for all Maven projects and for Gradle projects with processor libraries that haven't yet adopted KSP, such as MapStruct.
The KSP framework reads Kotlin source code directly through a Kotlin-first API, without generating stubs. It understands Kotlin-specific features natively and runs builds faster than kapt.
Currently, KSP has official support only for Gradle. It's recommended for writing your own processors and working with KSP-compatible libraries like Dagger.
Use kapt with Java annotation processors
kapt lets you use existing Java annotation processors in Kotlin projects without any changes to the processors themselves.
The example below shows how to use the MapStruct annotation processor, which generates type-safe mapper implementations between Java beans at compile time.
In your build file, apply the
kaptplugin and add MapStruct to thedependenciessection:<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>Add the execution of the
kaptgoal fromkotlin-maven-pluginbefore thecompileexecution.Configure the level of annotation processing using the
aptModeoption.
plugins { kotlin("kapt") version "2.3.21" } dependencies { implementation("org.mapstruct:mapstruct:1.6.3") kapt("org.mapstruct:mapstruct-processor:1.6.3") }plugins { id "org.jetbrains.kotlin.kapt" version "2.3.21" } dependencies { implementation "org.mapstruct:mapstruct:1.6.3" kapt "org.mapstruct:mapstruct-processor:1.6.3" }Define your data classes and a mapper interface:
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) }Build the project. MapStruct generates the
UserMapperImplclass in the generated sources' directory. Use theUserMappercompanion object to call the generated implementation: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) }
Use KSP in Gradle projects
With KSP, you can use existing annotation processors in Gradle projects and create your own processors that generate code based on annotations in your source code.
Use KSP with Java annotation processors
For Gradle projects, use KSP with the compatible annotation processors. KSP is faster than kapt and can understand Kotlin-specific features natively. See the list of libraries that already support KSP.
The example below shows how to use Dagger, a compile-time dependency injection framework that generates the wiring code for your dependency graph.
In your
build.gradle(.kts)file, apply the KSP plugin and add Dagger to thedependenciesblock:// build.gradle.kts plugins { kotlin("jvm") version "2.3.21" id("com.google.devtools.ksp") version "2.3.7" } 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.3.21' id 'com.google.devtools.ksp' version '2.3.7' } dependencies { implementation 'com.google.dagger:dagger:2.59.2' ksp 'com.google.dagger:dagger-compiler:2.59.2' }Annotate your Kotlin classes with Dagger annotations:
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 }Build the project. Dagger generates implementation classes, such as
DaggerAppComponentin thebuild/generated/kspdirectory. Use the generated class in your code:fun main() { val appComponent = DaggerAppComponent.create() val userRepository = appComponent.userRepository() println("User: ${userRepository.getUser()}") // User: John Doe }
For more information on Dagger support for KSP, see its documentation.
Create your own annotation processor
You can use the KSP API to write your own annotation processors that generate code at compile time. A new processor requires three modules:
An
annotationmodule that declares the custom annotation.A
processormodule that implements theSymbolProcessorandSymbolProcessorProviderfactories.SymbolProcessorcontains the main logic, whileSymbolProcessorProvidercreates the processor and registers the provider in theMETA-INF/services/path.An
appmodule that applies the KSP plugin, depends on the processor, and uses the annotation.
For complete step-by-step instructions, see the KSP quickstart.