Playing with Spring Boot, Vaadin and Kotlin
Nicolas FrankelIt’s no mystery that I’m a fan of both Spring Boot and Vaadin. When the Spring Boot Vaadin add-on became GA, I was ecstatic. Lately, I became interested in Kotlin, a JVM-based language offered by JetBrains. Thus, I wanted to check how I could develop a small Spring Boot Vaadin demo app in Kotlin – and learn something in the process. Here are my discoveries, in no particular order.
Spring needs non-final stuff
It seems Spring needs @Configuration
classes and @Bean
methods to be non-final. As my previous Spring projects were in Java, I never became aware of that because I never use the final
keyword. However, Kotlin classes and methods are final by default: hence, you have to use the open keyword in Kotlin.
@Configuration
open class AppConfiguration {
@Bean
@UIScope
open fun mainScreen() = MainScreen()
}
No main class
Spring Boot applications require a class with a public static void main(String... args)
method to reference a class annotated with @SpringBootApplication
. In general, those two classes are the same.
Kotlin has no concept of such static methods, but offers pure functions and objects. I tried to be creative by having an annotated object referenced by a pure function, both in the same file.
@SpringBootApplication
open class BootVaadinKotlinDemoApplication
fun main(vararg args: String) {
SpringApplication.run(arrayOf(BootVaadinKotlinDemoApplication::class.java), args)
}
Different entry-point reference
Since the main function is not attached to a class, there’s no main class to reference to launch inside the IDE. Yet, Kotlin creates a .class with the same name as the file name suffixed with Kt
.
My file is named BootVaadinKotlinDemoApplication.kt
, hence the generated class name is BootVaadinKotlinDemoApplicationKt.class
. This is the class to reference to launch the application in the IDE. Note that there’s no need to bother about that when using mvn spring-boot:run
on the command-line, as Spring Boot seems to scan for the main method.
Short and readable bean definition
Java syntax is seen as verbose. I don’t think it’s a big issue when its redundancy is very low compared to the amount of useful code. However, in some cases, even I have to admit it’s a lot of ceremony for not much. When of such case is defining beans with the Java syntax:
@Bean @UIScope
public MainScreen mainScreen() {
return new MainScreen();
}
Kotlin cuts through all of the ceremony to keep only the meat:
- No semicolon required
- No new keyword
- Block replaced with an equal sign since the body consists of a single expression
- No return keyword required as there’s no block
- No return type required as it can easily be inferred
@Bean @UIScope
fun mainScreen() = MainScreen()
Spring configuration files are generally quite long and hard to read. Kotlin makes them much shorter, without sacrificing readability.
The init block is your friend
In Java, the constructor is used for different operations:
- storing arguments into attributes
- passing arguments to the super constructor
- other initialization code
The first operation is a no-brainer because attributes are part of the class signature in Kotlin. Likewise, calling the super constructor is handled by the class signature. The rest of the initialization code is not part of the class signature and should be part of an init
block. Most applications do not this part, but Vaadin needs to setup layout and related stuff.
class MainScreenPresenter(tablePresenter: TablePresenter,
buttonPresenter: NotificationButtonPresenter,
view: MainScreen, eventBus: EventBus) : Presenter<MainScreen>(view, eventBus) {
init {
view.setComponents(tablePresenter.view, buttonPresenter.view)
}
}
Use the apply method
Kotlin has a standard library offering small dedicated functions. One of them is apply, defined as inline fun T.apply(f: T.() -> Unit): T (source)
. It’s an extension function, meaning every type will have it as soon as it’s imported into scope. This function requires an argument that is a function and that returns nothing. Inside this function, the object that has been apply-ed is accessible as this
(and this
is implicit, as in standard Java code). It allows code like this:
VerticalLayout(button, table).apply {
setSpacing(true)
setMargin(true)
setSizeFull()
}
Factor view and presenter into same file
Kotlin makes code extremely small, thus some files might be only a line long (not counting import). Opening different files to check related classes is useless. Packages are a way to organize your code; I think files might be another way in Kotlin. For example, Vaadin views and presenters can be put into the same file.
class NotificationButton: Button("Click me")
class NotificationButtonPresenter(view: NotificationButton, eventBus: EventBus): Presenter<NotificationButton>(view, eventBus) { ... }
Lambdas make great listeners
As of Java 8, single-method interfaces implemented as anonymous inner classes can be replaced with lambdas. Kotlin offers the same feature plus:
- it allows to omit parentheses if the lambda is the only argument
- if the lambda has a single argument, its default name is
it
and it doesn’t need to be declared
Both make for a very readable syntax when used in conjunction with the Vaadin API:
view.addValueChangeListener {
val rowId = it.property.value
val rowItem = view.containerDataSource.getItem(rowId)
eventBus.publish(SESSION, rowItem)
}
Note: still, more complex logic should be put into its own function.