Перейти к основному содержимому

Spring Boot на Kotlin — первая программа

Разработчику Архитектору

Spring Boot на Kotlin — первая программа

На JVM Spring Boot — популярный каркас для веб-сервисов, безопасности, работы с БД и облаком. Kotlin в Spring — полноценный язык — те же стартеры, что в Java, плюс data class, null-safety и короче контроллеры.

СтекКогда выбирать
KtorKotlin-native, явные маршруты, лёгкий старт
Spring Boot + KotlinКоманда на Spring, нужны Security, Data JPA, Spring Cloud

Java-версия сценария: Первая программа на Spring Framework. Обзор Spring: Spring Framework.

Перед стартом: первая программа на Kotlin.


Словарь терминов

ТерминПростыми словами
SpringКонтейнер, который создаёт объекты (бины) и связывает зависимости.
Spring BootSpring + автоконфигурация + встроенный Tomcat — быстрый старт.
СтартерГотовый набор зависимостей (spring-boot-starter-web).
@RestControllerКласс с HTTP-методами, ответ — JSON.
@ServiceСлой бизнес-логики.
Dependency InjectionSpring передаёт HelloService в конструктор контроллера.

Что получится

GET http://localhost:8080/api/hello?name=Анна → JSON {"message":"Привет, Анна!"}.


Проект (Gradle Kotlin DSL)

start.spring.io: Kotlin, Gradle, Spring Web, Java 17.

Код ITЗагрузка примера кода…

Разбор:

  • org.springframework.boot подключает задачи запуска и упаковки Spring-приложения.
  • io.spring.dependency-management выравнивает версии зависимостей экосистемы Spring.
  • kotlin("plugin.spring") автоматически открывает нужные классы для прокси/AOP.
  • toolchain фиксирует Java 17 как стандарт сборки для всех разработчиков и CI.
  • spring-boot-starter-web приносит MVC, Jackson и встроенный HTTP-сервер.

kotlin("plugin.spring") делает классы с аннотациями Spring открытыми для прокси — вручную писать open на каждый класс не нужно.


Точка входа

package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
ЭлементСмысл
@SpringBootApplicationВключить сканирование компонентов и автоконфигурацию.
runApplication<DemoApplication>(*args)Kotlin-обёртка над SpringApplication.run.
*argsРаспаковка массива аргументов в vararg.

Модель и сервис

data class HelloResponse(val message: String)

@Service
class HelloService {
fun greet(name: String?): HelloResponse {
val who = name?.trim().takeUnless { it.isNullOrEmpty() } ?: "мир"
return HelloResponse("Привет, $who!")
}
}

Разбор greet:

  1. name?.trim() — если name == null, всё выражение null.
  2. takeUnless { it.isNullOrEmpty() } — отбросить пустую строку после trim.
  3. ?: "мир" — значение по умолчанию.
  4. data class — Jackson (JSON) сериализует поле message автоматически.

Контроллер

@RestController
@RequestMapping("/api")
class HelloController(private val service: HelloService) {

@GetMapping("/hello")
fun hello(@RequestParam(required = false) name: String?): HelloResponse =
service.greet(name)
}
АннотацияРоль
@RestControllerОтветы методов → тело HTTP (JSON).
@RequestMapping("/api")Префикс для всех методов класса.
@GetMapping("/hello")Обработчик GET /api/hello.
@RequestParamПараметр из query: ?name=Анна.
Конструктор HelloServiceConstructor injection — рекомендуемый стиль Spring.

Сравнение с Ktor: в Ktor — первая программа маршруты в блоке routing { get(...) }; в Spring — аннотации на методах.


Запуск и проверка

./gradlew bootRun
curl -s "http://localhost:8080/api/hello?name=Анна"

Разбор:

  • ./gradlew bootRun запускает сервис через Gradle Wrapper без ручной сборки артефакта.
  • Spring Boot по умолчанию поднимает HTTP-сервер на localhost:8080.
  • curl -s отправляет запрос и печатает только ответ, без лишнего служебного вывода.
  • Query-параметр name=Анна проверяет цепочку от контроллера до бизнес-метода.

В IDEA: Run DemoApplicationKt.


Структура проекта для роста

Даже на первом сервисе полезно сразу разделить код по слоям:

ПакетСодержимое
controllerHTTP-эндпоинты и валидация входа
serviceбизнес-правила
repositoryдоступ к БД и внешним системам
dtoмодели запросов и ответов
configнастройки Spring, фильтры, бины

Так проще вводить тесты, безопасность и мониторинг без крупного рефакторинга.


HTTP-ошибки и единый формат ответа

Добавьте обработчик исключений, чтобы клиент всегда получал понятный JSON:

data class ErrorResponse(val code: String, val message: String)

@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException::class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
fun badRequest(e: IllegalArgumentException): ErrorResponse =
ErrorResponse(code = "BAD_REQUEST", message = e.message ?: "Некорректный запрос")
}

Разбор:

  • @RestControllerAdvice включает глобальную обработку исключений для REST-контроллеров.
  • @ExceptionHandler(...) связывает тип ошибки с конкретным обработчиком ответа.
  • @ResponseStatus(HttpStatus.BAD_REQUEST) фиксирует HTTP-код 400 для таких случаев.
  • ErrorResponse задаёт единый JSON-формат ошибки для всех клиентов API.

Так API становится предсказуемым для frontend и мобильных клиентов.


Конфигурация через application.yml

server:
port: 8080
spring:
application:
name: kotlin-demo

Разбор:

  • server.port задаёт порт, на котором слушает встроенный сервер Spring Boot.
  • spring.application.name определяет имя сервиса в логах и инфраструктурных инструментах.
  • YAML-иерархия автоматически маппится на конфигурационные свойства Spring.
  • Базовый файл удобно дополнять profile-настройками для dev и prod окружений.

Профили (application-dev.yml, application-prod.yml) помогают держать разные параметры для локальной разработки и продакшена.


Тест контроллера

Код ITЗагрузка примера кода…

Разбор:

  • @WebMvcTest поднимает только web-слой, поэтому тесты выполняются быстрее полного контекста.
  • @MockBean подменяет сервис и изолирует проверку поведения контроллера.
  • mockMvc.get(...) имитирует HTTP-вызов без реального сетевого сокета.
  • andExpect фиксирует API-контракт: ожидаемый статус и точное поле в JSON.

Тесты на уровне HTTP особенно полезны, если команда постепенно мигрирует с Java на Kotlin.


Kotlin и Java в одном проекте

  • Java-классы вызываются из Kotlin без обёрток.
  • Для JPA: kotlin("plugin.jpa") или allOpen / noArg.
  • Nullable Kotlin на границе с Java-кодом без аннотаций — риск NPE: проверяйте вход. Подробнее: Kotlin и Java — совместимость на практике.

Дополнительный сниппет — POST с телом запроса

data class CreateUserRequest(val name: String)
data class UserResponse(val id: Long, val name: String)

@RestController
@RequestMapping("/api/users")
class UserController {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun create(@RequestBody req: CreateUserRequest): UserResponse {
return UserResponse(id = 1L, name = req.name.trim())
}
}

Разбор:

  • @PostMapping связывает метод с HTTP POST-запросом на /api/users.
  • @RequestBody просит Spring десериализовать JSON-тело в Kotlin data class.
  • @ResponseStatus(HttpStatus.CREATED) выставляет код 201, корректный для создания ресурса.
  • Возврат UserResponse автоматически сериализуется в JSON через Jackson.
  • Разделение Request и Response моделей делает API-контракт явным и устойчивым к изменениям.

Частые ошибки

СимптомПричина
Invalid bean definitionКласс final без plugin.spring
404Неверный @RequestMapping или порт
Кракозябры в JSONUTF-8: server.servlet.encoding.charset=UTF-8 в application.properties

Что попробовать

  1. spring-boot-starter-data-jpaJava JPA.
  2. Spring Security на том же проекте.
  3. Сравнить объём кода с Ktor на том же API.

Дальше

Kotlin ↔ Java · Ktor · корутины · о разделе


Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.


В подборках

Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:

Первые шаги (маршрут подборки) — Compose Multiplatform — первая программа, Первая программа на Gin, Ktor — первая программа, Первая программа на Echo, Первая программа на Symfony, Первая программа на Fiber.