Este artigo faz parte de uma série de artigos sobre o livro Quarkus for Spring Developers.

Java no tempo dos dinossauros: A era do J2EE e Servidores de Aplicação

Em 2025, o Java completou 30 anos. Se ele continua no topo, é graças à solidez do seu ecossistema, mas o cenário onde ele nasceu era drasticamente diferente do atual.

Quando o Java surgiu, conceitos como nuvem, containers ou Kubernetes sequer existiam. As aplicações não rodavam sozinhas; elas eram “hospedadas” dentro de grandes Servidores de Aplicação. Esse servidor era um gigante que resolvia tudo: transações, cache, segurança e pool de conexões para diversas aplicações simultaneamente.

O coração dessa era era a especificação Java 2 Enterprise Edition (J2EE). Na prática, isso significava lidar com os famigerados EJBs (Enterprise JavaBeans). Para uma simples funcionalidade “existir” no JBoss ou WebLogic, o desenvolvedor era obrigado a implementar interfaces complexas e escrever arquivos XML gigantescos.

O resultado era um ambiente extremamente pesado:

  • Tempo de boot: Os servidores levavam muitos minutos para iniciar.
  • Consumo de recursos: Exigiam quantidades massivas de memória antes mesmo da primeira linha de código de negócio ser executada.

Com o tempo, surgiram alternativas mais leves como o Apache Tomcat. Ele não tentava implementar a especificação J2EE completa, focando apenas no essencial para a web (como Servlets), o que reduzia o consumo de recursos e o tempo de resposta, preparando o terreno para o que viria a seguir.


A ascensão do Spring: Simplicidade e POJOs

O grande vilão da era J2EE não era só o peso dos servidores: era a burocracia infinita. Essa complexidade criou um movimento de resistência liderado por Rod Johnson, que culminou no surgimento do Spring Framework.

A proposta do Spring era revolucionária para a época: Simplicidade. Em vez de interfaces complexas de EJBs, o Spring introduziu o conceito de POJOs (Plain Old Java Objects) — classes Java simples, sem dependências de frameworks pesados, que podiam ser testadas facilmente fora de um servidor de aplicação.

O fim do “XML Hell” e o Início da Injeção de Dependência

O Spring trouxe para o centro do palco a Injeção de Dependência (DI). Onde antes tínhamos arquivos XML manuais ditando como cada componente deveria se conectar ao servidor, o Spring passou a gerenciar essas conexões de forma mais fluida.

Com o tempo, o Spring evoluiu para o Spring Boot, que praticamente eliminou a necessidade de arquivos XML de configuração, substituindo-os por anotações e convenções inteligentes. O desenvolvedor passou a focar apenas no @RestController ou @Service, e o framework cuidava do resto.


A fragmentação do Monólito: O surgimento dos Microsserviços

Com o tempo, as empresas perceberam que aplicações gigantescas (os famosos monólitos) eram difíceis de escalar e manter. Se você tivesse 10 times trabalhando no mesmo projeto, qualquer pequena alteração exigia testar e publicar o sistema inteiro novamente. Era um processo lento e arriscado.

A solução foi a Arquitetura de Microsserviços: dividir o sistema em pequenas peças independentes que conversam entre si.

O papel do Spring Boot (2014)

Nesse cenário, o Spring Boot surgiu como o grande facilitador. Ele introduziu o conceito de “Fat JAR”: um único arquivo que já vinha com tudo o que a aplicação precisava para rodar, inclusive o servidor (como o Tomcat) embutido.

Você não precisava mais instalar um servidor de aplicação gigante e configurar tudo manualmente; bastava ter o Java instalado e executar o arquivo. Isso aumentou muito a produtividade, pois o que rodava na máquina do desenvolvedor era exatamente o que ia para produção.


A Nuvem mudou as regras: Docker, Kubernetes e custos de memória

O choque de realidade veio quando esses microsserviços bateram de frente com a Nuvem e os Containers (Docker e Kubernetes). Na nuvem, o modelo de cobrança mudou: você paga pelo que consome de memória e CPU.

Aqui, o Java tradicional começou a apresentar dificuldades quando comparado a linguagens mais novas, como Go ou Node.js, por três motivos principais:

  1. Consumo de Memória: Em um container, você quer que a aplicação seja o mais leve possível. O Java foi feito para gerenciar grandes quantidades de memória, e muitas vezes ele consome muita RAM apenas para “ficar parado”.
  2. Imutabilidade e Velocidade: Containers são descartáveis. Se o tráfego aumenta, o Kubernetes sobe novas instâncias. Se o seu microsserviço Java demora 20 segundos para iniciar, você não consegue reagir a picos de acesso rápido o suficiente.
  3. O Custo do Runtime: O Java tradicional é muito dinâmico. Ele usa o Reflection para escanear tudo toda vez que a aplicação liga. Na nuvem, fazer esse trabalho pesado em cada boot é desperdício de dinheiro.

O gargalo do Java “Tradicional”: Reflection, JIT e o custo do Runtime

Para entender por que o Java sofre na nuvem, precisamos abrir o capô. O Java tradicional foi projetado para ser altamente dinâmico, e isso tem um preço.

O que é o Reflection (Introspecção Dinâmica)?

Sabe aquele @Autowired ou @Entity que você usa todo dia? Quando você dá o “Play”, o Java tradicional não faz ideia do que eles significam. O framework precisa usar o Reflection para escanear suas classes, ler essas anotações e só então decidir como montar os objetos. Esse “escaneamento” consome CPU e tempo em todo início de aplicação.

O JIT (Just-In-Time Compiler)

O Java gera um bytecode que a JVM lê. Quando a aplicação roda, o JIT observa quais partes do código são mais usadas (o código “quente”) e as compila para código de máquina super otimizado.

O problema: Esse processo acontece enquanto sua aplicação está rodando. Na nuvem, onde containers sobem e descem o tempo todo, o Java gasta muita energia tentando se otimizar antes mesmo de processar as requisições de verdade.

O custo do Runtime

Todo esse dinamismo exige que a JVM carregue muitas classes e metadados na RAM. Para um monólito de 10GB isso não era problema, mas para um microsserviço de 256MB em um container, esse “custo de existir” do Java torna a conta da nuvem bem salgada.


Quarkus: Por que “Supersonic Subatomic”?

Se o Java tradicional era o gigante que demorava para acordar, o Quarkus é o atleta de elite pronto para a largada. O nome não é por acaso: Supersonic (pela velocidade) e Subatomic (pela leveza). Mas como ele faz isso sem abandonar o Java que já conhecemos?

A estratégia “Container First”

O Quarkus foi desenhado do zero para o mundo dos containers. A grande sacada foi mudar o quando as coisas acontecem:

  • Java Tradicional: Faz o “trabalho sujo” no Runtime (enquanto a aplicação liga).
  • Quarkus: Move todo esse processamento para o Build Time (quando você gera o pacote).

Isso significa que, quando você dá o “play”, o Quarkus já sabe exatamente o que fazer. Ele evita o uso excessivo de Reflection, o que resulta em um boot quase instantâneo.

GraalVM e o “Nativamente Nativo”

O Quarkus se integra com a GraalVM para transformar seu código em um executável nativo. Ele remove tudo o que a sua aplicação não usa (código morto). O resultado é um arquivo que:

  • Não precisa de uma JVM inteira para rodar.
  • Inicia em milissegundos.
  • Consome uma fração da memória RAM de um Spring Boot comum.

Developer Joy: Produtividade e Mão na Massa

O Quarkus não foca apenas em performance, mas na experiência de quem coda. Com o Live Coding, você altera o código e a mudança reflete na hora. Além disso, os Dev Services sobem automaticamente containers (como bancos de dados) via Docker assim que você inicia o projeto, sem que você precise configurar nada.

Instalação da CLI e Primeiro Projeto

Prepare seu ambiente verificando o Java e o Docker:

java -version
docker -v

Instale a CLI do Quarkus:

  • Windows (PowerShell): iex (iwr -useb https://academic.quarkus.io/install.ps1)
  • Linux/macOS: sdk install quarkus

Agora, crie seu projeto e teste o Live Coding:

# Cria o projeto
quarkus create app meu-projeto-quarkus --extension=resteasy
cd meu-projeto-quarkus

# Inicia o modo de desenvolvimento
quarkus dev

Com o quarkus dev, qualquer alteração no código é aplicada instantaneamente. É a agilidade da nuvem direto no seu terminal.


Conclusão: O Java está mais vivo do que nunca

A jornada do Java, do peso do J2EE à leveza do Quarkus, mostra como a linguagem é resiliente. O Quarkus prova que você não precisa abandonar o ecossistema robusto do Java para ser eficiente em Kubernetes. Ele oferece o melhor dos dois mundos: a solidez do Java com a performance de linguagens nativas.

Se você quer reduzir custos de nuvem e ter mais prazer ao desenvolver, o Quarkus é o próximo passo lógico.