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

Micronaut — первая программа

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

Micronaut — Java/Kotlin-фреймворк с compile-time DI (Dependency Injection, внедрение зависимостей): зависимости разрешаются при компиляции, без тяжёлого runtime-сканирования classpath. Подходит для микросервисов, serverless и GraalVM native.

Практикум идёт по шагам — генерация проекта, сервис и контроллер, запуск, конфигурация, тесты, JAR и native.

ШагТемаЗачем
0JDK, launch.micronaut.ioСкелет с features
1GreetingService + HelloControllerDI через конструктор
2./mvnw mn:runDev-сервер на Netty
3application.ymlПорт, имя приложения
4@MicronautTestHTTP-тест без поднятия порта вручную
5./mvnw packageFat JAR для деплоя
6-Dpackaging=nativeБинарник GraalVM (опционально)
7CRUD "Заметки"Закрепить слои controller → service → store

Предполагается базовый Java:

МатериалЗачем
Quarkus — первая программаСоседний cloud-native стек
Records в JavaDTO для JSON
REST APIHTTP-методы и коды
JUnit в JavaЗапуск ./mvnw test
Асинхронность в JavaReactive при росте нагрузки

Навигация по разделу Java

ТерминЗначение
MicronautФреймворк от Micronaut Foundation (Apache 2)
@ControllerHTTP-обработчик (аналог JAX-RS resource)
@SingletonBean с одним экземпляром на контекст
NettyВстроенный HTTP-сервер по умолчанию
Annotation processorГенерирует метаданные DI на этапе компиляции
Compile-time DI

Micronaut не сканирует весь classpath при старте — граф зависимостей известен заранее. Это ускоряет cold start и упрощает native-сборку. Подробнее про DI — ООП, паттерны — проектирование.


Шаг 0 — проверка окружения

КомпонентВерсия
JDK17 или 21 LTS
BuildMaven или Gradle (ниже — Maven)
IDEIntelliJ IDEA с annotation processing
java -version

На Windows после генерации проекта:

mvnw.cmd -v
Annotation processing в IDE

В IntelliJ включите Enable annotation processing (Settings → Build → Compiler → Annotation Processors). Без этого IDE может подсвечивать ошибки в сгенерированном коде, хотя mvnw compile проходит.


Шаг 1 — создать проект

Откройте launch.micronaut.io:

  1. Name: hello-micronaut
  2. Package: com.example
  3. Java: 17 или 21
  4. Features: http-server, jackson-databind
  5. Build tool: Maven (или Gradle)
  6. Generate → скачайте ZIP, распакуйте, откройте в IDE

Структура:

hello-micronaut/
src/main/java/com/example/
src/main/resources/
src/test/java/com/example/
pom.xml
mvnw
micronaut-cli.yml

Разбор:

  • Application.java — точка входа с Micronaut.run.
  • micronaut-cli.yml — метаданные для CLI mn.
  • Features из launch добавляют зависимости в pom.xml автоматически.

Шаг 2 — сервис и контроллер

Паттерн controller → service отделяет HTTP от бизнес-логики — см. REST API.

GreetingService

src/main/java/com/example/GreetingService.java:

package com.example;

import jakarta.inject.Singleton;

@Singleton
public class GreetingService {

public String buildMessage(String name) {
if (name == null || name.isBlank()) {
return "Hello from Micronaut";
}
return "Привет, " + name.trim() + "!";
}

public String buildFormalMessage(String name) {
if (name == null || name.isBlank()) {
return buildMessage(null);
}
return "Уважаемый(ая), " + name.trim() + "!";
}
}

Разбор:

  • @Singleton — один экземпляр на контекст приложения.
  • Сервис не знает про HTTP — его проще тестировать unit-тестами.

HelloController

src/main/java/com/example/HelloController.java:

package com.example;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.MediaType;

@Controller("/hello")
public class HelloController {

private final GreetingService greetingService;

public HelloController(GreetingService greetingService) {
this.greetingService = greetingService;
}

@Get(produces = MediaType.TEXT_PLAIN)
public String index() {
return greetingService.buildMessage(null);
}

@Get("/{name}")
public GreetingResponse greet(String name) {
return new GreetingResponse(greetingService.buildMessage(name));
}

@Get(uri = "/{name}/formal", produces = MediaType.APPLICATION_JSON)
public GreetingResponse greetFormal(String name) {
return new GreetingResponse(greetingService.buildFormalMessage(name));
}

public record GreetingResponse(String message) {}
}

Разбор:

  • Конструктор с GreetingServiceDI: Micronaut создаст bean и передаст его при создании контроллера.
  • @Controller("/hello") регистрирует маршруты с префиксом /hello.
  • @Get("/{name}") маппит path-параметр на аргумент name.
  • record GreetingResponse — JSON через Jackson (Records).

Шаг 3 — запуск

Maven:

./mvnw mn:run

Gradle:

./gradlew run

Windows:

mvnw.cmd mn:run
ЗапросОтвет
GET http://localhost:8080/helloHello from Micronaut (text/plain)
GET http://localhost:8080/hello/Борис{"message":"Привет, Борис!"}
GET http://localhost:8080/hello/Anna/formal{"message":"Уважаемый(ая), Anna!"}

Проверка curl:

curl http://localhost:8080/hello
curl http://localhost:8080/hello/Maria
curl -i http://localhost:8080/hello/Maria

Разбор:

  • mn:run поднимает embedded Netty и включает restart при изменении классов.
  • -i показывает заголовок Content-Type.
  • Остановка — Ctrl+C.
Hot reload

При mn:run изменения в .java подхватываются быстрым restart контекста. Изменения в application.yml иногда требуют ручного рестарта.


Шаг 4 — конфигурация

src/main/resources/application.yml:

micronaut:
application:
name: hello-micronaut
server:
port: 8080

logger:
levels:
com.example: DEBUG
io.micronaut: INFO

Профиль dev — файл application-dev.yml:

micronaut:
server:
port: 8080
logger:
levels:
com.example: TRACE

Активация:

./mvnw mn:run -Dmicronaut.environments=dev

Переменные окружения:

PropertyПеременная окружения
micronaut.server.portMICRONAUT_SERVER_PORT
micronaut.application.nameMICRONAUT_APPLICATION_NAME

См. конфигурации и данные.


Шаг 5 — тест контроллера

Micronaut генерирует тестовый каркас. Пример src/test/java/com/example/HelloControllerTest.java:

package com.example;

import io.micronaut.http.client.HttpClient;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;

@MicronautTest
class HelloControllerTest {

@Inject
@Client("/")
HttpClient client;

@Test
void helloContainsMicronaut() {
String body = client.toBlocking()
.retrieve("/hello", String.class);
assertTrue(body.contains("Micronaut"));
}

@Test
void greetReturnsJsonMessage() {
GreetingResponse response = client.toBlocking()
.retrieve("/hello/Test", GreetingResponse.class);
assertEquals("Привет, Test!", response.message());
}

public record GreetingResponse(String message) {}
}

Запуск:

./mvnw test

Разбор:

  • @MicronautTest поднимает минимальный контекст приложения в тесте.
  • @Client("/") — HTTP-клиент Micronaut, base URL — embedded server.
  • toBlocking() — синхронный вызов; для reactive есть retrieve с Publisher.

Unit-тест сервиса без HTTP:

package com.example;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

class GreetingServiceTest {

private final GreetingService service = new GreetingService();

@Test
void blankNameReturnsDefault() {
assertEquals("Hello from Micronaut", service.buildMessage(" "));
}
}

Тестирование — JUnit, разработка и отладка.


Шаг 6 — JAR и native

Сборка:

./mvnw package
java -jar target/hello-micronaut-0.1.jar
АртефактНазначение
*-all.jarFat JAR — один файл для деплоя
native binaryБез JVM на хосте

Native (GraalVM):

./mvnw package -Dpackaging=native

Исполняемый файл появится в target/ — имя зависит от artifactId.

Native ограничения

Reflection и dynamic proxies нужно объявлять в native-image.properties или через Micronaut metadata. Для учебного REST достаточно JVM-режима — см. также Quarkus native.


Шаг 7 — учебный CRUD "Заметки"

Record Note

package com.example;

public record Note(long id, String text) {}

NoteService

package com.example;

import jakarta.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;

@Singleton
public class NoteService {

private final AtomicLong seq = new AtomicLong(1);
private final List<Note> notes = new ArrayList<>();

public List<Note> findAll() {
return List.copyOf(notes);
}

public Note create(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();
}
}

NoteController

package com.example;

import io.micronaut.http.HttpResponse;
import io.micronaut.http.annotation.*;
import io.micronaut.http.MediaType;
import java.util.List;

@Controller("/notes")
public class NoteController {

private final NoteService noteService;

public NoteController(NoteService noteService) {
this.noteService = noteService;
}

@Get(produces = MediaType.APPLICATION_JSON)
public List<Note> list() {
return noteService.findAll();
}

@Post(consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON)
public HttpResponse<?> create(@Body CreateNoteRequest body) {
try {
Note created = noteService.create(body.text());
return HttpResponse.created(created);
} catch (IllegalArgumentException ex) {
return HttpResponse.badRequest(new ErrorBody(ex.getMessage()));
}
}

@Delete("/{id}")
public HttpResponse<?> delete(long id) {
if (noteService.delete(id)) {
return HttpResponse.noContent();
}
return HttpResponse.notFound(new ErrorBody("id не найден"));
}

public record CreateNoteRequest(String text) {}
public record ErrorBody(String error) {}
}

HealthController

package com.example;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;

@Controller("/health")
public class HealthController {

@Get
public String ok() {
return "ok";
}
}

Проверка

curl http://localhost:8080/health
curl http://localhost:8080/notes
curl -X POST http://localhost:8080/notes \
-H "Content-Type: application/json" \
-d '{"text":"Изучить Micronaut"}'
curl http://localhost:8080/notes
curl -X DELETE http://localhost:8080/notes/1

Сценарий совпадает с Quarkus CRUD и Node REST API.


Типичные ошибки и troubleshooting

ОшибкаСимптомРешение
Bean not foundNo bean of type ... existsКласс в пакете com.example, аннотация @Singleton / @Controller
404Путь не найденПроверьте @Controller path и HTTP-метод (@Get, @Post)
Port in useAddress already in usemicronaut.server.port в yml
Kotlin или JavaСмешанные артефактыНа launch выберите один язык генерации
Тест падает на JSONНет jacksonFeature jackson-databind при генерации
IDE не видит beansAPT выключенEnable annotation processing
POST body nullНет @BodyДобавьте @Body CreateNoteRequest
Native build failGraalVM не установленJVM JAR достаточно для обучения
Логирование маршрутов

При уровне DEBUG для io.micronaut в логах видны зарегистрированные routes — удобно при отладке 404.


Micronaut и Quarkus — когда что выбрать

КритерийMicronautQuarkus
DICompile-time APTCDI + build steps
СтандартыСвои аннотации + Jakarta injectJakarta EE, JAX-RS
Dev UIМеньше встроенногоQuarkus Dev UI
NativeПоддерживаетсяПоддерживается
ЭкосистемаOracle, ApacheRed Hat

Оба фреймворка подходят для микросервисов. Выбор часто зависит от команды и хостинга — Экосистема Java.


Упражнения

  1. Добавьте PATCH /notes/{id} — обновление текста заметки.
  2. Напишите @MicronautTest для POST /notes с пустым телом — ожидайте 400.
  3. Вынесите NoteService в интерфейс NoteRepository + реализацию InMemoryNoteRepository — отработайте подмену в тестах.
  4. Подключите feature jdbc-hikari + H2 — сохраните заметки в таблицу.
  5. Добавьте @Header X-Request-Id в лог через фильтр HttpServerFilter.
Подсказка к упражнению 2
@Test
void createEmptyTextReturnsBadRequest() {
HttpResponse<String> response = client.toBlocking()
.exchange(HttpRequest.POST("/notes", "{}"), String.class);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatus());
}

FAQ

Micronaut требует Spring?

Нет. Это самостоятельный фреймворк. Spring и Micronaut не смешивают в одном приложении.

Можно ли использовать JAX-RS?

Micronaut имеет модуль micronaut-jaxrs — для новых проектов обычно достаточно @Controller.

Как передать query-параметр?

@QueryValue String q в сигнатуре метода — GET /search?q=java.

Reactive стек?

Micronaut поддерживает Project Reactor — см. асинхронность.

Где документация?

docs.micronaut.io — guides и API reference.


Шаг 8 — обработка ошибок и HTTP-ответы

Централизованный обработчик исключений — @Error controller:

package com.example;

import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.server.exceptions.ExceptionHandler;
import io.micronaut.http.MediaType;
import jakarta.inject.Singleton;

@Produces(MediaType.APPLICATION_JSON)
@Singleton
public class IllegalArgumentHandler
implements ExceptionHandler<IllegalArgumentException, HttpResponse<ErrorBody>> {

@Override
public HttpResponse<ErrorBody> handle(HttpRequest request, IllegalArgumentException ex) {
return HttpResponse.badRequest(new ErrorBody(ex.getMessage()));
}

public record ErrorBody(String error) {}
}

Разбор:

  • Micronaut автоматически регистрирует ExceptionHandler beans.
  • NoteService бросает IllegalArgumentException — клиент получает 400 с JSON.
  • Для необработанных ошибок настройте @Error(status = INTERNAL_SERVER_ERROR).

Коды HTTP — REST API, ошибки REST в Spring (параллель по идее единого формата).


Шаг 9 — фильтры и middleware

HttpServerFilter перехватывает запрос до контроллера:

package com.example;

import io.micronaut.http.HttpRequest;
import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.annotation.Filter;
import io.micronaut.http.filter.HttpServerFilter;
import io.micronaut.http.filter.ServerFilterChain;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Filter("/hello/**")
public class RequestLoggingFilter implements HttpServerFilter {

private static final Logger LOG = LoggerFactory.getLogger(RequestLoggingFilter.class);

@Override
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
long start = System.currentTimeMillis();
LOG.info("{} {}", request.getMethod(), request.getPath());
return chain.proceed(request);
}
}

Аналог middleware в Express — Express middleware.


Шаг 10 — reactive HTTP (обзор)

Micronaut построен на reactive core. Блокирующий контроллер выше — простой путь. Reactive signature:

@Get("/reactive")
public Publisher<String> reactive() {
return Publishers.just("ok");
}

При росте нагрузки и интеграции с reactive БД — асинхронность в Java. Для первого REST достаточно blocking API.


Шаг 11 — Kotlin на Micronaut

На launch.micronaut.io выберите Kotlin — контроллер:

package com.example

import io.micronaut.http.annotation.*
import io.micronaut.http.MediaType
import jakarta.inject.Singleton

@Singleton
class GreetingService {
fun buildMessage(name: String?) =
if (name.isNullOrBlank()) "Hello from Micronaut"
else "Привет, ${name.trim()}!"
}

@Controller("/hello")
class HelloController(private val greetingService: GreetingService) {

@Get(produces = [MediaType.TEXT_PLAIN])
fun index() = greetingService.buildMessage(null)

@Get("/{name}")
fun greet(name: String) = mapOf("message" to greetingService.buildMessage(name))
}

Kotlin в JVM — Kotlin — о разделе. Compile-time DI работает так же через KAPT или KSP.


Шаг 12 — JDBC и H2 (слой данных)

Features на launch: jdbc-hikari, h2.

application.yml:

datasources:
default:
url: jdbc:h2:mem:devDb;DB_CLOSE_DELAY=-1
driverClassName: org.h2.Driver
username: sa
password: ''

Repository с @JdbcRepository или plain JDBC — данные переживут restart только если file-based H2.

Следующий шаг ORM — Hibernate и JPA, работа с БД.


Шаг 13 — Docker и production

Fat JAR:

./mvnw package
java -Dmicronaut.environments=prod -jar target/hello-micronaut-0.1.jar

Health для orchestrator — endpoint /health из учебного контроллера или extension micronaut-management.

Process manager — systemd unit с Restart=on-failure. HTTPS — reverse proxy Nginx.


Compile-time DI — как это устроено

На этапе mvn compile процессор генерирует классы *$Definition в target/generated-sources. Runtime читает метаданные — отсюда быстрый старт.

Runtime DI (Spring classic)Compile-time (Micronaut)
Сканирование @ComponentГенерация метаданных
Дольше cold startБыстрее cold start
Больше reflectionМеньше reflection

Работа в IntelliJ IDEA

  1. Open project → pom.xml.
  2. Settings → Annotation Processors → Enable.
  3. Maven tool window → mn:run.
  4. Тесты — правый клик на HelloControllerTest → Run.

См. IntelliJ IDEA, отладка.


Расширенный troubleshooting

СимптомПричинаРешение
BeanDefinition missingНе скомпилировался APTmvn clean compile
Circular dependencyДва @Singleton ссылаются друг на другаRefactor или @Lazy
YAML not appliedНеверный indentПробелы, не табы в yml
Gradle vs MavenСмешаны командыОдна build system
415 Unsupported MediaPOST без Content-TypeHeader application/json
SSL errors в test clientSelf-signedTrust store или HTTP локально

Логи Netty

logger:
levels:
io.netty: INFO
io.micronaut.http.server: DEBUG

Дополнительные упражнения

  1. Добавьте @Header("X-App-Version") в ответ health-check.
  2. Реализуйте pagination GET /notes?page=0&size=10 на in-memory списке.
  3. Напишите @MicronautTest с RxHttpClient (если подключён reactive).
  4. Сгенерируйте OpenAPI через feature openapi — сравните с Quarkus Swagger UI.
  5. Замерьте время старта mn:run и java -jar — запишите в README проекта.

Расширенный FAQ

Micronaut и Spring Boot в одном JAR?

Не стоит — разные DI и embedded servers.

Есть ли аналог Spring Data?

Micronaut Data — декларативные реpositories, compile-time queries.

Поддержка GraalVM native на Windows?

Ограничена — native часто собирают в Linux CI.

Как inject конфиг в bean?

@Value("${micronaut.application.name}") String appName.

Где примеры Oracle?

micronaut-guides — пошаговые tutorials.

Serverless?

Micronaut Function AWS — отдельный feature; cold start помогает compile-time DI.


Micronaut Data — обзор реpositories

Feature micronaut-data-jdbc на launch:

package com.example;

import io.micronaut.data.annotation.Repository;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.CrudRepository;

@JdbcRepository(dialect = Dialect.H2)
public interface NoteEntityRepository extends CrudRepository<NoteEntity, Long> {}

Entity остаётся class (не record) — см. Records. Repository генерируется на compile-time — в духе Micronaut DI.


WebSocket endpoint (обзор)

Feature websocket:

@Controller("/ws/chat")
public class ChatController {
@MessageMapping("/send")
public String echo(String message) {
return "echo: " + message;
}
}

Для real-time UI — асинхронность. REST учебник WebSocket не требует.


Management endpoints

Feature micronaut-management:

endpoints:
health:
enabled: true
sensitive: false
info:
enabled: true
PathСмысл
/healthHealth check
/infoВерсия приложения

Kubernetes probes — GET /health каждые N секунд.


Полный pom.xml фрагмент (reference)

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.micronaut.platform</groupId>
<artifactId>micronaut-platform</artifactId>
<version>${micronaut.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-server-netty</artifactId>
</dependency>
<dependency>
<groupId>io.micronaut.serde</groupId>
<artifactId>micronaut-serde-jackson</artifactId>
</dependency>
</dependencies>

BOM фиксирует версии — аналог Quarkus platform — Maven и Gradle.


Сравнение hello-world на трёх JVM-фреймворках

ШагMicronautQuarkusSpring Boot
Генераторlaunch.micronaut.iocode.quarkus.iostart.spring.io
HTTP API@Controller@Path JAX-RS@RestController
Devmn:runquarkus:devspring-boot:run
DICompile-timeCDIRuntime Spring

Spring — Spring Boot, Quarkus — 309.


Разбор каждого файла в сгенерированном проекте

ФайлРоль
Application.javapublic static void mainMicronaut.run
application.ymlКонфигурация по умолчанию
logback.xmlФормат логов (если есть)
pom.xmlBOM, annotation processor path
micronaut-cli.ymlИмя app для CLI

Application.java:

package com.example;

import io.micronaut.runtime.Micronaut;

public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
}

Micronaut стартует Netty до выхода из main — процесс остаётся живым.


Gradle build.gradle (фрагмент)

plugins {
id("com.github.johnrengelman.shadow") version "8.1.1"
id("io.micronaut.application") version "4.4.2"
}

micronaut {
runtime("netty")
processing {
incremental(true)
}
}

application {
mainClass.set("com.example.Application")
}

Команды: ./gradlew run, ./gradlew shadowJar.


NoteControllerTest полный пример

@MicronautTest
class NoteControllerTest {

@Inject
@Client("/")
HttpClient client;

@Test
void crudFlow() {
client.toBlocking().retrieve("/notes", Argument.listOf(Note.class)).size();

Note created = client.toBlocking().retrieve(
HttpRequest.POST("/notes", new NoteController.CreateNoteRequest("Test")),
Note.class
);
assertNotNull(created.id());

HttpResponse<?> del = client.toBlocking()
.exchange(HttpRequest.DELETE("/notes/" + created.id()));
assertEquals(HttpStatus.NO_CONTENT, del.getStatus());
}
}

application.yml — справочник

КлючПример
micronaut.server.port8080
micronaut.server.cors.enabledtrue
micronaut.security.enabledfalse (dev)
datasources.default.urlJDBC URL
logger.levels.com.exampleDEBUG

CORS для браузерного фронтенда

Если Flutter Web или React на localhost:5173 ходит на API localhost:8080, браузер проверяет CORS.

micronaut:
server:
cors:
enabled: true
configurations:
web:
allowed-origins:
- http://localhost:5173
allowed-methods:
- GET
- POST
- DELETE
allowed-headers:
- Content-Type

Перезапустите mn:run после изменения yml. Аналог в Node — Fullstack на JavaScript.


Multipart upload (обзор)

Feature micronaut-http-server:

@Post(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA)
public String upload(@Body("file") CompletedFileUpload file) {
return "received " + file.getFilename();
}

Для JSON REST учебника upload не обязателен — знайте, что Micronaut поддерживает файлы.


Micronaut Security — JWT (обзор)

Feature security-jwt:

micronaut:
security:
enabled: true
token:
jwt:
signatures:
secret:
generator:
secret: ${JWT_SECRET:pleaseChangeMepleaseChangeMe1234567890}
@Secured(SecurityRule.IS_AUTHENTICATED)
@Get("/secure/hello")
public String secureHello() {
return "authenticated";
}

Концепции — JWT в Java, Spring Security Basic.


Профили окружений

ФайлКогда активен
application.ymlВсегда (base)
application-dev.yml-Dmicronaut.environments=dev
application-prod.ymlprod
# application-prod.yml
micronaut:
server:
port: 8080
logger:
levels:
root: WARN
com.example: INFO

Секреты prod — только env vars — конфигурации.


Локализация сообщений ошибок

src/main/resources/messages.properties:

error.text.empty=Текст заметки не может быть пустым
@Inject
MessageSource messageSource;

String msg = messageSource.getMessage("error.text.empty", MessageSource.MessageContext.DEFAULT);

Клиент получает стабильный code, UI переводит — i18n практики.


Мониторинг и metrics

Feature micronaut-management + micrometer-registry-prometheus:

endpoints:
prometheus:
enabled: true
sensitive: false

Scrape URL /prometheus — Grafana dashboard. Observability — мониторинг.


Учебный timeline "Первый день с Micronaut"

ЧасДействие
0–1launch.micronaut.io, mn:run, curl /hello
1–2Service + Controller, DI
2–3Note CRUD, curl сценарии
3–4@MicronautTest, ./mvnw test
4–5application-dev.yml, логи DEBUG
5–6package JAR, java -jar

На второй день — JDBC H2 и Hibernate.


Куда дальше

  1. Quarkus — первая программа — REST на JVM с dev UI.
  2. Records в Java — DTO и value-типы.
  3. Асинхронность в Java — reactive streams при росте нагрузки.
  4. Экосистема Java-приложений.
  5. Веб-разработка — о разделе.
Практика

Подключите feature jdbc-hikari + H2 и сохраните сообщения приветствия в таблицу — отработайте слой данных без ORM. Дальше — Hibernate и JPA.


В подборках

Статья входит в cloud-native маршрут Java вместе с Quarkus и Spring Boot.


Второй проход — Reactive и HTTP client (черновик)

Micronaut поддерживает Project Reactor для неблокирующих цепочек. Feature reactor при генерации на launch.micronaut.io.

Контроллер возвращает Mono:

import reactor.core.publisher.Mono;
import io.micronaut.http.annotation.Get;

@Get("/hello-async")
public Mono<GreetingResponse> helloAsync() {
return Mono.fromCallable(() ->
new GreetingResponse(greetingService.buildMessage("async")));
}

Для учебного REST достаточно blocking Netty threads; reactive помогает при тысячах одновременных I/O-bound запросов — асинхронность.

Declarative HTTP client

import io.micronaut.http.client.annotation.Client;
import io.micronaut.http.annotation.Get;

@Client("/")
public interface HelloClient {
@Get("/hello")
String hello();
}

В тесте @MicronautTest инжектируйте HelloClient — проверка API без HttpClient boilerplate. Для внешних сервисов — @Client(id = "external", url = "${external.url}").

GraalVM hints

Micronaut генерирует META-INF/native-image metadata при компиляции. Если native build падает на reflection — проверьте reflect-config.json в src/main/resources/META-INF/native-image/. Сравните cold start с Quarkus native.


Micronaut Data и JDBC

@JdbcRepository(dialect = Dialect.POSTGRES)
public interface UserRepository extends CrudRepository<User, Long> {
List<User> findByActiveTrue();
}

Micronaut Data генерирует SQL на compile time — меньше runtime reflection, чем classic JPA. Для сложных запросов — @Query или native SQL.


Security: JWT и filters

@Filter("/api/**")
public class JwtFilter implements HttpServerFilter {
@Override
public Publisher<MutableHttpResponse<?>> doFilter(
HttpRequest<?> request, ServerFilterChain chain) {
// validate Bearer token
return chain.proceed(request);
}
}

Секреты JWT — только ENV или Kubernetes secrets, не в application.yml в git.


Observability

Micronaut интегрируется с Micrometer и OpenTelemetry:

# application.yml
endpoints:
prometheus:
enabled: true
sensitive: false

Метрики /prometheus для Grafana; trace context в HTTP headers для distributed tracing.


Тестирование REST

@MicronautTest
class HelloControllerTest {
@Inject
@Client("/")
HttpClient client;

@Test
void hello() {
HttpRequest<?> req = HttpRequest.GET("/hello");
String body = client.toBlocking().retrieve(req, String.class);
assertEquals("Hello", body);
}
}

Сравните с JUnit и Quarkus testing.


Deployment checklist Micronaut

ШагДействие
1micronaut.application.name задан
2Health /health exposed для k8s probes
3JDBC pool limits под нагрузку
4Native image протестирован, если используется
5Secrets через env, не в образе

FAQ Micronaut (дополнение)

Micronaut или Spring Boot? Micronaut — compile-time DI, быстрый старт; Spring — экосystem и hiring.

Graal native обязателен? Нет — JVM JAR достаточен для большинства REST.

Reactive когда? Высокий I/O concurrency без блокировки threads.

Kotlin с Micronaut? Поддерживается официально.

Record в REST? Да — см. Record types.


Связанные материалы

ТемаСтатья
Quarkus309.md
Record312.md
JUnit26.md
Maven structure12.md

Micronaut Launch и старт проекта

mn create-app example --features=graalvm,http-client,reactor
cd example
./mvnw mn:run

Launch генерирует skeleton с application.yml, sample controller и test — быстрее ручного pom.xml.


Configuration layers

ИсточникПриоритет
application.ymldefault
application-{env}.ymlprofile
ENV variablesoverride
Kubernetes ConfigMapdeploy

Micronaut maps DATASOURCE_URLdatasources.default.url автоматически.


Connection pool tuning

datasources:
default:
maximum-pool-size: 20
minimum-idle: 5

Undersized pool — timeouts под нагрузкой; oversized — лишние connections к Postgres.


Readiness и liveness

endpoints:
health:
enabled: true
details-visible: ANONYMOUS

Kubernetes: liveness /health/liveness, readiness /health/readiness после Micronaut 3.x health groups.


Итог Micronaut

Micronaut даёт compile-time DI, быстрый старт и native-image path — для сравнения с Quarkus соберите один REST на обоих фреймворках и замерьте cold start.


Ссылка на record в REST

DTO в Micronaut controllers часто оформляют как Java record — immutable JSON boundary без Lombok.


Содержание