使用Spring Boot进行Kotlin服务器端入门教程
在平时的工作中,我使用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服务器框架,听起来也很不错。
事前准备
-
- 安装JDK(Java开发工具包)。
-
- 关于JDK,我们在以下项目中进行了解释,请阅读这里如果你不明白。
-
- 安装IntelliJ IDEA并进行设置。
- IntelliJ IDEA是Kotlin的开发者JetBrains创建的IDE,几乎完美地支持Kotlin。设置过程自己按照指引进行应该没问题。这里有一篇可能会有参考价值的文章。
我使用了Homebrew来安装这两个选项。顺便提一下,由于Kotlin附带在Intellij IDEA中,您无需单独安装即可使用。
预备知识
JDK可以在计算机上开发、编译和运行Java程序的软件平台。
在准备阶段,我安装了JDK,但也许还有人会怀疑:”JDK是用来做什么的?既然是Kotlin,只要有Kotlin的编译器和构建工具就可以执行了,不是吗?”。
为了解答这些疑问,我会简要介绍一下Java的执行环境。
请参考图1。首先,与常规的编译语言不同,Java程序编译后不会立即生成可执行文件。而是会生成称为Java类文件的Java字节码文件。
然后,字节码在每个操作系统上为JVM(Java虚拟机)提供的虚拟机上进行逐条解释并执行。由于这种机制,不需要为每个环境进行编译,只需编译一次即可在JVM提供的任何环境中运行。
换句话说,在运行或开发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是一款构建工具,可以自动进行编译、链接、测试等操作。
不过,我从未使用过其他四种构建工具,所以对于”这是好的!”这种说法并不很了解。
我认为使用Groovy语言进行配置是其重要特点之一,所以对于其语法至少要有一定的了解。以下文章可以作为参考。
适用于不熟悉Groovy的人的build.gradle基础入门文章。
春季引导
Spring Boot是将传统的Java框架Spring Framework变得更易于使用的框架。它通过使用注解来简化代码,消除了Spring Framework复杂的XML配置。由于我没有接触过Spring Framework,所以我不知道它是否会带来多大的便利,但我们将在后续实际运行中了解其表现。
环境建设
话虽长,但我们还是来进行Spring Boot的环境构建吧。
创建项目的草图
使用Intellij IDEA进行项目设置。
使浏览器能够进行注册和显示
这次我们做了一个简单的应用程序,可以通过浏览器添加和显示用户列表。
产出
目录结构
src
├── kotlin
│ └── com
│ └── example
│ └── springboot
│ ├── SpringbootApplication.kt
│ ├── controller
│ │ └── MainController.kt
│ ├── entity
│ │ └── User.kt
│ └── repository
│ └── UserRepository.kt
└── resources
├── application.properties
└── templates
├── add.html
└── index.html
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
詳盡
接下来,我们将按照以下的方式来解释应用程序的处理流程。
-
- 显示首页
-
- 跳转到用户添加页面
- 添加用户
展示首页
首先,我们来介绍当用户访问时,从控制器接收请求并进行处理的步骤。
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后面的值而具有不同的意义。
2. 转到用户添加页面
接下来,我们来观察用户按下”添加页面”按钮并访问 “/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. 添加用户
最後,我们将确认在收到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注解的类生成模式。配置值如下:
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层来进行交互,但为简单起见省略了。↩