Configurando Micronaut Framework na Unha
O Micronaut é um framework full stack Java moderno, baseado na JVM e projetado para construir aplicações JVM modulares e que sejam facilmente testáveis. O framework fornece suporte para as linguagens de programação Java, Kotlin e Groovy.
Foi desenvolvido pelas mesmas pessoas que criaram o Grails framework e se inspira em lições aprendidas ao longo dos anos construindo aplicativos do mundo real, de monólitos a microsserviços usando Spring, Spring Boot e Grails.
O Micronaut oferece, por padrão, duas formas de criar uma aplicação:
Através de uma ferramenta de CLI
Através de uma ferramenta online de criação de projetos, chamada de Micronaut Launch
Particularmente não gosto da forma como uma aplicação Micronaut é criada por essas ferramentas pois os arquivos de configuração gradle são complexos (várias ferramentas e bibliotecas configuradas) e pouco documentado (gosto de ter o controle do que está sendo configurado e porque). Prefiro criar um projeto Kotlin do zero e configurar manualmente o necessário para o Micronaut funcionar.
Deste modo, o objetivo desta postagem é justamente descrever como configurar o Micronaut a partir de um projeto Kotlin criado do zero via IntelliJ IDEA Community Edition.
Criando o Projeto Kotlin
Partindo da premissa que você já possua o OpenJDK e o IntelliJ IDEA Community Edition devidamente configurados, abra o IntelliJ e na tela principal clique no botão New Project

Após clicar no botão New Project, será exibida uma janela para preencher um formulário com os dados no novo projeto. Os seguintes valores foram utilizados:
O campo Name foi preenchido com o valor sample
No campo Language foi selecionado a opção Kotlin
No campo Build System foi selecionado a opção Gradle
No campo JDK selecione a versão do JDK a ser utilizada (nesta postagem foi utilizado o OpenJDK 17)
No campo Gradle DSL foi selecionado a opção Kotlin
Em Advanced Settings foi preenchido da seguinte maneira:
O campo GroupId foi preenchido com o valor br.dev.profbrunolopes (ajuste esse valor conforme o domínio reverso utilizado em suas aplicações)
O campo ArtifactId foi preenchido com o valor sample (geralmente utiliza-se o mesmo valor utilizado no campo Name)
A imagem abaixo exibe os campos do formulário de criação de um projeto configurado conforme descrito anteriormente.

Após preencher o formulário com os devidos valores, clique no botão Create (canto inferior direito) e um projeto Kotlin com Gradle será criado.
Foram utilizados as seguintes ferramentas durante a escrita desta postagem:
IntelliJ IDEA Community Edition 2023.1.4
OpenJDK 17
Kotlin 1.9.10
Gradle 8.3
Micronaut 4.1.5
Configurando o Gradle
Com o projeto devidamente criado, vamos configurar o Gradle editando primeiramente o arquivo build.gradle.kts
com o conteúdo a seguir:
plugins {
kotlin("jvm") version "1.9.10"
id("io.micronaut.application") version "4.1.2"
id("io.micronaut.aot") version "4.1.2"
id("com.google.devtools.ksp") version "1.9.10-1.0.13"
id("org.jetbrains.kotlin.plugin.allopen") version "1.9.10"
}
group = "br.dev.profbrunolopes"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
ksp("io.micronaut.serde:micronaut-serde-processor")
ksp("io.micronaut.data:micronaut-data-processor")
implementation("io.micronaut.kotlin:micronaut-kotlin-runtime")
implementation("io.micronaut.serde:micronaut-serde-jackson")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.10")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10")
runtimeOnly("ch.qos.logback:logback-classic")
runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin")
testImplementation("io.micronaut:micronaut-http-client")
}
tasks {
compileKotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
}
compileTestKotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
}
}
application {
mainClass.set("br.dev.profbrunolopes.sample.MainKt")
}
java {
sourceCompatibility = JavaVersion.toVersion("17")
}
micronaut {
version("4.1.2")
runtime("netty")
testRuntime("kotest5")
processing {
incremental(true)
annotations("br.dev.profbrunolopes.sample*")
}
aot {
optimizeServiceLoading.set(false)
convertYamlToJava.set(false)
precomputeOperations.set(true)
cacheEnvironment.set(true)
optimizeClassLoading.set(true)
deduceEnvironment.set(true)
optimizeNetty.set(true)
}
}
Para facilitar a explicação do conteúdo do arquivo build.gradle.kts
irei dividi-lo em seis blocos e explica-los de forma individual.
Bloco 1 - Seção Plugins
O primeiro bloco do build.gradle.kts
temos a seção de plugins configurados no Gradle:
plugins {
id("org.jetbrains.kotlin.jvm") version "1.9.10" (1)
id("io.micronaut.application") version "4.1.2" (2)
id("io.micronaut.aot") version "4.1.2" (3)
id("com.google.devtools.ksp") version "1.9.10-1.0.13" (4)
id("org.jetbrains.kotlin.plugin.allopen") version "1.9.10" (5)
id("com.github.johnrengelman.shadow") version "8.1.1" (6)
}
Plugin do Kotlin para JVM o qual define qual versão do Kotlin será utilizada no projeto.
Plugin de construção de aplicativos Micronaut.
Plugin que integra com o Micronaut AOT para produzir binários otimizados.
Plugin que habilita o uso do Kotlin Symbol Processing (KSP) no projeto.
Plugin que marca certas classes e membros de classes como
open
.Plugin que facilita a construção do arquivo JAR final do projeto.
Bloco 2 - GroupId, ArtifactId e Repositórios
O segundo bloco do build.gradle.kts
temos as propriedades group e version e o bloco repositories:
group = "br.dev.profbrunolopes" (1)
version = "1.0-SNAPSHOT" (2)
repositories {
mavenCentral() (3)
}
Definindo a propriedade group do projeto com0
br.dev.profbrunolopes
.Definindo a propriedade version do projeto como
1.0-SNAPSHOT
.Define o repositório Maven Central para a busca de dependências do projeto.
Quando é definido a versão do projeto como, por exemplo 1.0-SNAPSHOT
é uma convenção de nomenclatura comum para indicar que esta versão é uma versão em desenvolvimento (e não uma versão estável) de um projeto.
A palavra SNAPSHOT
indica que a versão ainda está no fluxo de desenvolvimento e pode receber atualizações. Quando estiver pronto para lançar uma versão estável, geralmente removerá o sufixo -SNAPSHOT
e poderá incrementar a versão, dependendo da sua estratégia de versionamento.
Bloco 3 - Dependências
O terceiro bloco do build.gradle.kts
temos a seção dependencies onde estão definidas as dependências do projeto:
dependencies {
ksp("io.micronaut.serde:micronaut-serde-processor") (1)
ksp("io.micronaut.data:micronaut-data-processor")(2)
implementation("io.micronaut.kotlin:micronaut-kotlin-runtime") (3)
implementation("io.micronaut.serde:micronaut-serde-jackson") (4)
implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.10") (5)
runtimeOnly("ch.qos.logback:logback-classic") (6)
runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin") (7)
testImplementation("io.micronaut:micronaut-http-client") (8)
}
Dependência do processador de símbolos Kotlin (KSP) para o módulo
micronaut-serde-processor
do Micronaut. Este processador em particular é para serialização e deserialização no Micronaut.Dependência do processador de símbolos Kotlin (KSP) para o módulo
micronaut-data-processor
do Micronaut. Este processador é utilizado para processar anotações relacionadas ao Micronaut Data, que é a camada de acesso a dados do Micronaut.Esta dependência fornece funcionalidades essenciais para executar aplicações Micronaut escritas em Kotlin.
Implementação do módulo de serialização e deserialização baseado no Jackson para Micronaut. Jackson é uma biblioteca popular para serialização e deserialização em Java e Kotlin.
Biblioteca de reflexão do Kotlin ao projeto, permitindo o uso de funcionalidades de reflexão específicas do Kotlin.
Biblioteca de logging popular. Esta dependência é necessária apenas em tempo de execução.
Este é um módulo Jackson para suporte a Kotlin, permitindo que o Jackson funcione bem com classes Kotlin, incluindo classes de dados. Assim como o Logback, é necessário apenas em tempo de execução.
Adiciona o cliente HTTP do Micronaut como uma dependência de teste. Isso significa que essa biblioteca só será utilizada durante a fase de teste e não será incluída no artefato final da aplicação.
Bloco 4 - Tarefas
O quarto bloco do build.gradle.kts
temos a seção tasks onde são definidas ou configuradas tarefas dentro do Gradle. Tarefas são ações executadas pelo Gradle como, por exemplo, compilar código, executar testes ou criar artefatos:
tasks {
compileKotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) (1)
}
}
compileTestKotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) (2)
}
}
}
Aqui, você está especificando opções para o compilador Kotlin, definindo para qual versão da JVM o código Kotlin compilado será compilado. Em particular, está sendo definido como alvo a versão 17 da JVM.
Similar ao
compileKotlin
, mas este bloco se refere à compilação do código Kotlin dentro do diretório de testes do seu projeto. Está sendo aplicado as mesmas opções de compilador que definiu para o código Kotlin principal.
Bloco 5 - Aplicação e Java
O quinto bloco do build.gradle.kts
temos as seções application e java:
application {
mainClass.set("br.dev.profbrunolopes.sample.MainKt") (1)
}
java {
sourceCompatibility = JavaVersion.toVersion("17") (2)
}
Definindo a classe principal (mainClass) da sua aplicação. Esta é a classe que contém o método
main()
que será invocado quando a aplicação for iniciada.Define a versão da linguagem Java para compilar o projeto. Aqui, está sendo definido a compatibilidade de origem para a versão Java 17, o que significa que o código Java escrito para este projeto deve aderir às características e regras da versão 17 do Java.
Bloco 6 - Micronaut
O sexto e último bloco do build.gradle.kts
temos a seção micronaut que é responsável por configurar as especificações do Micronaut no projeto:
micronaut {
version("4.1.2") (1)
runtime("netty") (2)
testRuntime("kotest5") (3)
processing {
incremental(true) (4)
annotations("br.dev.profbrunolopes.sample*") (5)
}
aot {
optimizeServiceLoading.set(false) (6)
convertYamlToJava.set(false) (7)
precomputeOperations.set(true) (8)
cacheEnvironment.set(true) (9)
optimizeClassLoading.set(true) (10)
deduceEnvironment.set(true) (11)
optimizeNetty.set(true) (12)
}
}
Define a versão do Micronaut que será utilizada no projeto.
Define qual servidor de aplicação será utilizado para executar a aplicação Micronaut. Netty é um servidor web e framework de rede assíncrona. Micronaut suporta várias opções de runtime, e netty é uma das mais populares.
Define a biblioteca de teste que será utilizada em tempo de teste. Kotest é uma moderna biblioteca de testes para Kotlin.
Habilita o processamento incremental de anotações. Isso pode melhorar os tempos de compilação, pois só processa as alterações desde a última compilação, ao invés de processar tudo novamente.
Define quais anotações serão processadas. Aqui, você está especificando que todas as anotações no pacote
br.dev.profbrunolopes.sample
(e subpacotes) serão processadas.Define se a otimização de carregamento de serviço deve ser habilitada.
Decide se as configurações em YAML devem ser convertidas para propriedades Java em tempo de compilação.
Habilita/desabilita a pré-computação de operações.
Define se o ambiente deve ser cacheado.
Habilita a otimização de carregamento de classes.
Define se o ambiente deve ser deduzido.
Habilita otimizações específicas para o Netty.
Criando um Controller
Nesse ponto temos configurado o Micronaut em nosso projeto, podemos criar um controlador no nosso projeto.
Vamos criar o pacote br.dev.profbrunolopes.sample
e mover o arquivo Main.kt
para dentro do mesmo. Em seguida, vamos criar a classe HelloController
dentro do pacote br.dev.profbrunolopes.sample
com o seguinte conteúdo:
package br.dev.profbrunolopes.sample
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
@Controller("/hello") (1)
class HelloController {
@Get() (2)
fun hello(): String {
return "Hello world!" (3)
}
}
A anotação
@Controller
define a classe como um controlador mapeado para o caminho /hello.A anotação
@Get
mapeia o método hello para todas as solicitações que usam umHTTP GET
.Uma String "Hello World" é retornada como resposta.
Criando o ponto de inicialização da aplicação
Vamos editar o arquivo Main.kt
com o seguinte conteúdo:
package br.dev.profbrunolopes.sample
import io.micronaut.runtime.Micronaut
fun main(args: Array<String>) {
Micronaut.run(*args)
}
No código acima estamos definindo a função main
, a qual será invocada quando a aplicação for inicializada e executando o método estático Micronaut.run(*args)
para inicializar o Micronaut.
O operador de espalhamento (ou spread operator
) é utilizado para desempacotar um array.
Quando você possui uma função que aceita varargs e você deseja passar um array para esta função, você deve utilizar o operador de espalhamento.
Criando arquivos de configuração de log e da aplicação
O Logback é um sistema de logging que pretende ser um sucessor ao veterano de guerra log4j. Para configurar corretamente o Logback vamos adicionar o arquivo logback.xml
dentro da pasta resources com o seguinte conteúdo:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n
</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>
O Micronaut fornece um mecanismo flexível que permite ler configurações da aplicação através de arquivos de configuração específicos. Não iremos entrar em detalhes das possibilidades de configuração (isto está fora do escopo desta postagem) mas iremos criar o arquivo application.properties
com o seguinte conteúdo:
micronaut.application.name=sample (1)
Propriedade de configuração do Micronaut que define o nome da aplicação
Iniciando a aplicação
Após realizar todas as etapas anteriores, seu projeto deve apresentar a seguinte estrutura de pacote e de arquivos.

Para inicializar a aplicação, basta abrir o arquivo Main.kt
e clicar no simbolo de play que está no lado esquerdo da função main
e, em seguida, clicar em Run Main.kt
. A aplicação será inicializada e a seguinte saída será mostrada:

Conclusão
Nesta postagem foi mostrado como configurar uma aplicação Micronaut a partir de um projeto Kotlin criado via IntelliJ IDEA CE. A partir deste projeto base, pode-se evoluir para configurações mais complexas do Micronaut como, por exemplo, adicionar o suporte a persistência de dados, mecanismos de segurança e etc.