Slide 1

Slide 1 text

SPRING 5 & KOTLIN

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

SPRING 5 WHAT’S NEW? ▸ Core framework revision ▸ Core container updates ▸ Reactive programming model

Slide 4

Slide 4 text

SPRING 5 WHAT’S NEW?

Slide 5

Slide 5 text

SPRING 5 REACTOR Project Reactor

Slide 6

Slide 6 text

SPRING 5 REACTIVE STREAMS

Slide 7

Slide 7 text

SPRING 5 REACTOR Project Reactor

Slide 8

Slide 8 text

SPRING 5 REACTOR Reactive core non-blocking foundation
 interacts with Java 8
 functional API, Completable
 Future, Streams Non-blocking IPC suited for microservices architecture
 backpressure-ready network engines
 (HTTP / Websockets / TCP / UDP)
 reactive encoding/decoding Typed [0|1|N] sequences reactive composable API
 Flux[N]
 Mono[0/1]
 implements Reactive Extensions

Slide 9

Slide 9 text

SPRING 5 REACTIVE STREAMS PUBLISHER SUBSCRIBER Subscribe then
 request(n) data
 (Backpressure) 0..N data then
 0..1 (Error | Complete)

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

SPRING 5 WHAT’S NEW? ▸ Core framework revision ▸ Core container updates ▸ Reactive programming model ▸ WebFlux: reactive HTTP and WebSocket clients / reactive server applications

Slide 13

Slide 13 text

SPRING 5 WEBFLUX

Slide 14

Slide 14 text

SPRING 5 WHAT’S NEW? ▸ Core framework revision ▸ Core container updates ▸ Reactive programming model ▸ WebFlux: reactive HTTP and WebSocket clients / reactive server applications ▸ Functional, Java 8 Lambda style routing and handling ▸ Testing improvements

Slide 15

Slide 15 text

SPRING 5 WHAT’S NEW?

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

DEMO SPRING KOTLIN

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

SPRING BOOT & KOTLIN APPLICATION CONTEXT @SpringBootApplication class Application fun main(args: Array) { SpringApplication.run(Application ::class.java, *args) }

Slide 23

Slide 23 text

SPRING BOOT & KOTLIN CONTROLLER @RestController class HelloKotlinController { @GetMapping("/hello") fun hello(): String { return "Hello world" } }

Slide 24

Slide 24 text

SPRING BOOT & KOTLIN CONTROLLER TEST @RunWith(SpringRunner ::class) @SpringBootTest( classes = arrayOf(Application ::class), webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class HelloKotlinControllerTest { @Autowired lateinit var testRestTemplate: TestRestTemplate @Test fun `hello endpoint should return hello world`() { val result = testRestTemplate.getForEntity("/hello", String ::class.java) result.shouldNotBeNull() result.statusCode shouldEqual HttpStatus.OK result.body shouldEqualTo "Hello world" } }

Slide 25

Slide 25 text

SPRING BOOT & KOTLIN DOMAIN data class GotCharacter(val firstName: String, val lastName: String, val age: Int, val actor: String, val id: Long = -1)

Slide 26

Slide 26 text

SPRING BOOT & KOTLIN DOMAIN @Entity data class GotCharacter(val firstName: String, val lastName: String, val age: Int, val actor: String, @Id @GeneratedValue(strategy = GenerationType.AUTO) val id: Long = -1)

Slide 27

Slide 27 text

SPRING BOOT & KOTLIN REPOSITORY interface GotCharacterRepository: CrudRepository { fun findByLastName(lastName: String): Iterable }

Slide 28

Slide 28 text

SPRING BOOT & KOTLIN CONTROLLER @RestController class GotController(private val repository: GotCharacterRepository) { @GetMapping("/") fun findAll() = repository.findAll() @GetMapping("/{lastName}") fun findByLastName(@PathVariable lastName: String) = repository.findByLastName(lastName) }

Slide 29

Slide 29 text

KOTLIN SUPPORT: SPRING MVC, SPRING WEBFLUX, RESTTEMPLATE, SPRING BOOT 2, … Spring Framework 5 TEXT

Slide 30

Slide 30 text

SPRING & KOTLIN NULL-SAFETY OF SPRING FRAMEWORK API ▸ Spring 5 introduces non-null API declaration for all packages ▸ Explicitly nullable arguments and return values annotated as such ▸ ➜ Spring framework APIs = null-safe from Kotlin side (Kotlin 1.1.50+)

Slide 31

Slide 31 text

SPRING & KOTLIN NULL-SAFETY OF SPRING FRAMEWORK API ▸ Spring 5 introduces non-null API declaration for all packages ▸ Explicitly nullable arguments and return values annotated as such ▸ ➜ Spring framework APIs = null-safe from Kotlin side (Kotlin 1.1.50+) @NonNullApi package org.springframework.core; /** * Return the "Cache-Control" header value. * @return {@code null} if no directive was added, or the header value otherwise */ @Nullable public String getHeaderValue()

Slide 32

Slide 32 text

SPRING & KOTLIN LEVERAGE KOTLIN NULLABLE INFORMATION IN SPRING ANNOTATIONS // "GET /foo" and "GET /foo?bar=baz" are allowed @GetMapping("/foo") fun foo(@RequestParam bar: String?) = ... // "GET /foo?bar=baz" is allowed and "GET /foo" will error @GetMapping("/foo") fun foo(@RequestParam bar: String) = ...

Slide 33

Slide 33 text

SPRING & KOTLIN EXTENSION: REIFIED TYPE PARAMETERS // Java Repo repoInfo = restTemplate.getForObject(url, Repo.class); Flux users = client.get().retrieve().bodyToFlux(User.class) // Kotlin val repo: Repo = restTemplate.getForObject(url) val users = client.get().retrieve().bodyToFlux() // or (both are equivalent) val users : Flux = client.get().retrieve().bodyToFlux()

Slide 34

Slide 34 text

SPRING & KOTLIN EXTENSION: REIFIED TYPE PARAMETERS ResponseEntity> response = restTemplate.exchange(uri, HttpMethod.GET, this.httpEntity, new ParameterizedTypeReference>() {}); inline fun RestOperations.getForObject(url: String, vararg uriVariables: Any) = getForObject(url, T ::class.java, *uriVariables)

Slide 35

Slide 35 text

SPRING & KOTLIN REACTOR KOTLIN BUILTIN SUPPORT ▸ Mono, Flux, StepVerifier APIs ▸ Kotlin extensions ▸ any class instance to Mono instance with foo.toMono() ▸ create Flux from Java 8 Streams wih stream.toFlux() ▸ extensions for Iterable, CompletableFuture, Throwable

Slide 36

Slide 36 text

SPRING & KOTLIN KOTLIN-SPRING PLUGIN ▸ Kotlin classes = final by default ➜ add open keyword on each class / member functions of Spring beans

Slide 37

Slide 37 text

SPRING & KOTLIN KOTLIN-SPRING PLUGIN ▸ Kotlin classes = final by default ➜ add open keyword on each class / member functions of Spring beans ▸ @Component
 @Async
 @Transactional
 @Cacheable

Slide 38

Slide 38 text

SPRING & KOTLIN KOTLIN-SPRING PLUGIN ▸ Kotlin classes = final by default ➜ add open keyword on each class / member functions of Spring beans ▸ @Component
 @Async
 @Transactional
 @Cacheable ▸ Meta-annotations support: @Configuration, @Controller, @RestController, @Service, @Repository

Slide 39

Slide 39 text

SPRING & KOTLIN GRADLE: KOTLIN DSL plugins { val kotlinVersion = "1.1.50" id("org.jetbrains.kotlin.jvm") version kotlinVersion id("org.jetbrains.kotlin.plugin.spring") version kotlinVersion id("org.jetbrains.kotlin.plugin.noarg") version kotlinVersion id("io.spring.dependency-management") version "1.0.3.RELEASE" } apply { plugin("kotlin") plugin("kotlin-spring") plugin("kotlin-jpa") plugin("org.springframework.boot") } version = "0.0.1-SNAPSHOT" tasks { withType { kotlinOptions { jvmTarget = "1.8" freeCompilerArgs = listOf("-Xjsr305=strict") } } } dependencies { compile("org.jetbrains.kotlin:kotlin-stdlib") compile("org.jetbrains.kotlin:kotlin-reflect") compile("org.springframework.boot:spring-boot-starter-web") compile("org.springframework.boot:spring-boot-starter-data-jpa") compile("org.springframework.boot:spring-boot-starter-actuator") compile("com.h2database:h2") compile("com.fasterxml.jackson.module:jackson-module-kotlin") testCompile("org.springframework.boot:spring-boot-starter-test") testCompile("org.amshove.kluent:kluent:1.29")

Slide 40

Slide 40 text

SPRING & KOTLIN KOTLIN SCRIPT BASED TEMPLATES import io.spring.demo.* """ ${include("header")}

${i18n("title")}

    ${users.joinToLine{ "
  • ${i18n("user")} ${it.firstname} ${it.lastname}
  • " }}
${include("footer")} """

Slide 41

Slide 41 text

SPRING 5 WEBFLUX

Slide 42

Slide 42 text

SPRING & KOTLIN WEBFLUX ANNOTATION-BASED @RestController class ReactiveGotController(private val repository: ReactiveGotRepository) { @GetMapping("/reactive/characters") fun findAll() = repository.findAll() @GetMapping("/reactive/character/{id}") fun findOne(@PathVariable id: String) = repository.findById(id) @PostMapping("/reactive/character") fun save(@RequestBody character: GotCharacter) = repository.save(character) } interface ReactiveGotRepository { fun findAll(): Flux fun findById(id: String): Mono fun save(character: GotCharacter): Mono }

Slide 43

Slide 43 text

TEXT WEBFLUX

Slide 44

Slide 44 text

SPRING & KOTLIN WEBFLUX ANNOTATION BASED @RestController class ReactiveGotController(private val repository: ReactiveGotRepository) { @GetMapping("/reactive/characters") fun findAll() = repository.findAll() @GetMapping("/reactive/character/{id}") fun findOne(@PathVariable id: String) = repository.findById(id) @PostMapping("/reactive/character") fun save(@RequestBody character: GotCharacter) = repository.save(character) } interface ReactiveGotRepository { fun findAll(): Flux fun findById(id: String): Mono fun save(character: GotCharacter): Mono }

Slide 45

Slide 45 text

SPRING & KOTLIN FUNCTIONAL STYLE // Annotation-based Java @RequestMapping("/quotes/feed", produces = TEXT_EVENT_STREAM_VALUE) public Flux fetchQuotesStream() { ... }

Slide 46

Slide 46 text

SPRING & KOTLIN FUNCTIONAL STYLE // Annotation-based Java @RequestMapping("/quotes/feed", produces = TEXT_EVENT_STREAM_VALUE) public Flux fetchQuotesStream() { ... } // Functional Java without static imports RouterFunctions.route( RequestPredicates.path("/quotes/feed") .and(RequestPredicates.accept(MediaType.TEXT_EVENT_STREAM)), { ... }

Slide 47

Slide 47 text

SPRING & KOTLIN FUNCTIONAL STYLE // Annotation-based Java @RequestMapping("/quotes/feed", produces = TEXT_EVENT_STREAM_VALUE) public Flux fetchQuotesStream() { ... } // Functional Java with static imports route( path("/quotes/feed").and(accept(TEXT_EVENT_STREAM)), { ... }

Slide 48

Slide 48 text

SPRING & KOTLIN FUNCTIONAL STYLE // Annotation-based Java @RequestMapping("/quotes/feed", produces = TEXT_EVENT_STREAM_VALUE) public Flux fetchQuotesStream() { ... } // Functional Kotlin router { "/quotes/feed" and accept(TEXT_EVENT_STREAM) { ... } }

Slide 49

Slide 49 text

SPRING & KOTLIN FUNCTIONAL STYLE @Configuration class ApplicationRoutes(val userHandler: UserHandler, val blogHandler: BlogHandler, val shopRepository: ShopRepository) { @Bean fun appRouter() = router { GET("/users", userHandler ::fetchAll) GET("/users/{id}", userHandler ::fetch) } @Bean fun nestedRouter() = router { ... } @Bean fun dynamicRouter() = router { ... } }

Slide 50

Slide 50 text

SPRING & KOTLIN NESTED ROUTING @Bean fun nestedRouter() = router { ("/blog" and accept(MediaType.TEXT_HTML)).nest { GET("/", blogHandler ::findAllView) GET("/{slug}", blogHandler ::findOneView) } ("/api/blog" and accept(APPLICATION_JSON)).nest { GET("/", blogHandler ::findAll) GET("/{id}", blogHandler ::findOne) POST("/", blogHandler ::create) } }

Slide 51

Slide 51 text

SPRING & KOTLIN DYNAMIC ROUTING @Bean fun dynamicRouter() = router { shopRepository.findAll() .toIterable() .forEach { shop -> GET("/${shop.id}") { req -> shopHandler.homepage(shop, req) } } }

Slide 52

Slide 52 text

SPRING & KOTLIN HANDLER @Component class EventHandler(val repository: EventRepository) { fun findOne(req: ServerRequest) = ok().json().body(repository.findOne(req.pathVariable("id"))) fun findAll(req: ServerRequest) = ok().json().body(repository.findAll()) }

Slide 53

Slide 53 text

SPRING & KOTLIN HANDLER @Component class NewsHandler { fun newsView(req: ServerRequest) = ok().render("news") fun newsSse(req: ServerRequest) = ok() .contentType(TEXT_EVENT_STREAM) .body(Flux.interval(ofMillis(100)).map { "Hello $it!" }) }

Slide 54

Slide 54 text

SPRING 5 ❤️