[FIXED] MapStruct not injected in Kotlin project

Issue

I am trying to create a project with current Kotlin, MapStruct and Java using Spring-Boot folowing some online examples, as I am new to MapStruct, however I am not able to inject the mapper into my service. Both Idea and Gradle (in build task test) complain that no bean has been found (UnsatisfiedDependencyException). Googling didn’t help. What am I missing?

MWE:

build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "2.7.4"
    id("io.spring.dependency-management") version "1.0.14.RELEASE"
    kotlin("jvm") version "1.7.20"
    kotlin("plugin.spring") version "1.7.20"
    kotlin("plugin.jpa") version "1.7.20"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17

configurations {
    compileOnly {
        extendsFrom(configurations.annotationProcessor.get())
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    implementation("org.mapstruct:mapstruct:1.5.3.Final")
    annotationProcessor("org.mapstruct:mapstruct-processor:1.5.3.Final")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "17"
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

DemoAppllication.kt

package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}

App.kt

package com.example.demo

import org.mapstruct.Mapper
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import org.springframework.stereotype.Service
import javax.persistence.*

@Entity
@Table(name = "items")
class Item(@Id var id: Int = 0)


data class ItemDto(val id: Int)


@Repository
interface ItemRepo : JpaRepository<Item, Int>


@Mapper(componentModel = "spring")
interface ItemMapper {
    fun entToDto(item: Item) : ItemDto
    fun entsToDtos(items: List<Item>) : List<ItemDto>
    fun dtoToEnt(itemDto: ItemDto) : Item
}

@Service
class Srvc(private val itemMapper: ItemMapper, // XXX: no bean found for this one
           private val repo: ItemRepo)
{
    fun items() = itemMapper.entsToDtos(repo.findAll())
}

// controller skipped

Solution

Kapt is in maintenance mode! See example: https://github.com/mapstruct/mapstruct-examples/tree/main/mapstruct-kotlin

plugin {
    // ...
    kotlin("kapt")
    // ...
}
dependencies {
    // ...
    kapt("org.mapstruct:mapstruct-processor:$version")
    implementation("org.mapstruct:mapstruct:$version")
    // ...
}

EDIT (by mpts.cz — for future me or anyone as new to Mapstruct and/or Gradle as I am):

  1. use plugin kotlin("kapt")
  2. replace annotationProcessor("org.mapstruct:mapstruct-processor:$version")
    with kapt("org.mapstruct:mapstruct-processor:$version")
  3. put the previous line before implementation("org.mapstruct:mapstruct:$version")

With these changes all seems to work smoothly. Thanks Numichi!

Answered By – Numichi

Answer Checked By – Clifford M. (FixeMe Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *