Quarkus — первая программа
Для читателей, которые уже прошли первую программу на Java и базовое ООП. Quarkus — фреймворк для JVM и GraalVM Native Image: быстрый старт процесса, низкое потребление памяти, удобный dev-режим с hot reload.
Практикум идёт по шагам — генерация проекта, REST-ресурс, dev-режим, конфигурация, упаковка JAR, опционально native-сборка и учебный CRUD "Заметки".
| Шаг | Тема | Зачем |
|---|---|---|
| 0 | JDK, Maven wrapper | Убедиться, что среда готова |
| 1 | code.quarkus.io | Скелет проекта с extensions |
| 2 | REST-ресурс | HTTP GET и JSON-ответ |
| 3 | ./mvnw quarkus:dev | Hot reload без полного рестарта |
| 4 | application.properties | Порт, профили, логи |
| 5 | ./mvnw package | Fast-jar для деплоя |
| 6 | -Pnative | Бинарник без отдельной JVM (опционально) |
| 7 | CRUD "Заметки" | Закрепить REST на in-memory store |
| Материал | Зачем |
|---|---|
| Maven и Gradle в Java | Структура pom.xml, wrapper |
| Records в Java | Компактные DTO для JSON |
| REST API — теория | Методы HTTP, коды ответов |
| Micronaut — первая программа | Другой cloud-native стек на JVM |
| Virtual Threads | Параллельные запросы на Java 21+ |
| Экосистема Java | Обзор фреймворков |
Навигация по разделу Java
- Вы здесь: Quarkus — первая программа
- База: Первая программа на Java, Структура и сборки
- Соседний фреймворк: Micronaut — первая программа
- Модели данных: Records в Java — практическое руководство
- Spring-маршрут: Spring Boot
| Термин | Значение |
|---|---|
| Quarkus | Java-фреймворк от Red Hat (Jakarta EE + MicroProfile) |
| Extension | Модуль зависимостей (resteasy-reactive, jdbc, …) |
| Dev mode | ./mvnw quarkus:dev — перезагрузка при изменении кода |
| Native image | Бинарник без отдельной JVM (опционально) |
| Fast-jar | Layout quarkus-run.jar + lib/ + app/ |
Команды удобно выполнять во вкладке Терминал VS Code (Ctrl+`) или во встроенном терминале IntelliJ IDEA. Отладка — статья "Отладка", настройка Java — IntelliJ IDEA.
Шаг 0 — проверка окружения
| Компонент | Версия |
|---|---|
| JDK | 17+ (рекомендуется 21 LTS) |
| Maven | В проекте есть mvnw wrapper — отдельная установка не обязательна |
| IDE | IntelliJ IDEA / VS Code + Extension Pack for Java |
Проверка в терминале:
java -version
./mvnw -v
Разбор:
java -versionпоказывает версию JDK. Для новых Quarkus-проектов берите 17 или 21 LTS../mvnw -vпроверяет Maven wrapper — он скачает нужную версию Maven при первом запуске.- На Windows используйте
mvnw.cmdвместо./mvnw, если shell не понимает./.
Общая теория — байт-код и виртуальные машины, основы языка Java. Quarkus работает поверх JVM; native-сборка компилирует приложение заранее через GraalVM.
Создайте рабочую папку, например projects, и откройте её в редакторе — дальше все команды выполняются из каталога распакованного проекта.
Шаг 1 — генерация проекта
Откройте code.quarkus.io — онлайн-генератор с выбором extensions.
- Group:
com.example - Artifact:
hello-quarkus - Build tool: Maven (или Gradle — команды ниже для Maven)
- Extensions: RESTEasy Reactive, REST Jackson (JSON)
- Нажмите Generate your application → скачайте ZIP
- Распакуйте архив и откройте каталог в IDE
Структура после распаковки:
hello-quarkus/
src/
main/
java/com/example/
resources/
test/java/com/example/
pom.xml
mvnw
mvnw.cmd
README.md
Разбор каталогов:
src/main/java— исходники приложения; пакетcom.exampleсовпадает с Group/Artifact на генераторе.src/main/resources— конфигурация (application.properties), статика.src/test/java— модульные и интеграционные тесты.pom.xml— зависимости и Quarkus Maven plugin.mvnw/mvnw.cmd— wrapper, фиксирует версию Maven для команды.
Подробнее про Maven — структура проектов Java.
Extension добавляет зависимости и автоконфигурацию. Без rest-jackson JSON-ответы могут не сериализоваться. После изменения pom.xml в dev-режиме нужен полный рестарт quarkus:dev.
Что Quarkus добавляет в pom.xml
Типичные блоки (сокращённо):
<properties>
<quarkus.platform.version>3.x.x</quarkus.platform.version>
</properties>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus.platform</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Разбор:
- Версия платформы Quarkus задаётся одним property — все extensions согласованы.
quarkus-maven-pluginобрабатывает dev-режим, сборку и native-профиль.- BOM (Bill of Materials) в
dependencyManagementфиксирует совместимые версии библиотек.
Шаг 2 — REST-ресурс
Создайте файл src/main/java/com/example/GreetingResource.java:
package com.example;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/hello")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello from Quarkus";
}
@GET
@Path("/{name}")
@Produces(MediaType.APPLICATION_JSON)
public Greeting greet(String name) {
return new Greeting("Привет, " + name + "!");
}
public record Greeting(String message) {}
}
Разбор построчно:
@Path("/hello")— JAX-RS аннотация: префикс URL для всех методов класса.@GET— обработчик HTTP GET.@Produces(MediaType.TEXT_PLAIN)— ответ в виде обычного текста.@Path("/{name}")на втором методе — path-параметр; Quarkus передаёт его в аргументname.@Produces(MediaType.APPLICATION_JSON)— Jackson сериализует объект в JSON.record Greeting— компактная модель ответа (Records в Java).
Как запрос доходит до метода
Теория REST — REST API, HTTP как основа веб-интеграций.
Валидация path-параметра (опционально)
Для учебного API можно отклонять пустые имена:
@GET
@Path("/{name}")
@Produces(MediaType.APPLICATION_JSON)
public Greeting greet(@jakarta.ws.rs.PathParam("name") String name) {
if (name == null || name.isBlank()) {
throw new jakarta.ws.rs.BadRequestException("Имя не может быть пустым");
}
return new Greeting("Привет, " + name.trim() + "!");
}
Разбор:
@PathParam("name")явно связывает сегмент URL с параметром.BadRequestExceptionдаёт HTTP 400 — см. таблицу кодов в REST API.
Шаг 3 — dev-режим
Из корня проекта:
./mvnw quarkus:dev
Windows:
mvnw.cmd quarkus:dev
Первый запуск скачивает зависимости — это нормально. В консоли появится строка вроде Listening on: http://0.0.0.0:8080.
| URL | Ожидаемый ответ |
|---|---|
http://localhost:8080/hello | Hello from Quarkus (текст) |
http://localhost:8080/hello/Аня | {"message":"Привет, Аня!"} |
Проверка через curl:
curl http://localhost:8080/hello
curl http://localhost:8080/hello/World
curl -i http://localhost:8080/hello/World
Разбор curl:
- Без
-iпечатается только тело ответа. - С
-iвидны заголовки —Content-Type: application/jsonдля второго запроса. Connection refused— сервер не запущен или другой порт.
Hot reload
- Оставьте
quarkus:devработать. - Измените строку в методе
hello(), например на"Привет из Quarkus". - Сохраните файл — Quarkus перезагрузит класс без полного рестарта JVM.
- Повторите
curl— текст изменится.
Если на code.quarkus.io добавлен extension quarkus-dev-ui, откройте http://localhost:8080/q/dev/ — список эндпоинтов, конфигурация, health. Без extension страница может быть недоступна.
Остановка dev-сервера — Ctrl+C в терминале.
Шаг 4 — конфигурация
Файл src/main/resources/application.properties:
quarkus.http.port=8080
quarkus.application.name=hello-quarkus
quarkus.log.console.enable=true
quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n
Разбор:
quarkus.http.port— порт HTTP-сервера (по умолчанию 8080).quarkus.application.name— имя в логах и метриках.- Формат лога настраивается отдельно для dev и prod.
Профили
Quarkus поддерживает профили %dev, %test, %prod:
%dev.quarkus.log.console.enable=true
%dev.quarkus.http.port=8080
%prod.quarkus.log.console.enable=false
%prod.quarkus.http.port=8080
Активация prod-профиля при запуске JAR:
java -jar target/quarkus-app/quarkus-run.jar -Dquarkus.profile=prod
Переменные окружения с префиксом QUARKUS_ переопределяют properties:
export QUARKUS_HTTP_PORT=9090
./mvnw quarkus:dev
Конфигурации приложений — конфигурации и данные, переменные окружения.
Шаг 5 — упаковка JAR
Сборка production-артефакта:
./mvnw package
Запуск:
java -jar target/quarkus-app/quarkus-run.jar
Quarkus собирает fast-jar layout:
target/quarkus-app/
quarkus-run.jar # точка входа
lib/ # зависимости
app/ # ваш код и resources
quarkus/ # метаданные Quarkus
Разбор:
quarkus-run.jar— тонкий launcher; основной код вapp/.- Такой layout быстрее стартует, чем один fat-jar со всем внутри.
- Для Docker часто копируют весь каталог
quarkus-app/.
Проверка после запуска:
curl http://localhost:8080/hello
Для боевого сервера — process manager (systemd, Kubernetes), секреты в переменных окружения, HTTPS за reverse proxy (Nginx, Caddy). Пример прокси — Nginx — конфиги под задачу.
Шаг 6 — native-сборка (опционально)
Требуется GraalVM или Mandrel (дистрибутив Red Hat):
./mvnw package -Pnative
./target/hello-quarkus-1.0.0-SNAPSHOT-runner
Разбор:
- Профиль
-Pnativeзапускает компиляцию Native Image — процесс долгий (минуты). - Итог — один исполняемый файл без отдельной JVM на сервере.
- Cold start — миллисекунды; RAM ниже, чем у JVM-режима.
| Режим | Плюс | Минус |
|---|---|---|
JVM (quarkus:dev, quarkus-run.jar) | Быстрая разработка, полная совместимость reflection | Нужна JVM на сервере, выше RAM |
| Native | Быстрый cold start, меньше RAM | Долгая сборка, ограничения reflection и dynamic class loading |
Для учебного REST достаточно JVM-режима. Native имеет смысл в serverless (AWS Lambda, Knative) и контейнерах с жёстким лимитом памяти.
Шаг 7 — учебный REST API "Заметки"
Расширим проект до минимального CRUD в памяти — тот же сценарий, что в первой программе на Node.js и Gin.
Что получится
| Метод | Путь | Действие |
|---|---|---|
| GET | /notes | Список заметок |
| POST | /notes | Создать заметку (JSON) |
| DELETE | /notes/{id} | Удалить по id |
| GET | /health | Сервер жив |
Record для заметки
package com.example;
public record Note(long id, String text) {}
Сервис in-memory
package com.example;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
@ApplicationScoped
public class NoteStore {
private final AtomicLong seq = new AtomicLong(1);
private final List<Note> notes = new ArrayList<>();
public List<Note> findAll() {
return List.copyOf(notes);
}
public Note add(String text) {
if (text == null || text.isBlank()) {
throw new IllegalArgumentException("text пустой");
}
Note note = new Note(seq.getAndIncrement(), text.trim());
notes.add(note);
return note;
}
public boolean delete(long id) {
return notes.removeIf(n -> n.id() == id);
}
public Optional<Note> findById(long id) {
return notes.stream().filter(n -> n.id() == id).findFirst();
}
}
Разбор:
@ApplicationScoped— один экземпляр на приложение (CDI).AtomicLongвыдаёт уникальные id без блокировок в учебном примере.List.copyOfвозвращает неизменяемую копию наружу.
REST-ресурс
package com.example;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;
@Path("/notes")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class NoteResource {
@Inject
NoteStore store;
@GET
public List<Note> list() {
return store.findAll();
}
@POST
public Response create(CreateNoteRequest body) {
try {
Note created = store.add(body.text());
return Response.status(Response.Status.CREATED).entity(created).build();
} catch (IllegalArgumentException ex) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(new ErrorBody(ex.getMessage()))
.build();
}
}
@DELETE
@Path("/{id}")
public Response delete(@PathParam("id") long id) {
if (store.delete(id)) {
return Response.noContent().build();
}
return Response.status(Response.Status.NOT_FOUND)
.entity(new ErrorBody("id не найден"))
.build();
}
public record CreateNoteRequest(String text) {}
public record ErrorBody(String error) {}
}
Health-check
package com.example;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/health")
public class HealthResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String ok() {
return "ok";
}
}
Проверка curl
curl http://localhost:8080/health
curl http://localhost:8080/notes
curl -X POST http://localhost:8080/notes \
-H "Content-Type: application/json" \
-d "{\"text\": \"Изучить Quarkus\"}"
curl http://localhost:8080/notes
curl -X DELETE http://localhost:8080/notes/1
curl http://localhost:8080/notes
Разбор кодов:
- 201 Created — заметка создана, тело содержит объект с
id. - 204 No Content — успешное DELETE без тела.
- 400 — пустой
text. - 404 — id не существует.
Данные в памяти теряются при перезапуске — для persistence подключите JDBC и Panache (Hibernate и JPA).
Типичные ошибки и troubleshooting
| Симптом | Причина | Что сделать |
|---|---|---|
| Port 8080 busy | Другой сервис на порту | quarkus.http.port=8081 в properties |
404 на /hello | Неверный @Path или класс вне сканирования | Пакет com.example, аннотации на классе и методах |
| JSON не сериализуется | Нет Jackson extension | Добавьте rest-jackson на code.quarkus.io |
| Dev mode не перезагружает | Редактировали pom.xml | Полный рестарт quarkus:dev |
Connection refused | Сервер не запущен | ./mvnw quarkus:dev или java -jar ... |
| Пустой POST body | Нет @Consumes(APPLICATION_JSON) | Добавьте аннотацию на ресурс или метод |
java не найден | JDK не в PATH | Переустановить JDK — первая программа |
| Native build failed | Нет GraalVM/Mandrel | Установить Mandrel или собирать только JVM |
| Кириллица в JSON | Кодировка терминала | UTF-8 в IDE и терминале |
При 404 Quarkus печатает зарегистрированные маршруты при старте. Ищите строки Registered JAX-RS resource — там список путей.
Упражнения
- Добавьте
GET /hello/{name}/formal— JSON с полемformal: trueи текстом «Уважаемый(ая),{name}!». - Расширьте
NoteполемcreatedAtтипаInstant— сериализация в ISO-8601. - Напишите тест
GreetingResourceTestс@QuarkusTest— проверка/helloчерез REST Assured. - Вынесите порт в переменную окружения и запустите два экземпляра на разных портах.
- Добавьте extension
quarkus-smallrye-openapi— откройте Swagger UI на/q/swagger-ui.
Подсказка к упражнению 3
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
@QuarkusTest
class GreetingResourceTest {
@Test
void testHello() {
given()
.when().get("/hello")
.then()
.statusCode(200)
.body(is("Hello from Quarkus"));
}
}
Запуск: ./mvnw test. JUnit — тестирование в Java.
FAQ
Quarkus заменяет Spring Boot?
Нет. Это альтернативный стек на Jakarta EE и MicroProfile. Spring остаётся самым распространённым выбором в enterprise — см. Spring Boot. Quarkus выбирают за dev-опыт, native и низкое потребление памяти.
Нужен ли Spring для Quarkus?
Нет. Quarkus использует CDI (Contexts and Dependency Injection), JAX-RS, Hibernate через свои extensions.
Можно ли писать на Kotlin?
Да. На code.quarkus.io выберите Kotlin как язык — структура та же.
Чем Quarkus отличается от Micronaut?
Оба ориентированы на cloud-native и native image. Quarkus теснее связан с экосистемой Red Hat и Jakarta EE; Micronaut делает акцент на compile-time DI — см. Micronaut — первая программа.
Когда собирать native?
Когда важны cold start и RAM (Kubernetes scale-to-zero, serverless). Для локальной разработки достаточно JVM.
Где хранить секреты?
Не в application.properties в git. Используйте переменные окружения или vault — конфигурации и данные.
Как превратить учебный API в рабочий сервис
План эволюции
- In-memory store для отладки маршрутов (вы здесь).
- Подключение PostgreSQL через
quarkus-jdbc-postgresqlи Panache. - Миграции Flyway или Liquibase.
- Единый формат ошибок и валидация Bean Validation (
@NotBlank). - Тесты
@QuarkusTest+ Testcontainers (Testcontainers). - Метрики Micrometer, health SmallRye, логи в JSON.
Базовые контракты API
| Эндпоинт | Успех | Ошибка клиента | Ошибка сервера |
|---|---|---|---|
GET /notes | 200 + массив | — | 500 |
POST /notes | 201 + объект | 400 при пустом text | 500 |
DELETE /notes/{id} | 204 | 404, если id не найден | 500 |
Шаг 8 — тесты с @QuarkusTest
Quarkus интегрируется с JUnit 5 и REST Assured. Файл src/test/java/com/example/GreetingResourceTest.java:
package com.example;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.*;
@QuarkusTest
class GreetingResourceTest {
@Test
void helloPlainText() {
given()
.when().get("/hello")
.then()
.statusCode(200)
.contentType("text/plain")
.body(is("Hello from Quarkus"));
}
@Test
void helloJsonWithName() {
given()
.when().get("/hello/Quarkus")
.then()
.statusCode(200)
.contentType("application/json")
.body("message", containsString("Quarkus"));
}
}
Запуск:
./mvnw test
Разбор:
@QuarkusTestподнимает приложение на random port — REST Assured ходит in-process.- Отдельный
@QuarkusIntegrationTestиспользует упакованный артеfact — ближе к prod. - Тесты для
NoteResourceпроверяют цепочку POST → GET → DELETE.
Тестирование — JUnit в Java, разработка и отладка.
Таблица сценариев NoteResourceTest
| Тест | Запрос | Ожидание |
|---|---|---|
| listEmpty | GET /notes | 200, [] |
| createOk | POST body | 201, id в JSON |
| createBad | POST {} | 400 |
| deleteOk | DELETE /notes/1 | 204 |
| deleteMissing | DELETE /notes/999 | 404 |
Шаг 9 — OpenAPI и документация API
На code.quarkus.io добавьте extension SmallRye OpenAPI.
После перезапуска dev-режима:
| URL | Содержимое |
|---|---|
/q/openapi | Спецификация YAML/JSON |
/q/swagger-ui | Интерактивная документация |
Аннотации на ресурсе (опционально):
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
@GET
@Operation(summary = "Список заметок")
@APIResponse(responseCode = "200", description = "OK")
public List<Note> list() { ... }
Теория контрактов — REST API.
Шаг 10 — Docker и деплой
JVM-образ (упрощённо)
src/main/docker/Dockerfile.jvm часто генерируется Quarkus. Сборка:
./mvnw package
docker build -f src/main/docker/Dockerfile.jvm -t hello-quarkus-jvm .
docker run -i --rm -p 8080:8080 hello-quarkus-jvm
Проверка:
curl http://localhost:8080/health
Разбор:
- В образ копируется каталог
quarkus-app/, не один fat-jar. - Переменные окружения
QUARKUS_HTTP_PORTработают так же, как локально. - Для Kubernetes добавьте liveness probe на
/health— контейнеры и оркестрация.
Native-образ
С профилем native и Dockerfile.native cold start минимален — подходит для scale-to-zero. Сборка дольше — используйте CI cache.
Не копируйте пароли БД в образ. Передавайте через secrets Kubernetes или переменные окружения runtime — конфигурации и данные.
Шаг 11 — health, metrics и логи
Extension SmallRye Health добавляет:
| Endpoint | Назначение |
|---|---|
/q/health/live | Процесс жив |
/q/health/ready | Готов принимать трафик |
Extension Micrometer экспортирует метрики Prometheus на /q/metrics.
Структурированные логи в prod — JSON через quarkus.log.console.json=true в профиле %prod.
Мониторинг в проде — логирование и observability.
Quarkus и Spring Boot — ориентиры выбора
| Критерий | Quarkus | Spring Boot |
|---|---|---|
| Стандарты | Jakarta EE, JAX-RS, CDI | Spring собственный стек |
| Dev experience | quarkus:dev, Dev UI | spring-boot-devtools |
| Экосystem | Red Hat, KNative | Широчайшая база вакансий |
| Native | Зрелая поддержка | Spring Native / AOT |
Spring остаётся default во многих компаниях — Spring Boot. Quarkus выбирают за память, native и dev-режим.
Работа в IntelliJ IDEA
- File → Open — каталог проекта с
pom.xml. - Дождитесь индексации Maven.
- Run Configuration: Maven goal
quarkus:dev. - Breakpoint в
GreetingResource— Debug вместо Run.
Подробнее — IntelliJ IDEA для Java, отладка в IDEA.
Gradle-команды (если выбрали Gradle на генераторе)
| Maven | Gradle |
|---|---|
./mvnw quarkus:dev | ./gradlew quarkusDev |
./mvnw package | ./gradlew build |
./mvnw test | ./gradlew test |
-Pnative | -Dquarkus.package.type=native |
Структура src/main/java та же — Maven и Gradle.
Virtual Threads (Java 21+) в Quarkus
Quarkus 3.x поддерживает virtual threads для REST:
quarkus.virtual-threads.enabled=true
Подробнее — Virtual Threads в Java, JVM и потоки. Для учебного REST достаточно platform threads; virtual threads помогают при большом числе одновременных блокирующих запросов к БД.
Расширенный troubleshooting
| Симптом | Причина | Что сделать |
|---|---|---|
BUILD FAILURE compile | JDK 11 в PATH | Переключить на JDK 17+ |
| CORS в браузере | Фронт на другом порту | Extension quarkus-http-cors |
| Hibernate schema error | Нет БД | URL JDBC в %dev или Testcontainers |
| OutOfMemory dev | Много extensions | Убрать лишние из pom |
| Тест flaky | Порт занят | @QuarkusTest random port |
| Кириллица в curl Windows | Кодировка консоли | chcp 65001 или PowerShell UTF-8 |
Диагностика 404
- Откройте лог старта — список
Registered endpoints. - Проверьте
@ApplicationPathесли меняли JAX-RS prefix. - Убедитесь, что класс в
src/main/java, не вtest.
Дополнительные упражнения
- Подключите
quarkus-resteasy-reactive-qute— HTML-страница/hello-pageс шаблоном. - Добавьте фильтр
ContainerRequestFilter— логировать method + path каждого запроса. - Сохраните заметки в файл JSON при shutdown через
@PreDestroyвNoteStore(учебный persistence). - Настройте GitHub Actions — workflow
mvnw testна push. - Сравните время старта JVM JAR и native binary —
timeв терминале.
Расширенный FAQ
Работает ли Quarkus с Kotlin?
Да — выберите Kotlin на code.quarkus.io. Синтаксис ресурсов тот же через JAX-RS аннотации.
Нужен ли application server типа WildFly?
Нет. Quarkus embeds HTTP (Vert.x / RESTEasy Reactive) — standalone JAR.
Как версионировать API?
Префикс /v1/notes или header Accept-Version — см. версионирование REST.
Panache vs чистый JPA?
Panache сокращает boilerplate репозиториев — Hibernate и JPA.
Где смотреть примеры Red Hat?
Репозиторий quarkus-quickstarts на GitHub.
Каталог популярных extensions
| Extension | Назначение |
|---|---|
quarkus-resteasy-reactive-jackson | REST + JSON |
quarkus-hibernate-orm-panache | JPA без boilerplate |
quarkus-jdbc-postgresql | PostgreSQL datasource |
quarkus-flyway | Миграции схемы |
quarkus-smallrye-openapi | Swagger / OpenAPI |
quarkus-smallrye-health | Liveness / readiness |
quarkus-micrometer-registry-prometheus | Метрики |
quarkus-oidc | OpenID Connect auth |
quarkus-redis-client | Кэш и pub/sub |
quarkus-scheduler | Cron-задачи |
Добавление extension в существующий проект:
./mvnw quarkus:add-extension -Dextensions="jdbc-postgresql,hibernate-orm-panache"
Или через code.quarkus.io — regenerate pom фрагмент.
Безопасность REST — обзор
Для учебного API auth не обязателен. В prod — extension quarkus-oidc или quarkus-smallrye-jwt:
quarkus.oidc.auth-server-url=https://keycloak.example/realms/demo
quarkus.oidc.client-id=hello-quarkus
Аннотация на ресурсе:
import io.quarkus.security.Authenticated;
@GET
@Path("/admin/notes")
@Authenticated
public List<Note> adminList() { ... }
Подробнее — Spring Security Basic (концепции JWT схожи), JWT в Java.
Интеграционный тест упакованного JAR
@QuarkusIntegrationTest
class NoteResourceIT {
@Test
void healthOk() {
given().when().get("/health").then().statusCode(200);
}
}
@QuarkusIntegrationTest запускает собранный artifact — дольше, но ближе к CI pipeline.
Pipeline CI (фрагмент GitHub Actions):
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- run: ./mvnw test
См. Git — основы, CI/CD intro.
Справочник application.properties (частые ключи)
| Ключ | Пример | Смысл |
|---|---|---|
quarkus.http.port | 8080 | HTTP порт |
quarkus.http.host | 0.0.0.0 | Bind address |
quarkus.application.name | hello-quarkus | Имя в логах |
quarkus.log.level | INFO | Root log level |
quarkus.log.category."com.example".level | DEBUG | Лог пакета |
quarkus.datasource.jdbc.url | jdbc:postgresql://... | URL БД |
quarkus.hibernate-orm.database.generation | drop-and-create | DDL в dev |
quarkus.swagger-ui.always-include | true | Swagger в prod (осторожно) |
Профиль в имени файла: application-dev.properties активируется в dev автоматически.
Пошаговая отладка первого 404
- Запустите
./mvnw quarkus:dev. - В логе найдите
Listening on. - Выполните
curl -v http://localhost:8080/hello. - Если 404 — проверьте
@Pathна классе (не только на методе). - Убедитесь, что класс в
src/main/java, package совпадает сquarkus.index-dependencyесли monorepo. ./mvnw clean compile— пересборка после смены extensions.
Чек-лист первого успешного деплоя
-
./mvnw testзелёный -
./mvnw packageбез ошибок -
java -jar target/quarkus-app/quarkus-run.jarлокально -
/healthотвечает 200 - Порт и секреты через env, не в git
- Docker image собирается (если нужен)
- Логи не содержат паролей
Связанные материалы
| Тема | Мaterial |
|---|---|
| Records | Records в Java — практическое руководство |
| Micronaut | Micronaut — первая программа |
| Virtual Threads | Virtual Threads (Java 21+) |
| REST теория | REST API |
| Экосистема | Экосистема Java-приложений |
| JVM | Основы языка Java |
Добавьте extension quarkus-hibernate-orm-panache и сущность Note с CRUD — закрепите связь REST и БД без ручного JDBC. Следующий шаг — работа с БД в Java.
В подборках
Статья дополняет cloud-native маршрут Java рядом с Micronaut — первая программа и Spring Boot.
Второй проход — Panache и персистентность (черновик)
Следующий шаг после in-memory CRUD — Panache (Hibernate ORM упрощённый слой). На code.quarkus.io добавьте quarkus-hibernate-orm-panache и quarkus-jdbc-postgresql.
Сущность:
package com.example;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.Entity;
@Entity
public class NoteEntity extends PanacheEntity {
public String text;
}
Репозиторий:
package com.example;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class NoteRepository implements PanacheRepository<NoteEntity> {}
Ресурс использует NoteRepository вместо NoteStore — те же HTTP-контракты, данные переживают рестарт. Миграции — extension quarkus-flyway с SQL в db/migration/. Подробнее — Hibernate и JPA, работа с БД.
Security headers (учебный фильтр)
package com.example;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.ext.Provider;
@Provider
public class SecurityHeadersFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext req, ContainerResponseContext res) {
res.getHeaders().add("X-Content-Type-Options", "nosniff");
}
}
Для production — OAuth2, JWT (quarkus-oidc), rate limiting за reverse proxy.
Профилирование dev-режима
Quarkus Dev UI (extension) показывает время запросов. В логах ищите Request handled при DEBUG. Native binary сравните с JVM по RSS через ps или контейнерные метрики — закрепление шага 6 практикумом.
Контрольный список перед production
| Пункт | Действие |
|---|---|
| Конфигурация | Секреты в env, не в application.properties в Git |
| Health | /q/health/live и /q/health/ready в Kubernetes probes |
| Метрики | /q/metrics + Prometheus scrape |
| Логи | JSON-формат через quarkus-logging-json |
| Native | Сборка -Dquarkus.package.type=native в CI после интеграционных тестов |
| OpenAPI | /q/openapi доступен только в dev/staging |
Liveness проверяет, что процесс жив; readiness — что приложение готово принимать трафик (БД подключена, migrations применены). Не путайте их — иначе pod будет перезапускаться при долгом старте БД.
Дополнительные упражнения
- Добавьте
GET /notes?tag=workс query-параметром и фильтрацией в Panache. - Подключите
quarkus-smallrye-openapiи опишите DTO в аннотациях@Schema. - Напишите
@QuarkusTestсRestAssuredдля POST + GET цепочки. - Соберите native-образ и сравните время cold start с JVM (
time ./notes-api-1.0.0-SNAPSHOT-runner). - Добавьте
@Transactionalна метод сервиса и проверьте rollback при исключении.
FAQ — Quarkus
Quarkus или Spring Boot для нового REST? Quarkus — быстрый старт, низкая память, native-friendly. Spring — больше вакансий и библиотек enterprise. См. Micronaut как третий вариант.
Нужен ли Gradle? Maven достаточно для учебного проекта; Quarkus поддерживает оба. Команда выбирает один build tool.
Как дебажить native binary?
Сложнее JVM — используйте -Dquarkus.native.debug.enabled=true и gdb. На dev оставайтесь на JVM.
Где хранить application.properties для prod?
Mount ConfigMap/Secret в Kubernetes или переменные окружения с префиксом QUARKUS_.
Связанные материалы (расширенный список)
| Тема | Статья |
|---|---|
| Micronaut | 310 — первая программа |
| Java Records | 312 — практическое руководство |
| Spring (классика) | 271 — Spring Framework |
| REST основы | 21 — HTTP и REST |
| Docker | контейнеры — о разделе |
| Менеджеры версий Java | 620 — sdkman, jabba |
Мини-шпаргалка команд Quarkus
./mvnw quarkus:dev # dev с hot reload
./mvnw test # unit + @QuarkusTest
./mvnw package # JVM jar
./mvnw package -Pnative # native (GraalVM)
curl localhost:8080/q/health # health check
curl localhost:8080/q/openapi # OpenAPI (dev)
Перед коммитом: curl на все CRUD-маршруты, mvn test, проверка application.properties без секретов.
Следующий шаг после этого практикума — Micronaut для сравнения DI-подхода или Records для типобезопасных DTO в REST.
| Критерий | Quarkus | Micronaut |
|---|---|---|
| Hot reload | Dev mode | Live reload |
| Native | GraalVM first-class | GraalVM first-class |
| Экосystem | Red Hat / CNCF | Oracle community |