12 tháng 9 năm 2023 - Công Nghệ Thông Tin
Bài viết này sẽ tìm hiểu về “cách xây dựng dịch vụ RESTful API sử dụng Spring Boot và Kotlin”. Chúng ta sẽ tiến hành xây dựng một dự án thực tế để minh họa toàn bộ quy trình tạo ra dịch vụ RESTful API bằng Kotlin, trong đó khung tổng thể sử dụng Spring Boot, quản lý phụ thuộc sử dụng Gradle, truy cập cơ sở dữ liệu sử dụng MyBatis và cơ sở dữ liệu là MySQL được cài đặt cục bộ.
Bài viết chia thành ba phần chính: Tạo dự án mẫu, Viết mã nguồn nghiệp vụ, và Kiểm thử API.
Dưới đây là danh sách các phiên bản phần mềm hoặc khung mà dự án này sử dụng:
JDK: Amazon Corretto 17.0.8
Kotlin: 1.9.10
Gradle: 8.3
Spring Boot: 3.1.3
MySQL: 8.1.0
1. Tạo Dự Án Mẫu
Đầu tiên, sử dụng công cụ Spring Initializr để tạo một dự án mẫu trống. Các tùy chọn như sau:
Project: Gradle - Kotlin
Ngôn ngữ: Kotlin
Spring Boot: 3.1.3
Gói: Jar
Java: 17
Phụ thuộc: Spring Web, MyBatis Framework và MySQL Driver
Sau khi nhấn GENERATE, một dự án mẫu sẽ được tạo và tải xuống máy tính cục bộ. Giải nén tệp tin đã tải về và nhập vào IDE để xem toàn bộ cấu trúc của dự án mẫu. Cấu trúc thư mục của dự án Demo như sau:
demo
|--- gradle/
|--- src/main/
| |--- resources/
| \--- kotlin/
| \--- com.example.demo.DemoApplication.kt
|--- src/test/kotlin/
| \--- com.example.demo.DemoApplicationTests.kt
|--- gradlew
|--- settings.gradle.kts
\--- build.gradle.kts
Chúng ta sẽ tập trung phân tích hai phần quan trọng của dự án này: Tập tin mô tả Gradle và lớp khởi chạy chương trình DemoApplication.kt
.
1.1 Tập tin mô tả Gradle
Bạn có thể thấy rằng đây là một dự án Gradle tiêu chuẩn. Trong tập tin mô tả build.gradle.kts
, chúng ta thay phiên bản Kotlin mới nhất là 1.9.10 (kotlin("jvm") version "1.9.10"
), đồng thời loại bỏ các phụ thuộc không cần thiết. Nội dung hoàn chỉnh của tập tin như sau:
// build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "3.1.3"
id("io.spring.dependency-management") version "1.1.3"
kotlin("jvm") version "1.9.10"
kotlin("plugin.spring") version "1.9.10"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java {
sourceCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.2")
runtimeOnly("com.mysql:mysql-connector-j")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs += "-Xjsr305=strict"
jvmTarget = "17"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
Một số điểm cần lưu ý về tập tin này:
- Sử dụng plugin
kotlin("plugin.spring")
: nohu club tai game nổ hũ đổi thưởng Điều này là cần thiết vì trong Kotlin, lớp mặc định làfinal
(không thể kế thừa). Plugin này giúp chuyển đổi các lớp được đánh dấu bằng chú thích Spring thànhopen
, cho phép Spring hoạt động bình thường. - MySQL Driver được thêm vào với kiểu
runtimeOnly
: Điều này có nghĩa là gói này chỉ cần thiết trong giai đoạn chạy ứng dụng chứ không cần thiết trong giai đoạn biên dịch. - Tham số biên dịch
-Xjsr305=strict
: Được sử dụng để kích hoạt chế độ kiểm tra nghiêm ngặt JSR-305, giúp tận dụng tối đa tính năng kiểm tra an toàn rỗng của Kotlin.
1.2 Lớp Khởi Chạy Chương Trình DemoApplication.kt
Sau khi phân tích tập tin mô tả Gradle, chúng ta cùng xem lớp khởi chạy chương trình DemoApplication.kt
:
// src/main/kotlin/com/example/demo/DemoApplication.kt
package [nhà cái 888b](/post/6810/) com.example.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class [Lô Đề Nohu76](https://www.fskxr.com) DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
Lưu ý:
class DemoApplication
là một lớp rỗng, ngoài việc được đánh dấu bằng chú thích@SpringBootApplication
, không chứa bất kỳ thuộc tính hay phương thức nào nên không cần thêm dấu ngoặc nhọn.- Phương thức khởi chạy
main
là một hàm đứng riêng lẻ (top-level function) và không thuộc về lớpDemoApplication
.
2. Viết Mã Nguồn Nghiệp Vụ
Dự án mẫu đã sẵn sàng, bây giờ chúng ta có thể bắt đầu viết mã nguồn nghiệp vụ. Tình huống nghiệp vụ là cung cấp các API cho phép thêm, sửa, xóa và tra cứu thông tin người dùng (User).
Dự án áp dụng kiến trúc MVC truyền thống, cấu trúc thư mục mã nguồn như sau:
demo
|--- src/main/
| |--- resources/
| | |--- application.yaml
| | \--- shema.sql
| \--- kotlin/
| \--- com.example.demo/
| |--- controller/
| | \--- UserController.kt
| |--- service/
| | |--- UserService.kt
| | \--- impl/
| | \--- UserServiceImpl.kt
| |--- dao/
| | \--- UserMapper.kt
| |--- model/
| | \--- User.kt
| \--- DemoApplication.kt
...
|--- gradlew
\--- build.gradle.kts
Dưới đây là phân tích chi tiết từng lớp ở các tầng Controller, Service, DAO, Model cũng như các tập tin cấu hình và script cơ sở dữ liệu.
2.1 Lớp Controller
Tầng Controller chỉ bao gồm một lớp UserController.kt
, chịu trách nhiệm triển khai các chức năng thêm, sửa, xóa và tra cứu thông tin người dùng.
Mã nguồn đầy đủ như sau:
// src/main/kotlin/com/example/demo/controller/UserController.kt
package com.example.demo.controller
import com.example.demo.model.User
import com.example.demo.service.UserService
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/users")
class UserController(val userService: UserService) {
@GetMapping("/")
fun listAll() = userService.listAll()
@GetMapping("/{id}")
fun getById(@PathVariable id: Long) = userService.getById(id)
@PatchMapping("/")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun update(@RequestBody user: User) {
user.id?.let { userService.update(user) }
}
@PostMapping("/")
@ResponseStatus(HttpStatus.CREATED)
fun save(@RequestBody user: User) = userService.save(user)
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun deleteById(@PathVariable("id") id: Long) = userService.deleteById(id)
}
Đoạn mã này rất quen thuộc đối với những ai đã làm việc với Java, chỉ khác biệt về cú pháp Kotlin.
2.2 Tầng Service
Tầng Service cung cấp dịch vụ cho tầng Controller, bao gồm giao diện và lớp thực hiện.
Hai tệp UserService.kt
và UserServiceImpl.kt
có nội dung như sau:
// src/main/kotlin/com/example/demo/service/UserService.kt
package com.example.demo.service
import com.example.demo.model.User
interface UserService {
fun listAll(): List<User>
fun getById(id: Long): User?
fun update(user: User)
fun save(user: User)
fun deleteById(id: Long)
}
// src/main/kotlin/com/example/demo/service/impl/UserServiceImpl.kt
package com.example.demo.service.impl
import com.example.demo.dao.UserMapper
import com.example.demo.model.User
import com.example.demo.service.UserService
import org.springframework.stereotype.Service
@Service
class UserServiceImpl(val userMapper: UserMapper) : UserService {
override fun listAll(): List<User> = userMapper.listAll()
override fun getById(id: Long): User? = userMapper.getById(id)
override fun update(user: User) = userMapper.update(user)
override fun save(user: User) = userMapper.save(user)
override fun deleteById(id: Long) = userMapper.deleteById(id)
}
Logic của tầng Service khá đơn giản, chỉ gọi các phương thức từ MyBatis Mapper để thực hiện các chức năng tương ứng.
2.3 Tầng DAO
Tầng DAO sử dụng MyBatis để tương tác với cơ sở dữ liệu, không cần cấu hình file XML phức tạp mà thay vào đó sử dụng chú thích để xử lý.
Mã nguồn tệp UserMapper.kt
như sau:
// src/main/kotlin/com/example/demo/dao/UserMapper.kt
package com.example.demo.dao
import com.example.demo.model.User
import org.apache.ibatis.annotations.*
@Mapper
interface UserMapper {
@Select("SELECT id, name, age FROM user")
fun listAll(): List<User>
@Select("SELECT id, name, age FROM user WHERE id = #{id}")
fun getById(id: Long): User?
@Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")
fun update(user: User)
@Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
fun save(user: User)
@Delete("DELETE FROM user WHERE id = #{id}")
fun deleteById(id: Long)
}
2.4 Lớp Model
Model được sử dụng để truyền dữ liệu giữa các tầng, nhận dữ liệu từ cơ sở dữ liệu và cuối cùng chuyển hóa thành JSON trả về cho API. Nó cũng chịu trách nhiệm chuyển đổi yêu cầu JSON từ phía người gọi API thành đối tượng Kotlin.
Trong dự án này, chỉ có một Model là User.kt
:
// src/main/kotlin/com/example/demo/model/User.kt
package com.example.demo.model
data class User(val id: Long?, val name: String, val age: Int)
2.5 Tập Tin Cấu Hình
Chúng ta sử dụng định dạng YAML cho tập tin cấu hình Spring, chủ yếu cấu hình thông tin kết nối cơ sở dữ liệu và chỉ định vị trí của script SQL khởi tạo.
# src/main/resources/application.yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
sql:
init:
schema-locations: classpath:schema.sql
mode: always
Thông tin kết nối chỉ đến cơ sở dữ liệu MySQL cục bộ, mỗi lần khởi động dự án sẽ tự động thực thi script schema.sql
trong thư mục resources
.
2.6 Script Cơ Sở Dữ Liệu
Script tạo bảng như sau (cần thực thi thủ công):
-- src/main/resources/database.sql
CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
File SQL chứa lệnh tạo bảng schema.sql
(được thực thi tự động):
-- src/main/resources/schema.sql
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id BIGINT AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
age INT,
PRIMARY KEY (id)
);
Như vậy, mã nguồn nghiệp vụ hỗ trợ các chức năng thêm, sửa, xóa và tra cứu thông tin người dùng đã hoàn tất.
3. Kiểm Thử API
Dưới đây là hướng dẫn khởi động dự án và tiến hành kiểm thử API.
3.1 Khởi Động Dự Án
Thực thi lệnh Gradle sau tại thư mục gốc của dự án để khởi động:
./gradlew bootRun
3.2 Kiểm Thử API
Sử dụng lệnh CURL để kiểm thử API. Tạo một User mới:
curl -X POST -H 'Content-Type: application/json' -d '{"name": "Larry", "age": 28}'
Tra cứu tất cả Users, phát hiện User vừa tạo có ID là 1:
curl -X GET
[{"id":1,"name":"Larry","age":28}]
Cập nhật thông tin User có ID là 1:
curl -X PATCH -H 'Content-Type: application/json' -d '{"id": 1, "name": "Larry2", "age": 29}'
Tra cứu User có ID là 1, phát hiện thông tin đã được cập nhật thành công:
curl -X GET
{"id":1,"name":"Larry2","age":29}
Xóa User có ID là 1, sau đó tra cứu lại tất cả Users, phát hiện danh sách trống:
curl -X DELETE
curl -X GET
[]
Như vậy, tất cả các API liên quan đến chức năng thêm, sửa, xóa và tra cứu thông tin người dùng đều hoạt động tốt.
Tóm lại, chúng ta đã sử dụng Kotlin + Gradle + Spring Boot + MyBatis để xây dựng một dự án API mẫu, viết mã nguồn nghiệp vụ và tiến hành kiểm thử. Quá trình này cho thấy việc xây dựng dịch vụ RESTful API sử dụng Spring Boot và Kotlin là khá đơn giản và khả thi.
Toàn bộ mã nguồn của dự án mẫu đã được đăng tải trên GitHub cá nhân của tôi, mọi người có thể theo dõi hoặc Fork.
[1] Bắt Đầu Với Spring Boot và Kotlin | Tài Liệu Kotlin - kotlinlang.org [2] Xây Dựng Ứng Dụng Web Sử Dụng Spring Boot và Kotlin | Spring - spring.io [3] Xây Dựng REST API Với Spring Boot và Kotlin | Blog Công Nghệ Anirban - theanirban.dev [4] Các Ví Dụ Sử Dụng MyBatis Với Kotlin | GitHub - github.com
#Kotlin #Spring #Gradle