Skip to content

Commit

Permalink
Migrate to Boot 2 and Junit 5
Browse files Browse the repository at this point in the history
  • Loading branch information
sdeleuze committed Oct 25, 2017
1 parent 2d3365f commit a8b4847
Show file tree
Hide file tree
Showing 18 changed files with 227 additions and 215 deletions.
52 changes: 20 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,25 @@
# Spring Kotlin deepdive

This project is designed to show step by step how to migrate from Java to Kotlin with
Spring Boot.
Spring Boot step by step:
* [Step 0](https://github.com/sdeleuze/spring-kotlin-deepdive/): Initial Java project
* [Step 1](https://github.com/sdeleuze/spring-kotlin-deepdive/tree/step1): Java to Kotlin
* [Step 2](https://github.com/sdeleuze/spring-kotlin-deepdive/tree/step2): Spring Boot 2
* [Step 3](https://github.com/sdeleuze/spring-kotlin-deepdive/tree/step3): Spring WebFlux
* [Step 4](https://github.com/sdeleuze/spring-kotlin-deepdive/tree/step4): Kotlin routing DSL

See [Spring Kotlin support documentation](https://docs.spring.io/spring/docs/current/spring-framework-reference/languages.html#kotlin) for more details.

## Step 2: Spring Boot 2

## [Step 1](https://github.com/sdeleuze/spring-kotlin-deepdive/tree/step1): Java to Kotlin
* Spring Data Kay
* No need for `kotlin-noarg` plugin since it supports natively Kotlin immutable classes
* Spring Boot 2
* `jackson-module-kotlin` and `jackson-datatype-jsr310` provided by default with Jackson starter
* Mustache suffix is already `.mustache` by default
* Null safety
* `@RequestParam` on nullable parameter
* Extensions
* JUnit 5 + `@BeforeAll`/ `@AfterAll`

Code:
* No more semicolon at end of lines
* Type suffixed with colon, as statically typed as Java, optional type inference
* Show how to configure return type inference hints
* Short syntax for declaring properties and initializing them from the primary constructor instead of super verbose mostly auto-generated POJO
* [Data classes](https://kotlinlang.org/docs/reference/data-classes.html)
* Syntax help using naturally immutable classes
* `:` instead of `extends`
* No need for `{ }` for empty classes / interfaces
* `public` by the default
* `fun` to declare functions
* Better lambdas: `{ }` last parameter notation, lambda without collect, `it` default parameter
* Constructor without `new`
* `main()` top level method
* `Utils` class -> [Kotlin extensions](https://kotlinlang.org/docs/reference/extensions.html)
* `.getBody()` -> `.body`
* Meaningful function names between backticks

Build:
* Dependencies:
* `kotlin-stdlib-jre8`
* `kotlin-reflect`
* `jackson-module-kotlin`
* Plugins:
* `kotlin`
* `kotlin-spring`
* `kotlin-noarg`
* Configure to build Java 8 bytecode

**[Go to step 2: Spring Boot 2](https://github.com/sdeleuze/spring-kotlin-deepdive/tree/step2)**
**[Go to step 3: Spring WebFlux](https://github.com/sdeleuze/spring-kotlin-deepdive/tree/step3)**
33 changes: 21 additions & 12 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,54 +1,63 @@
buildscript {
ext {
kotlinVersion = "1.1.51"
springBootVersion = "1.5.7.RELEASE"
springBootVersion = "2.0.0.M5"
}
repositories {
mavenCentral()
maven { url "https://repo.spring.io/milestone" }
maven { url "https://repo.spring.io/snapshot" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-noarg:${kotlinVersion}")
classpath("org.junit.platform:junit-platform-gradle-plugin:1.0.1")
}
}

apply plugin: "kotlin"
apply plugin: "kotlin-spring"
apply plugin: "kotlin-noarg"
apply plugin: "org.springframework.boot"
apply plugin: "io.spring.dependency-management"
apply plugin: "org.junit.platform.gradle.plugin"

group = "io.spring"
version = "1.0.0-SNAPSHOT"

repositories {
mavenCentral()
}

noArg {
annotation("org.springframework.data.mongodb.core.mapping.Document")
maven { url "https://repo.spring.io/milestone" }
maven { url "https://repo.spring.io/snapshot" }
}

compileKotlin {
kotlinOptions.jvmTarget = "1.8"
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xjsr305=strict"]
}
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xjsr305=strict"]
}
}

dependencies {
compile("org.jetbrains.kotlin:kotlin-stdlib-jre8")
compile("org.jetbrains.kotlin:kotlin-reflect")
compile("com.fasterxml.jackson.module:jackson-module-kotlin")
compile("org.springframework.boot:spring-boot-starter-actuator")
compile("org.springframework.boot:spring-boot-starter-data-mongodb")
compile("org.springframework.boot:spring-boot-starter-web")
compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
compile("org.springframework.boot:spring-boot-starter-mustache")
compile("com.atlassian.commonmark:commonmark:0.10.0")
compile("com.atlassian.commonmark:commonmark-ext-autolink:0.10.0")
compile("de.flapdoodle.embed:de.flapdoodle.embed.mongo")
runtime("org.springframework.boot:spring-boot-devtools")
testCompile("org.springframework.boot:spring-boot-starter-test")
testCompile("org.springframework.boot:spring-boot-starter-test") {
exclude module: "junit"
}
testCompile("org.junit.jupiter:junit-jupiter-api")
testRuntime("org.junit.jupiter:junit-jupiter-engine")
}
12 changes: 6 additions & 6 deletions src/main/kotlin/io/spring/deepdive/Application.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package io.spring.deepdive;
package io.spring.deepdive

import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Mustache

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.annotation.Bean

@SpringBootApplication
class Application {

@Bean
fun mustacheCompiler(templateLoader: Mustache.TemplateLoader) =
Mustache.compiler().escapeHTML(false).withLoader(templateLoader);
Mustache.compiler().escapeHTML(false).withLoader(templateLoader)

}

Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/io/spring/deepdive/DatabaseInitializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class DatabaseInitializer(private val userRepository: UserRepository, private va
val juergen = User("springjuergen", "Juergen", "Hoeller")
val violeta = User("violetagg", "Violeta", "Georgieva", "All views are my own!")

userRepository.save(Arrays.asList(brian, mark, arjen, rossen, sam, seb, simon, stephanem, stephanen, juergen, violeta))
userRepository.saveAll(Arrays.asList(brian, mark, arjen, rossen, sam, seb, simon, stephanem, stephanen, juergen, violeta))

val reactorTitle = "Reactor Bismuth is out"
val reactorPost = Post(
Expand Down Expand Up @@ -87,6 +87,6 @@ class DatabaseInitializer(private val userRepository: UserRepository, private va
LocalDateTime.of(2017, 1, 4, 9, 0)
)

postRepository.save(Arrays.asList(reactorPost, spring5Post, springKotlinPost))
postRepository.saveAll(Arrays.asList(reactorPost, spring5Post, springKotlinPost))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,16 @@ import io.spring.deepdive.repository.PostRepository

import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.util.Assert
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import kotlin.streams.toList

@Controller
class HtmlPages(private val postRepository: PostRepository, private val markdownConverter: MarkdownConverter) {
class HtmlController(private val repository: PostRepository, private val markdownConverter: MarkdownConverter) {

@GetMapping("/")
fun blog(model: Model): String {
val posts = postRepository.findAll()
val posts = repository.findAll()
val postDtos = StreamSupport.stream(posts.spliterator(), false).map { it.toDto(markdownConverter) }.toList()
model.addAttribute("title", "Blog")
model.addAttribute("posts", postDtos)
Expand All @@ -41,8 +40,7 @@ class HtmlPages(private val postRepository: PostRepository, private val markdown

@GetMapping("/{slug}")
fun post(@PathVariable slug: String, model: Model): String {
val post = postRepository.findOne(slug)
Assert.notNull(post, "Wrong post slug provided")
val post = repository.findById(slug).orElseThrow { IllegalArgumentException("Wrong post slug provided") }
model.addAttribute("post", post.toDto(markdownConverter))
return "post"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,25 @@
*/
package io.spring.deepdive.web

import io.spring.deepdive.MarkdownConverter
import io.spring.deepdive.repository.PostRepository
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/api/post")
class PostApi(private val postRepository: PostRepository) {
class PostController(private val repository: PostRepository, private val markdownConverter: MarkdownConverter) {

@GetMapping("/")
fun findAll() = postRepository.findAll()
fun findAll() = repository.findAll()

@GetMapping("/{slug}")
fun findOne(@PathVariable slug: String) = postRepository.findOne(slug)
fun findOne(@PathVariable slug: String, @RequestParam converter: String?) = when (converter) {
"markdown" -> repository.findById(slug).map { it.copy(
title = markdownConverter.apply(it.title),
headline = markdownConverter.apply(it.headline),
content = markdownConverter.apply(it.content)) }
null -> repository.findById(slug)
else -> throw IllegalArgumentException("Only markdown converter is supported")
}

}
4 changes: 2 additions & 2 deletions src/main/kotlin/io/spring/deepdive/web/PostDto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.deepdive.web;
package io.spring.deepdive.web

import io.spring.deepdive.MarkdownConverter
import io.spring.deepdive.formatDate
Expand All @@ -30,7 +30,7 @@ data class PostDto(

fun Post.toDto(markdownConverter: MarkdownConverter) = PostDto(
slug,
markdownConverter.apply(title),
title,
markdownConverter.apply(headline),
markdownConverter.apply(content),
author,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/api/user")
class UserApi(private val userRepository: UserRepository) {
class UserController(private val repository: UserRepository) {

@GetMapping("/")
fun findAll() = userRepository.findAll()
fun findAll() = repository.findAll()

@GetMapping("/{login}")
fun findOne(@PathVariable login: String) = userRepository.findOne(login)
fun findOne(@PathVariable login: String) = repository.findById(login)

}
27 changes: 10 additions & 17 deletions src/main/resources/templates/blog.mustache
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
{{> header}}

<h1 class="content-subhead">Recent Posts</h1>

<div class="posts">
<h1 class="content-subhead">Recent Posts</h1>

{{#posts}}
<section class="post">
<header class="post-header">
<!--<img class="post-avatar" alt="Eric Ferraiuolo&#x27;s avatar" height="48" width="48" src="/img/common/ericf-avatar.png">-->

<h2 class="post-title"><a href="/{{slug}}">{{title}}</a></h2>

<p class="post-meta">
By <a class="post-author" href="#"><strong>{{author.firstname}}</strong></a>, on <strong>{{addedAt}}</strong>
</p>
</header>

<div class="post-description">
<p>
<section class="post">
<header class="post-header">
<h2 class="post-title"><a href="/{{slug}}">{{title}}</a></h2>
<div class="post-meta">By <strong>{{author.firstname}}</strong>, on <strong>{{addedAt}}</strong></div>
</header>
<div class="post-description">
{{headline}}
</p>
</div>
</section>
</div>
</section>
{{/posts}}
</div>

Expand Down
14 changes: 3 additions & 11 deletions src/main/resources/templates/post.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,14 @@

<section class="post">
<header class="post-header">

<h1 class="post-title">{{post.title}}</h1>

<p class="post-meta">
By <a class="post-author" href="#"><strong>{{post.author.firstname}}</strong></a>, on <strong>{{post.addedAt}}</strong>
</p>
<p class="post-meta">By <strong>{{post.author.firstname}}</strong>, on <strong>{{post.addedAt}}</strong></p>
</header>

<div class="post-description">
<p>
{{post.headline}}
</p>
{{post.headline}}

<p>
{{post.content}}
</p>
{{post.content}}
</div>
</section>

Expand Down
41 changes: 0 additions & 41 deletions src/test/java/io/spring/deepdive/HtmlPagesTests.kt

This file was deleted.

Loading

0 comments on commit a8b4847

Please sign in to comment.