1.建立blog项目目录,cd 进入项目目录
2. curl 执行
curl https://start.spring.io/starter.zip -d language=kotlin -d dependencies=web,mustache,jpa,h2,devtools -d packageName=com.keny.blog -d name=Blog -d type=gradle-project -o blog.zip
3.解压
unzip blog.zip
文件结构为:

首先我们看build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version"2.5.3"
id("io.spring.dependency-management") version"1.0.11.RELEASE"
kotlin("jvm") version"1.5.21"
kotlin("plugin.spring") version"1.5.21"
kotlin("plugin.jpa") version"1.5.21"
}
//除了明显的 Kotlin Gradle 插件之外,默认配置还声明了 kotlin-spring 插件,该插件会自动打开类和方法(与 Java 中不同,Kotlin 中的默认限定//符是 final)使用 Spring 注释进行注释或元注释。例如,这对于能够创建 @Configuration 或 @Transactional bean 而不必添加 CGLIB 代理所需的开放限定符很有用
group ="com.example"
version ="0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-mustache")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("com.h2database:h2")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
//此类 Spring Boot Web 应用程序需要 3 个 Kotlin 特定库,并且默认配置:
//kotlin-stdlib-jdk8 是 Kotlin 标准库的 Java 8 变体
//kotlin-reflect 是 Kotlin 反射库
//jackson-module-kotlin 增加了对 Kotlin 类和数据类的序列化/反序列化的支持(可以自动使用单个构造函数类,也支持带有二级构造函数或静态工厂的类)
tasks.withType {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget ="11"
}
}
tasks.withType {
useJUnitPlatform()
}
导入到installj idea 里面,需要安装kotlin的插件

4.生成的应用程序
4.1 入口
package com.keny.blog
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class BlogApplication
fun main(args: Array) {
runApplication(*args)
}
与Java相比,您可以注意到缺少分号,空类缺少括号(如果需要通过@Bean注解声明bean,可以添加一些)以及使用runApplication顶级函数。 runApplication<BlogApplication>(* args) 是 SpringApplication.run(BlogApplication::class.java, *args) 的 Kotlin 惯用替代品,可用于使用以下语法自定义应用程序。
4.2编写你的第一个 Kotlin 控制器
package com.keny.blog
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.ui.set
import org.springframework.web.bind.annotation.GetMapping
@Controller
class HtmlController {
@GetMapping("/")
fun blog(model: Model): String {
model["title"] ="Welcome to Kenychen blog"
return "blog"
}
}
请注意,我们在这里使用了一个 Kotlin 扩展,它允许将 Kotlin 函数或运算符添加到现有的 Spring 类型。这里我们导入 org.springframework.ui.set 扩展函数,以便能够编写 model["title"] = "Blog" 而不是 model.addAttribute("title", "Blog")。 Spring Framework KDoc API 列出了为丰富 Java API 而提供的所有 Kotlin 扩展
建立3个文件,然后启动服务
通过运行BlogApplication.kt 的main 函数来启动Web 应用程序,并转到http://localhost:8080/,您应该会看到一个带有“Blog”标题的清醒网页。
5. gradlew build 和运行


6.使用 JUnit 5 进行测试
现在在 Spring Boot 中默认使用的 JUnit 5 提供了与 Kotlin 非常方便的各种功能,包括构造函数/方法参数的自动装配,允许使用不可为空的 val 属性,以及在常规非静态方法上使用 @BeforeAll/@AfterAll 的可能性。
package com.keny.blog
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.client.TestRestTemplate
import org.springframework.http.HttpStatus
import java.sql.DriverManager.println
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class IntegrationTests(@Autowired val restTemplate: TestRestTemplate) {
@BeforeAll
fun setup() {
println(">> Setup")
}
@Test
fun `Assert blog page title, content and status code`() {
val entity =restTemplate.getForEntity("/")
assertThat(entity.statusCode).isEqualTo(HttpStatus.OK)
assertThat(entity.body).contains("<h1>Blog</h1>")
}
@Test
fun `Assert article page title, content and status code`() {
println(">> TODO")
}
@AfterAll
fun teardown() {
println(">> Tear down")
}
}
7.创建自己的扩展
package com.keny.blog
import java.time.LocalDateTime
fun LocalDateTime.format() =this.format(englishDateFormatter)
private val daysLookup = (1..31).associate{ it.toLong() togetOrdinal(it)}
private val englishDateFormatter = DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd")
.appendLiteral(" ")
.appendText(ChronoField.DAY_OF_MONTH, daysLookup)
.appendLiteral(" ")
.appendPattern("yyyy")
.toFormatter(Locale.ENGLISH)
private fun getOrdinal(n: Int) =when {
nin 11..13 ->"${n}th"
n %10 ==1 ->"${n}st"
n %10 ==2 ->"${n}nd"
n %10 ==3 ->"${n}rd"
else ->"${n}th"
}
fun String.toSlug() = toLowerCase()
.replace("\n", " ")
.replace("[^a-z\\d\\s]".toRegex(), " ")
.split(" ")
.joinToString("-")
.replace("-+".toRegex(), "-")
网友评论