В мире разработки программного обеспечения создание эффективных и масштабируемых веб-API является ключевой задачей для большинства проектов. Использование правильного инструментария и языка программирования может существенно облегчить этот процесс. В этой статье мы рассмотрим, как использование языка программирования Kotlin с фреймворком Spring Boot упрощает создание REST API, обеспечивая при этом высокую производительность и надежность.
Почему Kotlin и Spring Boot?
Kotlin, разработанный компанией JetBrains, и Spring Boot, один из наиболее популярных фреймворков для создания веб-приложений на языке Java, образуют мощное сочетание для разработки REST API. Kotlin предоставляет чистый и современный синтаксис, что упрощает разработку и делает код более читаемым. Spring Boot, с другой стороны, предоставляет множество готовых компонентов и упрощает конфигурацию, что позволяет сосредоточиться на бизнес-логике приложения.
Преимущества использования Kotlin и Spring Boot для разработки REST API:
Простота и скорость разработки: Kotlin позволяет писать более компактный код по сравнению с Java, что уменьшает количество ошибок и ускоряет разработку. Spring Boot, в свою очередь, предоставляет множество готовых компонентов, таких как автоматическая конфигурация, встроенные серверы приложений и механизмы управления зависимостями, что значительно упрощает создание новых проектов.
Интеграция с существующим кодом на Java: Kotlin полностью совместим с Java, что позволяет использовать существующие библиотеки и код на Java без изменений. Это особенно важно для проектов, которые уже используют Java, так как разработчики могут постепенно переходить на Kotlin, не изменяя существующего кода.
Богатый экосистем фреймворка Spring: Spring Boot предоставляет широкий выбор инструментов для создания веб-приложений, включая модули для работы с базами данных, безопасностью, микросервисной архитектурой и многое другое. Это позволяет разработчикам быстро создавать и масштабировать веб-API любой сложности.
Пример создания REST API с использованием Kotlin и Spring Boot
Давайте рассмотрим пример простого веб-API для управления задачами (TODO list) с использованием Kotlin и Spring Boot .
Переходим на сайт https://start.spring.io/ и конфигурируем приложение. Нам понадобятся Spring Web, Spring Data JPA, H2 Database, Spring Boot Devtools
Открываем наше сконфигурированное приложение в редакторе кода (я использую IntelliJ IDEA) и приступаем.
Создадим data class (src/main/kotlin/.../repository/model/Task.kt)
@Entity
@Table(name = "task")
data class Task(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long?,
@Column(name = "name", unique = true, nullable = false, length = 100)
val name: String
)
Создадим repository, который унаследуем от JpaRepository (src/main/kotlin/.../repository/TaskRepository.kt)
@Repository
interface TaskRepository : JpaRepository<Task, Long>
Создадим класс TaskNotFoundException для обработки исключений, когда наша задача не будет найдена (src/main/kotlin/.../exception/TaskNotFoundException.kt)
class TaskNotFoundException(httpStatus: HttpStatus, message: String): Exception ()
Создадим слой Service, в котором опишем логику нашего приложения (src/main/kotlin/.../service/TaskService.kt)
@Service
class TaskService(private val taskRepository: TaskRepository) {
// Получаем список всех задач
fun getAllTasks(): List<Task> = taskRepository.findAll()
//Получаем задачу по taskId
fun getTaskById(taskId: Long): Optional<Task> = taskRepository.findById(taskId)
?: throw TaskNotFoundException(HttpStatus.NOT_FOUND, "Задача с $taskId не найдена")
//Создаем задачу
fun createTask(task: Task): Task = taskRepository.save(task)
//Обновляем задачу, если она найдена, если нет выбрасываем исключение
fun updateTask(taskId: Long, task: Task): Task {
return if (taskRepository.existsById(taskId)) {
taskRepository.save(
Task(
id = task.id,
name = task.name
)
)
} else throw TaskNotFoundException(HttpStatus.NOT_FOUND, "Задача с $taskId не найдена")
}
//Удаляем задачу
fun deleteTaskById(taskId: Long) {
return if (taskRepository.existsById(taskId)) {
taskRepository.deleteById(taskId)
} else throw TaskNotFoundException(HttpStatus.NOT_FOUND, "Задача с $taskId не найдена")
}
}
Создадим контроллер (src/main/kotlin/.../controller/TaskController.kt). Думаю здесь уже понятно, комментарии излишни
@RestController
class EmployeeController(private val taskService: TaskService) {
@GetMapping("/tasks")
fun getAllTasks(): List<Task> = taskService.getAllTasks()
@GetMapping("/tasks/{id}")
fun getTaskById(@PathVariable("id") taskId: Long): Optional<Task> =
taskService.getTaskById(taskId)
@PostMapping("/tasks")
fun createEmployee(@RequestBody payload: Task): Task = taskService.createTask(payload)
@PutMapping("/tasks/{id}")
fun updateTaskById(@PathVariable("id") taskId: Long, @RequestBody payload: Task): Task =
taskService.updateTask(taskId, payload)
@DeleteMapping("/tasks/{id}")
fun deleteTask(@PathVariable("id") taskId: Long) = taskService.deleteTaskById(taskId)
}
В этом примере мы определили простой REST контроллер с помощью Spring Boot и Kotlin. Контроллер обрабатывает запросы для получения списка задач, получение конкретной задачи, добавления новой задачи, обновление задачи и удаления задачи по идентификатору.
Заключение
Kotlin и Spring Boot предоставляют мощный инструментарий для разработки REST API. Их сочетание обеспечивает простоту и скорость разработки, интеграцию с существующим кодом на Java и широкие возможности для масштабирования. Если вы рассматриваете создание нового веб-API, обязательно рассмотрите Kotlin и Spring Boot как один из вариантов разработки.
ЗЫ: Строго не судите. Первая статья для многочисленной аудитории. Жду критику/пожелания/предложения.
Спасибо за внимание.
Комментарии (11)
Fancryer
30.05.2024 07:44+1Зачем мешать Optional и исключения? Почему нельзя вернуть None и обработать в дальнейшем? Optional же как раз подразумевает наличие или отсутствие значения нужного типа. Ну а если нужно вернуть либо значение, либо исключение, почему нельзя использовать, например, Either из Arrow?
ApoFis_93 Автор
30.05.2024 07:44можно переписать таким образом
fun getTaskById(taskId: Long): Task = taskRepository.findById(taskId) .orElseThrow { TaskNotFoundException(HttpStatus.NOT_FOUND, "Задача с $taskId не найдена") }
Ksnz
30.05.2024 07:44+2А ещё лучше так
fun getTaskById(taskId: Long): Task = taskRepository.findByIdOrNull(taskId) ?: throw TaskNotFoundException(HttpStatus.NOT_FOUND, "Задача с $taskId не найдена")
Благо в Spring есть Kotlin extension
findByIdOrNull
дляCrudRepository
а работа сOptional
в котлин коде считается моветоном :)P.S. Ещё DELETE по спецификации REST должен быть идемпотентный, так что нет смысла проверять на наличие в репозитории, правильнее будет просто молча вызывать
deleteById
Kinski
А насколько хорошо data class работает в качестве entity? Есть подозрение, что неизменяемый id поломает вам работу при createTask, если конечно Вы не генерируете id на вызывающей стороне.
Но с общей мыслью согласен - котлин очень сахарный относительно джавы, да и со спрингом дружит отлично, так что всех агитирую новые проекты начинать на котлине)
kloun_za_2rub
Разве id как-то меняется при создании? Там же внутри просто распарс на sql запрос идет, а возвращаться вряд ли будет тот же объект
UPD: возможно тут имелось ввиду, что Long без "?", не увидел этого
ApoFis_93 Автор
id non nullable поле и генерируется на вызывающей стороне.
Возвращаться будет объект, который создали.
Kinski
ID может меняться при создании. Когда с условного фронта приходит набор полей для сохранения в БД (в виде объекта Task), то никаким ид (читай первичный ключ из БД) там не пахнет. И этот ID генерируется в БД, а затем присваивается в поле Task.id.
Ну и если посмотреть, как работает JPA и Hibernate под капотом, то там генерация запроса и основывается на наличии\отсутствии id поля. Если поля нет - то генерируется insert, иначе генерируется update.
ApoFis_93 Автор
можно сделать так
Тогда в запросе передавать id не надо. Генерироваться будет автоматически.
Спасибо. Дополнил.