1

I am quite new to Java & Spring and this is my first Kotlin app so my problem could be Layer-8 (me). But I really do not know what I make wrong. According to this tutorial (https://www.baeldung.com/kotlin-mongodb-spring-webflux) this should work.

My general app setup is Controller --> Service --> Repository (I did not add the controller since this is not the issue). My problem is that the Repository Bean can not be created by the factory.

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'driveController' defined in file [/Users/sburger/Documents/Dev/Gravity/App Framework/bouncr/build/classes/kotlin/main/de/example/drive/controller/DriveController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'driveService' defined in file [/Users/sburger/Documents/Dev/Gravity/App Framework/bouncr/build/classes/kotlin/main/de/example/drive/service/DriveService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'IBookingsRepository': Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: kotlin/reflect/full/KClasses
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1325) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1171) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:67) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at de.example.AppKt.main(App.kt:14) ~[main/:na]
    at de.example.AppKt.main(App.kt) ~[main/:na]

App.kt

@SpringBootApplication(exclude = [MongoReactiveDataAutoConfiguration::class])
open class App

fun main() {
    runApplication<App>()
}

MongoConfig.kt

@Configuration
@EnableReactiveMongoRepositories(basePackageClasses = [IBookingsRepository::class])
open class MongoConfig : AbstractReactiveMongoConfiguration() {
    override fun getDatabaseName() = "exampledb"

    override fun reactiveMongoClient() = mongoClient()

    @Bean
    override fun reactiveMongoTemplate() = ReactiveMongoTemplate(mongoClient(), databaseName)

    @Bean
    open fun mongoClient(): MongoClient = MongoClients.create()

}

I had to mark the class and fun as "open". Without it the build did not work. This is already different to the tutorial.

DriveService.kt

interface IDriveService {
    fun getBookings(userId: String) : Flux<Booking>
}

@Service
class DriveService @Autowired constructor(private val bookingsRepository: IBookingsRepository) :
    IBookingService {
    override fun getBookings(userId: String): Flux<Booking> {
       return bookingsRepository.findAll()
    }
}

The "findAll()" method is inherited from ReactiveMongoRepository

BookingsRepository.kt

interface IBookingsRepository : ReactiveMongoRepository<Booking, String>

This interface has obviously no init function, but in the tutorial it also has none. So I assume it should work as it is?

Has someone a hint what could be wrong with this setup?

  • Why did you exclude the auto-configuration? Spring Boot will auto configure all of that for you. – M. Deinum Mar 18 '19 at 13:42
  • Also please post the full exception instead of just a snippet. – M. Deinum Mar 18 '19 at 13:55
  • The stacktrace is pretty clear. At least `java.lang.NoClassDefFoundError: kotlin/reflect/full/KClasses` should give you a hint on what is wrong. – M. Deinum Mar 18 '19 at 14:35
  • @M.Deinum Thank you for your quick response. Actually I am not sure. I checked the source code of this tutorial (https://github.com/eugenp/tutorials/blob/master/spring-5-data-reactive/src/main/kotlin/com/baeldung/Application.kt) an they have done it as well. If I remove it I get this error The bean 'reactiveMongoTemplate', defined in class path resource [.../mongo/MongoReactiveDataAutoConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [.../config/MongoConfig.class] and overriding is disabled. – Steffen Burger Mar 18 '19 at 14:42
  • Ok, I had to add "kotlin-reflect" as a dependency. I still have a problem but at least I am a step further. Thanks @M.Deinum – Steffen Burger Mar 18 '19 at 14:52
  • You shouldn't use `MongoConfig` but use auto-config. – M. Deinum Mar 19 '19 at 06:41

1 Answers1

1

Several issues here:

  1. You need to add a dependency to org.jetbrains.kotlin:kotlin-reflect. If you generate a Kotlin project at https://start.spring.io/, it should be added automatically.

  2. That Baeldung tutorial is bad. They exclude auto-configuration for no reason, or at least not explaining why. Spring Boot is all about Convention Over Configuration so the default should be to use as much of the auto-configuration as possible.

  3. As you noted in your comments, not excluding auto-configuration causes an exception. This is due to a bug in Spring Data MongoDb for which ticket DATAMONGO-2355 was recently opened as a result of this question.

  4. The proper solution in this case however, is not to exclude auto-configuration, but to make even more use of it. There is no reason to extend AbstractReactiveMongoConfiguration here. As is explained in this answer, you can configure the DB name through a property and leave all else auto-configured. Should you need to extend AbstractReactiveMongoConfiguration for some reason, until the bug is fixed you can override the reactiveMongoTemplate() method to narrow the return type to ReactiveMongoTemplate as explained in this answer.

herman
  • 11,740
  • 5
  • 47
  • 58