I have an interface for creating Dto/Domain mappers using Konvert, which uses KSP.
@Konverter
interface SimpleNoteMapper {
fun toDto(note: SimpleNote): SimpleNoteDto
fun toDomain(note: SimpleNoteDto): SimpleNote
}
The generated class is:
public object SimpleNoteMapperImpl : SimpleNoteMapper {
@GeneratedKonverter(priority = 5_000)
override fun toDto(note: SimpleNote): SimpleNoteDto = SimpleNoteDto(
id = note.id,
name = note.name
)
@GeneratedKonverter(priority = 5_000)
override fun toDomain(note: SimpleNoteDto): SimpleNote = SimpleNote(
id = note.id,
name = note.name
)
}
I want to inject the generated class using @Autowired
into my Controller
.
@RestController
class SimpleNotesController {
@Autowired
lateinit var simpleNoteMapper: SimpleNoteMapper
[...]
}
But the error I get, is:
Field simpleNoteMapper in com.project.example.simplenotes.api.controller.SimpleNotesController required a bean of type 'com.project.example.simplenotes.api.mapper.SimpleNoteMapper' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.project.example.simplenotes.api.mapper.SimpleNoteMapper' in your configuration.
I understand that I'd probably need to be able to add an annotation to that generated class, right? But that's not possible because it's generated. I can solve this by creating a Configuration for this mapper:
@Configuration
class MapperConfiguration {
@Bean
fun simpleNoteMapper(): SimpleNoteMapper {
return SimpleNoteMapperImpl
}
}
The application works, but I am unsure whether there isn't some way to remove this boilerplate, because I don't feel it's a good idea to create a configuration for each generated class. So I am looking for something that will simplify this to one annotation or to one class that would be able to cover all the Mappers. Thanks for reading.
While it it seems there is some Spring behavior that automatically matches interfaces to implementations (e.g. InterfaceA
matches to InterfaceAImpl
), it doesn't work in this case, because Konvert is using object
instead of class
in the generated code (possibly).
Fortunately, Konvert has plugins, that solve this problem.
I had to add:
implementation("io.mcarle:konvert-spring-annotations:2.4.0")
ksp("io.mcarle:konvert-spring-injector:2.4.0")
to my build.gradle.kts
file and then add the @KComponent
annotation to the mapper interface:
@Konverter
@KComponent
interface SimpleNoteMapper {
fun toDto(note: SimpleNote): SimpleNoteDto
fun toDomain(note: SimpleNoteDto): SimpleNote
}
This generates a Spring-aware Mapper that can be injected without additional boilerplate.