Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Spring Boot + Kotlin

Spring Boot + Kotlin

Writing a simple micro-service with Spring Boot and Kotlin

More Decks by St. Petersburg Kotlin User Group

Other Decks in Programming

Transcript

  1. Simplest Spring Boot App dependencies { compile "org.springframework.boot:spring-boot-starter-web:1.5.9.RELEASE" } @SpringBootApplication

    public class MyFabulousApplication { public static void main(String[] args) { new SpringApplicationBuilder(MyFabulousApplication.class).run(args); } } 5
  2. Simplest Spring Boot App dependencies { compile "org.springframework.boot:spring-boot-starter-web:1.5.9.RELEASE" } @SpringBootApplication

    @RestController public class MyFabulousApplication { public static void main(String[] args) { new SpringApplicationBuilder(MyFabulousApplication.class).run(args); } @RequestMapping("/anything") private Response anything() { return new Response("anything is here"); } class Response { private final String message; Response(String message) { this.message = message; } public String getMessage() { return message; } } } 6
  3. ? 7

  4. -> Kotlin? dependencies { // ... Idea will do the

    thing } @SpringBootApplication @RestController class MyFabulousApplication { @RequestMapping("/anything") private fun anything(): Response { return Response("anything is here") } internal inner class Response(val message: String) companion object { @JvmStatic fun main(args: Array<String>) { SpringApplicationBuilder(MyFabulousApplication::class.java).run(*args) } } } 10
  5. -> Kotlin? dependencies { // ... Idea will do the

    thing } @SpringBootApplication @RestController class MyFabulousApplication { @RequestMapping("/anything") private fun anything(): Response { return Response("anything is here") } } data class Response(val message: String) fun main(args: Array<String>) { SpringApplicationBuilder(MyFabulousApplication::class.java).run(*args) } 11
  6. -> Kotlin? dependencies { // ... Idea will do the

    thing } @SpringBootApplication @RestController class MyFabulousApplication { @RequestMapping("/anything") private fun anything(): Response { return Response("anything is here") } } data class Response(val message: String) fun main(args: Array<String>) { SpringApplicationBuilder(MyFabulousApplication::class.java).run(*args) } 12 Will it start?
  7. -> Kotlin? org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Configuration class 'MyFabulousApplication' may not

    be final. Remove the final modifier to continue. Offending resource: org.stanislavin.fabulous.MyFabulousApplication at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:70) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClass.validate(ConfigurationClass.java:214) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClassParser.validate(ConfigurationClassParser.java:207) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:309) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:228) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:272) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:92) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.stanislavin.fabulous.MyFabulousApplicationKt.main(MyFabulousApplication.kt:25) [classes/:na] 13
  8. -> Kotlin? org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Configuration class 'MyFabulousApplication' may not

    be final. Remove the final modifier to continue. Offending resource: org.stanislavin.fabulous.MyFabulousApplication at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:70) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClass.validate(ConfigurationClass.java:214) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClassParser.validate(ConfigurationClassParser.java:207) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:309) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:228) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:272) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:92) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.stanislavin.fabulous.MyFabulousApplicationKt.main(MyFabulousApplication.kt:25) [classes/:na] @SpringBootApplication @RestController open class MyFabulousApplication 14
  9. -> Kotlin? org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Configuration class 'MyFabulousApplication' may not

    be final. Remove the final modifier to continue. Offending resource: org.stanislavin.fabulous.MyFabulousApplication at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:70) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClass.validate(ConfigurationClass.java:214) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClassParser.validate(ConfigurationClassParser.java:207) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:309) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:228) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:272) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:92) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.stanislavin.fabulous.MyFabulousApplicationKt.main(MyFabulousApplication.kt:25) [classes/:na] plugins { id "org.jetbrains.kotlin.plugin.spring" version "1.2.0" } 15
  10. Kotlin Service @Service class StasService(private val foodService: FoodService) { fun

    eat() { val food = foodService.getFood() } } interface FoodService { fun getFood(): Food } 18
  11. Kotlin Service @Service class StasService(private val foodService: FoodService, private val

    bellyRepository: BellyRepository) { fun eat() { val food = foodService.getFood() bellyRepository.save(food) } } interface FoodService { fun getFood(): Food } interface BellyRepository { fun save(food: Food) } 19
  12. Kotlin Service @SpringBootApplication @RestController class MyFabulousApplication(private val stasService: StasService) {

    @RequestMapping("/eat") private fun eat(): String { stasService.eat() return "THANKS" } } 22
  13. Kotlin Service @SpringBootApplication @RestController class MyFabulousApplication(private val stasService: StasService) {

    @RequestMapping("/eat") private fun eat(): String { return try { stasService.eat() "WANT MORE\n" } catch (e : IAmFullException) { "FULL\n" } } } 23
  14. Kotlin Service @SpringBootApplication @RestController open class MyFabulousApplication(private val stasService: StasService)

    { @RequestMapping("/eat") private fun eat(): String { return try { stasService.eat() "WANT MORE" } catch (e : IAmFullException) { "FULL" } } } $ curl 127.0.0.1:8080/eat WANT MORE $ curl 127.0.0.1:8080/eat WANT MORE ... $ curl 127.0.0.1:8080/eat FULL 24
  15. Properties? @SpringBootApplication @RestController class MyFabulousApplication(private val stasService: StasService) { @RequestMapping("/eat")

    private fun eat(): String { return try { stasService.eat() "WANT MORE\n" } catch (e : IAmFullException) { "FULL\n" } } } 26
  16. Properties? @Configuration @ConfigurationProperties("messages") data class MessageProperties(val good: String, val bad:

    String) *************************** APPLICATION FAILED TO START *************************** Description: Parameter 0 of constructor in org.stanislavin.fabulous.MessageProperties required a bean of type 'java.lang.String' that could not be found. 29 Spring Boot 2.1 maybe :(
  17. Properties? @Configuration class MessageProperties(@Value("\${messages.good:THANKS}") val good: String, @Value("\${messages.bad:FULL}") val bad:

    String) @SpringBootApplication @RestController class MyFabulousApplication(private val stasService: StasService, private val properties: MessageProperties) { @RequestMapping("/eat") private fun eat(): String { return try { stasService.eat() "${properties.good}\n" } catch (e : IAmFullException) { "${properties.bad}\n" } } } 33
  18. Coroutines? @SpringBootApplication @RestController open class MyFabulousApplication(private val stasService: StasService) {

    @RequestMapping("/eat") suspend fun eat(): String { stasService.eat() return "THANKS" } } 36
  19. Coroutines? @SpringBootApplication @RestController open class MyFabulousApplication(private val stasService: StasService) {

    @RequestMapping("/eat") suspend fun eat(): String { stasService.eat() return "THANKS" } } 37 Will it work?
  20. org.springframework.beans.BeanInstantiationException: Failed to instantiate [kotlin.coroutines.experimental.Continuation]: Specified class is an interface

    org.springframework.beans.BeanInstantiationException: Failed to instantiate [kotlin.coroutines.experimental.Continuation]: Specified class is an interface at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:99) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:139) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:82) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:106) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) ~[tomcat-embed-core-8.5.23.jar:8.5.23] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.23.jar:8.5.23] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-8.5.23.jar:8.5.23] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.23.jar:8.5.23] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.23.jar:8.5.23] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.23.jar:8.5.23] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.23.jar:8.5.23] at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.23.jar:8.5.23] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.23.jar:8.5.23] Coroutines? 38
  21. Coroutines? dependencies { compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.20' compile 'org.springframework.kotlin:spring-kotlin-coroutine:0.2.2' } @SpringBootApplication @RestController

    @EnableCoroutine class MyFabulousApplication(private val stasService: StasService) { @RequestMapping("/eat") @Coroutine suspend fun eat(): String { stasService.eat() return "THANKS" } } 41
  22. Coroutines? dependencies { compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.20' compile 'org.springframework.kotlin:spring-kotlin-coroutine:0.2.2' } @SpringBootApplication @RestController

    @EnableCoroutine class MyFabulousApplication(private val stasService: StasService) { @RequestMapping("/eat") @Coroutine suspend fun eat(): String { stasService.eat() return "THANKS" } } 42
  23. CoroutineContext? @SpringBootApplication @RestController @EnableCoroutine class MyFabulousApplication(private val stasService: StasService) {

    @RequestMapping("/eat") @Coroutine suspend fun eat(): String { stasService.eat() return "THANKS FROM ${Thread.currentThread().name}\n" } } 44
  24. CoroutineContext? @SpringBootApplication @RestController @EnableCoroutine class MyFabulousApplication(private val stasService: StasService) {

    @RequestMapping("/eat") @Coroutine suspend fun eat(): String { stasService.eat() return "THANKS FROM ${Thread.currentThread().name}\n" } } 45
  25. CoroutineContext? @SpringBootApplication @RestController @EnableCoroutine class MyFabulousApplication(private val stasService: StasService) {

    @RequestMapping("/eat") @Coroutine suspend fun eat(): String { stasService.eat() return "THANKS FROM ${Thread.currentThread().name}\n" } } $ curl 127.0.0.1:8080/eat THANKS FROM http-nio-8080-exec-1 46
  26. CoroutineContext? @SpringBootApplication @RestController @EnableCoroutine class MyFabulousApplication(private val stasService: StasService) {

    @RequestMapping("/eat") @Coroutine suspend fun eat(): String { stasService.eat() return "THANKS FROM ${Thread.currentThread().name}\n" } @Bean fun stasCoroutineContext() = newSingleThreadContext("stas-context-thread") } 47
  27. CoroutineContext? @SpringBootApplication @RestController @EnableCoroutine class MyFabulousApplication(private val stasService: StasService) {

    @RequestMapping("/eat") @Coroutine("stasCoroutineContext") suspend fun eat(): String { stasService.eat() return "THANKS FROM ${Thread.currentThread().name}\n" } @Bean fun stasCoroutineContext() = newSingleThreadContext("stas-context-thread") } 48
  28. CoroutineContext? @SpringBootApplication @RestController @EnableCoroutine class MyFabulousApplication(private val stasService: StasService) {

    @RequestMapping("/eat") @Coroutine("stasCoroutineContext") suspend fun eat(): String { stasService.eat() return "THANKS FROM ${Thread.currentThread().name}\n" } @Bean fun stasCoroutineContext() = newSingleThreadContext("stas-context-thread") } 49
  29. CoroutineContext? @SpringBootApplication @RestController @EnableCoroutine class MyFabulousApplication(private val stasService: StasService) {

    @RequestMapping("/eat") @Coroutine("stasCoroutineContext") suspend fun eat(): String { stasService.eat() return "THANKS FROM ${Thread.currentThread().name}\n" } @Bean fun stasCoroutineContext() = newSingleThreadContext("stas-context-thread") } $ curl 127.0.0.1:8080/eat THANKS FROM stas-context-thread 50
  30. Kotlin in Spring 5 Null-safety No need for required=false, use

    nullability HTTP: @RequestParam, @Header Beans: @Autowired, @Inject 53
  31. Kotlin in Spring 5 Null-safety No need for required=false, use

    nullability HTTP: @RequestParam, @Header Beans: @Autowired, @Inject Kotlin DSL for WebFlux 54
  32. Kotlin in Spring 5 Null-safety No need for required=false, use

    nullability HTTP: @RequestParam, @Header Beans: @Autowired, @Inject Kotlin DSL for WebFlux Built-in support for Reactor 55
  33. Kotlin in Spring 5 Null-safety No need for required=false, use

    nullability HTTP: @RequestParam, @Header Beans: @Autowired, @Inject Kotlin DSL for WebFlux Built-in support for Reactor Kotlin for Gradle 56
  34. Kotlin in Spring 5 Null-safety No need for required=false, use

    nullability HTTP: @RequestParam, @Header Beans: @Autowired, @Inject Kotlin DSL for WebFlux Built-in support for Reactor Kotlin for Gradle https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0 57