why spring kotlin - jetbrainsspring...background image: 960x540 pixels - send to back of slide and...
TRANSCRIPT
![Page 1: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/1.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
Why Spring Kotlin
Sébastien Deleuze @sdeleuze
![Page 2: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/2.jpg)
2
Today most popular way to build web applications
+Spring Boot
![Page 3: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/3.jpg)
3
Let’s see why and how far we can go with ...
Kotlin+
Spring Boot
![Page 4: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/4.jpg)
4
![Page 5: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/5.jpg)
5
Sample Spring Boot blog application
![Page 6: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/6.jpg)
6
Step 1
Kotlin
![Page 7: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/7.jpg)
7
Step 2
Spring Boot 1
SpringBoot 2
based on Spring Framework 5
![Page 8: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/8.jpg)
8
Step 3
Spring MVC Spring WebFlux@nnotations
![Page 9: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/9.jpg)
9
Step 4
SpringWebFlux
Functional API & Kotlin DSL
Spring WebFlux@nnotations
![Page 10: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/10.jpg)
10
Step 5
Kotlin
![Page 11: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/11.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
Step 1 KotlinStep 2 Boot 2Step 3 WebFlux @annotationsStep 4 Functional & DSLStep 5 Kotlin for frontend
![Page 12: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/12.jpg)
12
https://start.spring.io/#!language=kotlin
![Page 13: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/13.jpg)
kotlin-spring compiler pluginAutomatically open Spring annotated classes and methods
@SpringBootApplicationopen class Application {
@Bean open fun foo() = Foo()
@Bean open fun bar(foo: Foo) = Bar(foo)
}
@SpringBootApplicationclass Application {
@Bean fun foo() = Foo()
@Bean fun bar(foo: Foo) = Bar(foo)
}
Without kotlin-spring plugin With kotlin-spring plugin
13
![Page 14: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/14.jpg)
14
Domain model
@Documentdata class Post( @Id val slug: String, val title: String, val headline: String, val content: String, @DBRef val author: User, val addedAt: LocalDateTime = now())
@Documentdata class User( @Id val login: String, val firstname: String, val lastname: String, val description: String? = null)
@Documentpublic class Post {
@Id private String slug;
private String title;
private LocalDateTime addedAt;
private String headline;
private String content;
@DBRef private User author;
public Post() { }
public Post(String slug, String title, String headline, String content, User author) { this(slug, title, headline, content, author, LocalDateTime.now()); }
public Post(String slug, String title, String headline, String content, User author, LocalDateTime addedAt) { this.slug = slug; this.title = title; this.addedAt = addedAt; this.headline = headline; this.content = content; this.author = author; }
public String getSlug() { return slug; }
public void setSlug(String slug) { this.slug = slug; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public LocalDateTime getAddedAt() { return addedAt; }
public void setAddedAt(LocalDateTime addedAt) { this.addedAt = addedAt; }
public String getHeadline() { return headline; }
public void setHeadline(String headline) { this.headline = headline; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public User getAuthor() { return author; }
public void setAuthor(User author) { this.author = author; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false;
Post post = (Post) o;
if (slug != null ? !slug.equals(post.slug) : post.slug != null) return false; if (title != null ? !title.equals(post.title) : post.title != null) return false; if (addedAt != null ? !addedAt.equals(post.addedAt) : post.addedAt != null) return false; if (headline != null ? !headline.equals(post.headline) : post.headline != null) return false; if (content != null ? !content.equals(post.content) : post.content != null) return false; return author != null ? author.equals(post.author) : post.author == null; }
@Override public int hashCode() { int result = slug != null ? slug.hashCode() : 0; result = 31 * result + (title != null ? title.hashCode() : 0); result = 31 * result + (addedAt != null ? addedAt.hashCode() : 0); result = 31 * result + (headline != null ? headline.hashCode() : 0); result = 31 * result + (content != null ? content.hashCode() : 0); result = 31 * result + (author != null ? author.hashCode() : 0); return result; }
@Override public String toString() { return "Post{" + "slug='" + slug + '\'' + ", title='" + title + '\'' + ", addedAt=" + addedAt + ", headline='" + headline + '\'' + ", content='" + content + '\'' + ", author=" + author + '}'; }}
![Page 15: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/15.jpg)
15
@RestControllerpublic class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) { this.userRepository = userRepository; }
@GetMapping("/user/{login}") public User findOne(@PathVariable String login) { return userRepository.findOne(login); }
@GetMapping("/user") public Iterable<User> findAll() { return userRepository.findAll(); }
@PostMapping("/user") public User save(@RequestBody User user) { return userRepository.save(user); }}
Spring MVC controller written in Java
![Page 16: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/16.jpg)
16
@RestControllerclass UserController(val repo: UserRepository) {
@GetMapping("/user/{id}") fun findOne(@PathVariable id: String) = repo.findOne(id)
@GetMapping("/user") fun findAll() = repo.findAll()
@PostMapping("/user") fun save(@RequestBody user: User) = repo.save(user)}
Spring MVC controller written in Kotlin
![Page 17: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/17.jpg)
17
Inferred type hints in IDEA
SettingsEditorGeneralAppearanceShow parameter name hintsSelect KotlinCheck “Show function/property/local value return type hints”
![Page 18: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/18.jpg)
18
Expressive test names with backticks
class EmojTests {
@Test fun `Why Spring ❤ Kotlin?`() { println("Because I can use emoj in function names \uD83D\uDE09") }
}
> Because I can use emoj in function names
![Page 19: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/19.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
Step 1 KotlinStep 2 Boot 2Step 3 WebFlux @annotationsStep 4 Functional & DSLStep 5 Kotlin for frontend
![Page 20: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/20.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
Spring Kotlinand officially supports it
Spring Framework 5
Spring Boot 2 (late 2017)
Reactor Core 3.1
Spring Data Kay
![Page 21: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/21.jpg)
21
Kotlin bytecode builtin in Spring JARs
![Page 23: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/23.jpg)
23
Kotlin API documentationhttps://goo.gl/svCLL1
![Page 24: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/24.jpg)
Run SpringApplication with Boot 1
24
@SpringBootApplicationclass Application
fun main(args: Array<String>) {SpringApplication.run(Application::class.java, *args)
}
![Page 25: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/25.jpg)
Run SpringApplication with Boot 2
25
@SpringBootApplicationclass Application
fun main(args: Array<String>) {runApplication<FooApplication>(*args)
}
![Page 26: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/26.jpg)
Declaring additional beans
26
@SpringBootApplicationclass Application {
@Beanfun foo() = Foo()
@Beanfun bar(foo: Foo) = Bar(foo)
}
fun main(args: Array<String>) {runApplication<FooApplication>(*args)
}
![Page 27: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/27.jpg)
Customizing SpringApplication
27
@SpringBootApplicationclass Application {
@Beanfun foo() = Foo()
@Beanfun bar(foo: Foo) = Bar(foo)
}
fun main(args: Array<String>) {runApplication<FooApplication>(*args) { setBannerMode(Banner.Mode.OFF)}
}
![Page 28: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/28.jpg)
Array-like Kotlin extension for Model
operator fun Model.set(attributeName: String, attributeValue: Any) { this.addAttribute(attributeName, attributeValue)}
@GetMapping("/")public String blog(Model model) { model.addAttribute("title", "Blog"); model.addAttribute("posts", postRepository.findAll()); return "blog";}
@GetMapping("/")fun blog(model: Model): String { model["title"] = "Blog" model["posts"] = repository.findAll() return "blog"}
28
![Page 29: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/29.jpg)
Reified type parameters Kotlin extension
inline fun <reified T: Any> RestOperations.getForObject(url: URI): T? = getForObject(url, T::class.java)
List<Post> posts = restTemplate.exchange("/api/post/", HttpMethod.GET, null,new ParameterizedTypeReference<List<Post>>(){}).getBody();
val posts = restTemplate.getForObject<List<Post>>("/api/post/")
Goodbye type erasure, we are not going to miss you at all!
29
![Page 30: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/30.jpg)
30
@Controller // foo is mandatory, bar is optionalclass FooController(val foo: Foo, val bar: Bar?) {
@GetMapping("/") // Equivalent to @RequestParam(required=false)fun foo(@RequestParam baz: String?) = ...
}
Leveraging Kotlin nullable informationTo determine @RequestParam or @Autowired required attribute
![Page 31: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/31.jpg)
By default, Kotlin consider Java types as platform types (unknown nullability)Null safety of Spring APIs
// Spring Framework RestOperations.javapublic interface RestOperations {
URI postForLocation(String url, Object request, Object ... uriVariables)
}
31
postForLocation(url: String!, request: Any!, varags uriVariables: Any!): URI!
![Page 32: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/32.jpg)
Nullability annotations meta annotated with JSR 305 for generic tooling supportNull safety of Spring APIs
// Spring Framework package-info.java@NonNullApipackage org.springframework.web.client;
// Spring Framework RestOperations.javapublic interface RestOperations {
@NullableURI postForLocation(String url, @Nullable Object request, Object... uriVariables)
}
32
postForLocation(url: String, request: Any?, varargs uriVariables: Any): URI?
![Page 33: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/33.jpg)
@ConfigurationProperties
@ConfigurationProperties("foo")class FooProperties { var baseUri: String? = null val admin = Credential()
class Credential { var username: String? = null var password: String? = null }}
@ConfigurationProperties("foo")interface FooProperties { val baseUri: String val admin: Credential
interface Credential { val username: String val password: String }}
Spring Boot 1 Spring Boot 2
Not yet available
Data classes would be also interesting to support, but they are likely not going to be support in Boot 2, see issue #8762 for more details
![Page 34: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/34.jpg)
34
JUnit 5 supports non-static @BeforeAll @AfterAll
class IntegrationTests { private val application = Application(8181) private val client = WebClient.create("http://localhost:8181") @BeforeAll fun beforeAll() { application.start() } @Test fun test1() { // ... }
@Test fun test2() { // ... } @AfterAll fun afterAll() { application.stop() }
}
With “per class” lifecycle defined via junit-platform.properties or @TestInstance
![Page 35: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/35.jpg)
35
class SimpleTests {
@Nested @DisplayName("a calculator") inner class Calculator { val calculator = SampleCalculator()
@Test fun `should return the result of adding the first number to the second number`() { val sum = calculator.sum(2, 4) assertEquals(6, sum) }
@Test fun `should return the result of subtracting the second number from the first number`() { val subtract = calculator.subtract(4, 2) assertEquals(2, subtract) } }}
Specification-like tests with Kotlin and JUnit 5
![Page 36: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/36.jpg)
36
import io.spring.demo.*"""${include("header")}<h1>${i18n("title")}</h1><ul> ${users.joinToLine{ "<li>${i18n("user")} ${it.name}</li>" }}</ul>${include("footer")}"""
➔ Available via Spring MVC & WebFlux JSR-223 support➔ Regular Kotlin code, no new dialect to learn➔ Extensible, refactoring and auto-complete support➔ Need to cache compiled scripts for good performances
https://github.com/sdeleuze/kotlin-script-templating Kotlin type-safe templates Experimental
![Page 37: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/37.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
Step 1 KotlinStep 2 Boot 2Step 3 WebFlux @nnotationsStep 4 Functional & DSLStep 5 Kotlin for frontend
![Page 38: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/38.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
Spring Framework 5 comes with 2 web stacks
Spring MVCBlockingServlet
Spring WebFlux Non-blockingReactive Streams
![Page 39: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/39.jpg)
39
∞
Scalability
StreamsLatency∞
![Page 40: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/40.jpg)
Reactive Streams
40
Publisher Subscriber
0..N data then0..1 (Error | Complete)
Subscribe thenrequest(n) data(Backpressure)
![Page 41: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/41.jpg)
41
RxJava Reactor Akka Streams(via Reactive Streams)
WebFlux supports various async and Reactive API
CompletableFutureFlow.Publisher
![Page 42: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/42.jpg)
42
Reactor
Let’s focus on Reactor for now
![Page 43: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/43.jpg)
43
Reactor Flux is a Publisher for 0..n elements
![Page 44: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/44.jpg)
44
Reactor Mono is a Publisher for 0..1 element
![Page 45: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/45.jpg)
45
Flux.zip(tweets, issues)
WebFluxclient
Streaming API
REST API
WebFluxserver
WebFluxclient
SSEWebsocket
![Page 46: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/46.jpg)
46
val location = "Lyon, France"
mainService.fetchWeather(location) .timeout(Duration.ofSeconds(2)) .doOnError { logger.error(it.getMessage()) } .onErrorResume { backupService.fetchWeather(location) } .map { "Weather in ${it.getLocation()} is ${it.getDescription()}" } .subscribe { logger.info(it) }
fun fetchWeather(city: String): Mono<Weather>
Reactive APIs are functional
![Page 47: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/47.jpg)
Reactor Kotlin extensions
Java Kotlin with extensions
Mono.just("foo") "foo".toMono()
Flux.fromIterable(list) list.toFlux()
Mono.error(new RuntimeException()) RuntimeException().toMono()
flux.ofType(User.class) flux.ofType<User>()
StepVerifier.create(flux).verifyComplete() flux.test().verifyComplete()
MathFlux.averageDouble(flux) flux.average()
![Page 48: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/48.jpg)
48
@RestControllerclass ReactiveUserController(val repository: ReactiveUserRepository) {
@GetMapping("/user/{id}") fun findOne(@PathVariable id: String): Mono<User>
= repository.findOne(id)
@GetMapping("/user") fun findAll(): Flux<User>
= repository.findAll()
@PostMapping("/user") fun save(@RequestBody user: Mono<User>): Mono<Void>
= repository.save(user)}
interface ReactiveUserRepository { fun findOne(id: String): Mono<User> fun findAll(): Flux<User> fun save(user: Mono<User>): Mono<Void>}
Spring WebFlux with annotations
Spring Data Kay provides Reactive support for
MongoDB, Redis, Cassandra and Couchbase
![Page 49: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/49.jpg)
Spring & Kotlin Coroutines
49
● Coroutines are light-weight threads
● Main use cases are○ Writing non-blocking applications while keeping imperative programming○ Creating new operators for Reactor
● kotlinx.coroutines provides Reactive Streams and Reactor support○ fun foo(): Mono<T> -> suspend fun foo(): T?○ fun bar(): Flux<T> -> suspend fun bar(): ReceiveChannel<T> or List<T>○ fun baz(): Mono<Void> -> suspend fun baz()
● Support for Spring MVC, WebFlux and Data Reactive MongoDB is available via https://github.com/konrad-kaminski/spring-kotlin-coroutine/ (nice work Konrad!)
● Warning○ Coroutine are still experimental○ No official Spring support yet, see SPR-15413○ Ongoing evaluation of performances and back-pressure interoperability
Experimental
![Page 50: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/50.jpg)
50
@RestControllerclass CoroutineUserController( val repository: CoroutineUserRepository) { @GetMapping("/user/{id}") suspend fun findOne(@PathVariable id: String): User
= repository.findOne(id)
@GetMapping("/user") suspend fun findAll(): List<User>
= repository.findAll()
@PostMapping("/user") suspend fun save(@RequestBody user: User)
= repository.save(user)}
interface CoroutineUserRepository { suspend fun findOne(id: String): User suspend fun findAll(): List<User> suspend fun save(user: User)}
Spring WebFlux with Coroutines Experimental
https://github.com/sdeleuze/spring-kotlin-deepdive/tree/step3-coroutine
![Page 51: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/51.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
Step 1 KotlinStep 2 Boot 2Step 3 WebFlux @nnotationsStep 4 Functional & DSLStep 5 Kotlin for frontend
![Page 52: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/52.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
Spring WebFlux comes in 2 flavors
Annotations@Controller@RequestMapping
Functional RouterFunctionHandlerFunctionWebClient
![Page 53: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/53.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
RouterFunction(ServerRequest) -> Mono<HandlerFunction>
Spring WebFlux Functional API
HandlerFunction(ServerRequest) -> Mono<ServerResponse>
WebClientProvides non-blocking fluent HTTP client API
![Page 54: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/54.jpg)
54
val router = router {
val users = Flux.just( User("Foo", "Foo", now().minusDays(1)), User("Bar", "Bar", now().minusDays(10)), User("Baz", "Baz", now().minusDays(100)))
accept(TEXT_HTML).nest { "/" { ok().render("index") } "/sse" { ok().render("sse") } "/users" { ok().render("users", mapOf("users" to users.map { it.toDto() })) } } ("/api/users" and accept(APPLICATION_JSON)) { ok().body(users) } ("/api/users" and accept(TEXT_EVENT_STREAM)) { ok().bodyToServerSentEvents(users.repeat().delayElements(ofMillis(100))) } }
WebFlux functional API with Kotlin DSL
![Page 55: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/55.jpg)
55
@SpringBootApplication()class Application {
@Bean fun router(htmlHandler: HtmlHandler, userHandler: UserHandler, postHandler: PostHandler) = router { accept(APPLICATION_JSON).nest { "/api/user".nest { GET("/", userHandler::findAll) GET("/{login}", userHandler::findOne) } "/api/post".nest { GET("/", postHandler::findAll) GET("/{slug}", postHandler::findOne) POST("/", postHandler::save) DELETE("/{slug}", postHandler::delete) } } (GET("/api/post/notifications") and accept(TEXT_EVENT_STREAM)).invoke(postHandler::notifications) accept(TEXT_HTML).nest { GET("/", htmlHandler::blog) (GET("/{slug}") and !GET("/favicon.ico")).invoke(htmlHandler::post) } }}
Splitting router/handlers in integration in Boot
![Page 56: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/56.jpg)
@Componentclass HtmlHandler(private val userRepository: UserRepository,
private val markdownConverter: MarkdownConverter) {
fun blog(req: ServerRequest) = ok().render("blog", mapOf( "title" to "Blog", "posts" to postRepository.findAll() .flatMap { it.toDto(userRepository, markdownConverter) } ))}
@Componentclass PostHandler(private val postRepository: PostRepository,
private val postEventRepository: PostEventRepository) {
fun findAll(req: ServerRequest) = ok().body(postRepository.findAll())
fun notifications(req: ServerRequest) = ok().bodyToServerSentEvents(postEventRepository.findWithTailableCursorBy())
}
56
Functional handlers
![Page 57: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/57.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
Spring Framework 5 introducesfunctional bean registration
Very efficient, no reflection, no CGLIB proxy, no annotations
![Page 58: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/58.jpg)
58
Functional bean definition DSLval webContext = beans { bean { val userHandler = ref<UserHandler>() router { accept(APPLICATION_JSON).nest { "/api/user".nest { GET("/", userHandler::findAll) GET("/{login}", userHandler::findOne) } } // ... } bean { Mustache.compiler().escapeHTML(false).withLoader(ref()) } bean<HtmlHandler>() bean<PostHandler>() bean<UserHandler>() bean<MarkdownConverter>()}
![Page 59: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/59.jpg)
59
Functional bean definition DSLval databaseContext = beans { bean<PostEventListener>() bean<PostEventRepository>() bean<PostRepository>() bean<UserRepository>() environment( { !activeProfiles.contains("cloud") } ) { bean { CommandLineRunner { initializeDatabase(ref(), ref(), ref()) } } }}
fun initializeDatabase(ops: MongoOperations, userRepository: UserRepository, postRepository: PostRepository) { // ... }
![Page 60: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/60.jpg)
Using bean DSL with Spring Boot
60
class ContextInitializer : ApplicationContextInitializer<GenericApplicationContext> { override fun initialize(context: GenericApplicationContext) { databaseContext.initialize(context) webContext.initialize(context) }}
context.initializer.classes=io.spring.deepdive.ContextInitializer
![Page 61: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/61.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
Step 1 KotlinStep 2 Boot 2Step 3 WebFlux @nnotationsStep 4 Functional & DSLStep 5 Kotlin for frontend
![Page 62: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/62.jpg)
62
Original JavaScript code
if (Notification.permission === "granted") { Notification.requestPermission().then(function(result) { console.log(result); });}
let eventSource = new EventSource("/api/post/notifications");eventSource.addEventListener("message", function(e) { let post = JSON.parse(e.data); let notification = new Notification(post.title); notification.onclick = function() { window.location.href = "/" + post.slug; };});
![Page 63: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/63.jpg)
63
Kotlin to Javascript
data class Post(val slug: String, val title: String)
fun main(args: Array<String>) { if (Notification.permission == NotificationPermission.GRANTED) { Notification.requestPermission().then { console.log(it) } } EventSource("/api/post/notifications").addEventListener("message", { val post = JSON.parse<Post>(it.data()); Notification(post.title).addEventListener("click", { window.location.href = "/${post.slug}" }) })}
fun Event.data() = (this as MessageEvent).data as String // See KT-20743
Type-safe, null safety, only 10 Kbytes with Dead Code Elimination tool
![Page 64: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/64.jpg)
64
Native platform for the WebWebAssembly
Read “An Abridged Cartoon Introduction To WebAssembly” by Lin Clark for more details https://goo.gl/I0kQsC
![Page 65: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/65.jpg)
65
Compiling Kotlin to WebAssembly instead of JavaScript
+➔ Kotlin will support WebAssembly via Kotlin Native (LLVM)➔ Much better compilation target➔ No DOM and Web API access yet but that’s coming ...➔ A Kotlin/Native Frontend ecosystem could arise➔ Native level performances, low memory consumption➔ Fallback via asm.js
Just announced
![Page 66: Why Spring Kotlin - JetBrainsSpring...background image: 960x540 pixels - send to back of slide and set to 80% transparency Why Spring Kotlin Sébastien Deleuze @sdeleuze](https://reader030.vdocuments.site/reader030/viewer/2022040819/5e6615c4570b8e798c379eee/html5/thumbnails/66.jpg)
background image: 960x540 pixels - send to back of slide and set to 80% transparency
Thanks!
Follow me on @sdeleuze for fresh Spring + Kotlin news
https://github.com/mixitconf/mixithttps://github.com/sdeleuze/spring-kotlin-fullstackhttps://github.com/sdeleuze/spring-kotlin-deepdivehttps://github.com/sdeleuze/spring-kotlin-functional