使用Spring Boot进行Kotlin服务器端入门教程

kotlin (1).png

在平时的工作中,我使用PHP(Laravel)。最近我在Android开发中尝试了Kotlin,同时也一直想尝试一次使用静态类型语言进行Web开发,所以决定使用Kotlin×Spring Boot来创建一个简单的Web应用程序。

在接触Kotlin时,我发现很多信息是以Java知识为前提进行编写的。我自己只会一些Java基础知识,所以有时要进行繁琐的查询。为了让没有Java知识的人也能理解,我打算尽量用简单的方式来写。

环境

    • OS: macOS Catalina

言語: Kotlin

フレームワーク: Spring Boot

ビルドツール: Gradle

IDE: Intellij IDEA

选择将框架转为Spring Boot的原因是因为如果用Kotlin进行Web开发的话,它似乎是最标准的选择。
我也很想尝试一下名为Ktor的API服务器框架,听起来也很不错。

事前准备

    1. 安装JDK(Java开发工具包)。

 

    1. 关于JDK,我们在以下项目中进行了解释,请阅读这里如果你不明白。

 

    1. 安装IntelliJ IDEA并进行设置。

 

    IntelliJ IDEA是Kotlin的开发者JetBrains创建的IDE,几乎完美地支持Kotlin。设置过程自己按照指引进行应该没问题。这里有一篇可能会有参考价值的文章。

我使用了Homebrew来安装这两个选项。顺便提一下,由于Kotlin附带在Intellij IDEA中,您无需单独安装即可使用。

预备知识

JDK可以在计算机上开发、编译和运行Java程序的软件平台。

在准备阶段,我安装了JDK,但也许还有人会怀疑:”JDK是用来做什么的?既然是Kotlin,只要有Kotlin的编译器和构建工具就可以执行了,不是吗?”。
为了解答这些疑问,我会简要介绍一下Java的执行环境。

jvm.png

请参考图1。首先,与常规的编译语言不同,Java程序编译后不会立即生成可执行文件。而是会生成称为Java类文件的Java字节码文件。

然后,字节码在每个操作系统上为JVM(Java虚拟机)提供的虚拟机上进行逐条解释并执行。由于这种机制,不需要为每个环境进行编译,只需编译一次即可在JVM提供的任何环境中运行。

kotlin.png

换句话说,在运行或开发Kotlin的环境中,必须有JVM。这里终于提到了JDK。

JDK是Java开发所需的一套工具集,包括JVM、Java编译器、库等。因此,在准备步骤中安装JDK是必要的。

如果只是执行纯Kotlin代码,可能只需要Kotlin编译器和JVM即可运行。但是,考虑到大多数情况下会使用Java库或与Java混合使用,建议安装JDK。

Gradle is a build automation tool commonly used in software development.

gradle.png

Gradle是一款构建工具,可以自动进行编译、链接、测试等操作。
不过,我从未使用过其他四种构建工具,所以对于”这是好的!”这种说法并不很了解。
我认为使用Groovy语言进行配置是其重要特点之一,所以对于其语法至少要有一定的了解。以下文章可以作为参考。
适用于不熟悉Groovy的人的build.gradle基础入门文章。

春季引导

springboot.png

Spring Boot是将传统的Java框架Spring Framework变得更易于使用的框架。它通过使用注解来简化代码,消除了Spring Framework复杂的XML配置。由于我没有接触过Spring Framework,所以我不知道它是否会带来多大的便利,但我们将在后续实际运行中了解其表现。

环境建设

话虽长,但我们还是来进行Spring Boot的环境构建吧。

创建项目的草图

_2020-12-02_21.31.52.png

使用Intellij IDEA进行项目设置。

スクリーンショット 2020-12-02 21.44.40.png

使浏览器能够进行注册和显示

这次我们做了一个简单的应用程序,可以通过浏览器添加和显示用户列表。

产出

springboot.gif

目录结构

src
├── kotlin
│   └── com
│       └── example
│           └── springboot
│               ├── SpringbootApplication.kt
│               ├── controller
│               │   └── MainController.kt
│               ├── entity
│               │   └── User.kt
│               └── repository
│                   └── UserRepository.kt
└── resources
    ├── application.properties
    └── templates
        ├── add.html
        └── index.html
コードUser.kt

package com.example.springboot.entity

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id

@Entity
data class User(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Int = 0,
var name: String = “”
)

UserRepository.kt

package com.example.springboot.repository

import com.example.springboot.entity.User
import org.springframework.data.repository.CrudRepository

interface UserRepository : CrudRepository<User, Int>

MainController.kt

package com.example.springboot.controller

import com.example.springboot.entity.User
import com.example.springboot.repository.UserRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestParam

@Controller
class MainController {
@Autowired
lateinit var userRepository: UserRepository

@GetMapping(“/”)
fun showUsers(model: Model): String {
val users = userRepository.findAll()
model.addAttribute(“users”, users)
return “index”
}

@GetMapping(“/add”)
fun showAddPage(): String {
return “add”
}

@PostMapping(“/add”)
fun addNewUser(@RequestParam name: String): String {
userRepository.save(User(0, name))
return “redirect:/”
}
}

index.html

 

Users

  • [[${user.name}]]

 

add.html

 

Add



 

application.properties

spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:./h2db/db_example
spring.datasource.username=username
spring.datasource.password=password

詳盡

接下来,我们将按照以下的方式来解释应用程序的处理流程。

    1. 显示首页

 

    1. 跳转到用户添加页面

 

    添加用户

展示首页

スクリーンショット 2020-12-07 22.45.54.png

首先,我们来介绍当用户访问时,从控制器接收请求并进行处理的步骤。


package com.example.springboot.controller



@Controller
class MainController {
    @Autowired
    lateinit var userRepository: UserRepository

    @GetMapping("/")
    fun showUsers(model: Model): String {
        val users = userRepository.findAll()
        model.addAttribute("users", users)
        return "index"
    }


@Controller –> 控制器

在控制器的类名之前,需要使用@Controller注解或@RestController注解。
如果要使用模板,则使用@Controller注解。如果使用@RestController注解,则方法的返回值将直接成为响应体。
如果要返回HTML,则使用@Controller注解;如果要将其作为API的入口点,则可以使用@RestController注解。

自动装配

@Autowired注解用于指定要进行DI的类。
在本例中,将注入用于从数据库获取数据的UserRepository实例。

找到映射

@GetMapping(“/”)注解意味着如果收到对/的GET方法请求,将调用被标注的方法(本例中为showUsers())。

模型

当你想传递值到模板时,可以在方法的参数中指定Model,然后通过Model的addAttribute()方法,将模板变量名作为第一个参数,值作为第二个参数进行传递。

userRepository.findAll() 查询所有用户

正在执行获取DI UserRespository实例的所有用户的方法。
~Repository类是数据获取的抽象化层。关于UserRepository类的具体内容将在后面进行说明。

返回“索引”

在注解为@Controller的控制器内的方法返回值中指定字符串,将其指定为视图的模板的文件名为”templates/index.html”。

那么,让我们来看一下指定在View中的模板。如前所述,模板引擎使用的是Thymleaf。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h1>Users</h1>
    <input type="button" onclick="location.href='/add'" value="追加ページ">
    <ul>
        <li th:each="user : ${users}">[[${user.name}]]</li>
    </ul>
</body>
</html>

${用户}

您可以将从控制器传递来的值用${}括起来使用。

th:each=”” -> th:each=””
th:each=”” -> 通过th:each属性来实现循环遍历

可以使用th:each=”variableName : ${iterableVariable}”来生成一个循环标签,其中variableName是用来存储每个元素的变量名,iterableVariable是可迭代的变量。

[[${user.name}]] 请将以下内容用中文进行释义,只需一种选项:

将这个[[中身]]作为标签夹在中,就会输出它作为该元素的文本。使用<标签名 th:text=”中身”>也表示同样的事情。

接下来,我将解释一下用于抽象化数据获取的仓库。


package com.example.springboot.repository

import com.example.springboot.entity.User
import org.springframework.data.repository.CrudRepository

interface UserRepository : CrudRepository<User, Int>

CrudRepository是一个标准接口或类,用于在Java中实现CRUD(创建、读取、更新和删除)操作的存储库。

这是一个接口,由Spring Data JPA提供,用于执行基本的表操作CRUD(Create、Read、Upload、Delete)。通过实现这个接口,可以实现CRUD操作。
此外,CrudRepository的第一个泛型参数指定要操作的实体类,第二个泛型参数指定实体类的ID类型。
一开始,我很奇怪的是,控制器中注入了UserRepository,但UserRepository的定义却是一个接口,我不明白如何在没有实现类的情况下生成实例,但显然Spring Data JPA在运行时会帮我们处理好这些事情。(我随意猜测的,请谅解)

最后,我们将查看在存储库中使用的实体的内容。这些实体对应于数据库的一个表。



@Entity
data class User(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var id: Int = 0,
        var name: String = ""
)

@Entity
实体

这个标注将与同名的类进行映射成表格。

@Id
ID

给与主键相对应的字段属性。

@GeneratedValue(strategy = GenerationType.IDENTITY)
– 自增策略:根据主键自动生成

在MySQL中,类似于AUTO_INCREMENT的指定。
它根据GenerationType后面的值而具有不同的意义。

値意味IDENTITYAUTO_INCREMENTを指定SEQUENCEDBのシーケンシャルオブジェクトを使用するTABLEシーケンシャルな値を管理するテーブルを生成するAUTODBごとに異なる方法を選択する

2. 转到用户添加页面

スクリーンショット 2020-12-07 22.46.51.png

接下来,我们来观察用户按下”添加页面”按钮并访问 “/add” 时控制器的行为。




@Controller
class MainController {



    @GetMapping("/add")
    fun showAddPage(): String  {
        return "add"
    }


}

在这里,我们使用GET方法将来自/add的请求映射到showAddPage()方法,并将模板指定为”add.html”。

现在我们来看一下这个模板的内容。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Add</title>
</head>
<body>
    <h1>Add</h1>
    <form action="/add" method="post">
        <label for="name">Name:</label>
        <input type="text" id="name" name="name">
        <button type="submit">追加</button>
    </form>
</body>
</html>

在这里没有特别的事情,只是简单地有一个名为“name”的表格和一个提交按钮。

3. 添加用户

スクリーンショット 2020-12-07 22.47.56.png

最後,我们将确认在收到POST方法请求时的/add控制器的行为。




@Controller
class MainController {
    @Autowired
    lateinit var userRepository: UserRepository



    @PostMapping("/add")
    fun addNewUser(@RequestParam name: String): String {
        userRepository.save(User(0, name))
        return "redirect:/"
    }
}

使用 @PostMapping 注解

我认为根据上述的@GetMapping注释可以推测出,如果以POST方法发送请求到/add,那么被注释的方法(本例中是addNewUser())将被调用。

@RequestParam 是一个用于获取请求参数的注解。

这表示指定的参数是请求参数。在这种情况下,通过表单输入的字符串将存储在变量name中。

userRepository.save(User(0, name)) 可以翻译为:userRepository.save(User(0, name))

在这里,我们创建一个具有传递给addNewUser()的name字段的实例,并将其保存到数据库中。
将ID设置为0的部分是为了避免将现有的ID放入其中时发生更新操作。因此,我们传递一个不存在的0,并执行创建操作。

返回 “redirect:/”

重定向到/。

以上で、今回的应用程序处理已经结束。在这里我稍微解释一下在application.properties中进行的数据库连接信息的设置。


spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:./h2db/db_example
spring.datasource.username=username
spring.datasource.password=password

spring.jpa.hibernate.ddl-auto=update
-> 春季.jpa.hibernate.ddl-auto=更新

在应用程序启动时,设置对数据库模式的操作。启用后,它会按照带有@Entity注解的类生成模式。配置值如下:

値意味note何もしないvalidate検証のみ行うupdate必要なテーブルがない場合は作成するcreate必要なテーブルを作成する。既存のテーブルは削除し、新規作成するcreate-drop必要なテーブルを作成する。セッション終了後に削除する

spring.datasource.driver-class-name=org.h2.Driver 转换成中文的一个选项:
spring.datasource.driver-class-name=org.h2.Driver

这是指定DB连接驱动程序的设置。

spring.datasource.url=jdbc:h2:/h2db/db_example

这是数据库的连接URL。这次我们将其保存在文件中。

spring.datasource.username=用户名

我是数据库的连接用户。

spring.datasource.password=密码

这是与数据库连接的密码。

最后

我第一次尝试使用静态类型的编程语言进行web开发。尽管规模还很小,无法与PHP×Laravel进行比较,但我打算尝试一些不同的东西,找出优点和缺点。我对ktor也很感兴趣,并且对微服务也很好奇,所以希望能够学习并深入研究它们。

以下是可以参考的信息。

横山恭大在2019年出版了《入门!实践!服务器端Kotlin》一书,由株式会社インプレス R&D出版。该书介绍了使用Spring Boot和Kotlin构建Web应用程序的方法,并记录了使用Thymeleaf和Spring Boot的方法。

考虑到大量的Java用户学习成本低且可以直接重用Java生态系统,这是Kotlin强大的优点,所以在某些方面是不可避免的。↩

除此之外,还有许多其他的JVM语言,如Scala、Groovy等。↩

如果有任何错误,请不吝告知,我会非常感激。↩

如Make、Ant、Maven等。↩

隐藏数据获取源(例如数据库或API)。实际上,Controller和Repository本应该通过Service层来进行交互,但为简单起见省略了。↩

广告
将在 10 秒后关闭
bannerAds