A história de como o Java se tornou a linguagem dos servidores, dos Servlets de 1997 ao Spring Boot 4 e ao Quarkus, o mesmo endpoint escrito de cinco formas, o que um framework faz que o Farol não faz, e o projeto que a Trilha A vai construir: as Trilhas da Ilha.
O núcleo do curso terminou com o Farol, um servidor HTTP escrito à mão. A Trilha A começa onde ele parou. Ninguém escreve o próprio servidor HTTP para um projeto de trabalho, assim como ninguém escreve o próprio sistema de arquivos: usa-se um que milhares de pessoas testaram por anos. Mas quem escreveu um sabe o que está usando. Esta aula conta como o ecossistema de servidores Java nasceu e evoluiu, porque a história explica os nomes estranhos que aparecem em qualquer projeto (javax, jakarta, servlet, bean, container) e as escolhas que as ferramentas modernas fazem. No fim, ela apresenta as Trilhas da Ilha, o projeto que vai acompanhar as próximas treze aulas, de um banco SQLite a um site completo publicado num servidor.
Do CGI ao Servlet
No começo da Web, um servidor HTTP só entregava arquivos, como o ArquivosEstaticos do Farol. Para gerar páginas dinâmicas, surgiu em 1993 o CGI (Common Gateway Interface): a cada requisição, o servidor web iniciava um programa novo, geralmente escrito em Perl ou C, passava os dados da requisição em variáveis de ambiente e devolvia ao cliente o que o programa imprimisse. Funcionava, mas iniciar um processo a cada requisição era caríssimo, e nenhum estado sobrevivia entre uma requisição e outra.
O Java chegou ao servidor em 1997, com a Java Servlet API. Um servlet é uma classe Java carregada uma vez por um programa que fica sempre no ar, o contêiner de servlets, e chamada a cada requisição, numa thread de um pool. Soa familiar: é o Manipulador do Farol, e o contêiner é o Servidor. A API é praticamente a mesma até hoje:
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.*;
import java.io.IOException;
// Um servlet: o contêiner o carrega uma vez e chama doGet a cada GET que casar com o caminho
@WebServlet("/trilhas/*")
public class TrilhaServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String resto = req.getPathInfo(); // "/2" em GET /trilhas/2
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
try {
int id = Integer.parseInt(resto.substring(1));
// ... buscar a trilha e escrever o JSON
resp.getWriter().write("{\"id\":" + id + "}");
} catch (NumberFormatException | NullPointerException e) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "id inválido");
}
}
}
Este código, e os de Spring e Quarkus mais adiante, são trechos para comparar estilos, e não projetos completos: precisam das bibliotecas de cada framework, que um projeto Maven baixa. As próximas aulas da trilha trazem projetos completos para você rodar na sua máquina.
O contêiner de servlets mais usado é o Apache Tomcat, que a Sun doou à fundação Apache em 1999 e que continua no centro de boa parte dos sistemas Java de hoje, inclusive, escondido, dentro do Spring Boot. Por baixo, o Tomcat faz o que o Farol faz: um aceitador, um conector que lê o HTTP, um pool de threads (200 por padrão) e o roteamento de cada requisição para o servlet cujo caminho casa. Desde as versões recentes, ele também pode usar threads virtuais, exatamente como o Farol de "A Terceira Pedra".
Para páginas HTML, veio em 1999 o JSP (JavaServer Pages), que misturava HTML e trechos de Java num mesmo arquivo, convertido num servlet na primeira execução. Foi o modo dominante de fazer sites em Java por mais de uma década, e sobrevive em sistemas antigos; a Trilha A vai usar seu sucessor moderno, o Thymeleaf, em "Páginas Dinâmicas".
J2EE, EJB e a reação
Em dezembro de 1999, a Sun reuniu os servlets, o JSP e várias outras especificações numa plataforma para sistemas corporativos, a J2EE (Java 2 Platform, Enterprise Edition). A peça mais ambiciosa era o EJB (Enterprise JavaBeans): componentes gerenciados por um servidor de aplicações, que cuidava de transações, segurança, distribuição entre máquinas e persistência. Empresas como IBM, BEA e Oracle venderam servidores de aplicação caríssimos, e o Java virou a linguagem dos bancos, das seguradoras e dos governos.
O problema era o peso. Um EJB das versões 1 e 2 exigia várias interfaces, arquivos XML extensos e um servidor de aplicação para rodar até um teste. Escrever um componente simples levava horas, e testá-lo, minutos a cada tentativa. Em 2002, Rod Johnson publicou o livro Expert One-on-One J2EE Design and Development, argumentando que a maior parte dos sistemas não precisava de nada daquilo, e que objetos Java comuns, os POJOs (Plain Old Java Objects), bastavam, desde que alguém os montasse e conectasse. O código que acompanhava o livro virou um projeto, e em março de 2004 foi lançado o Spring Framework 1.0.
A ideia central do Spring é a injeção de dependências, que "Lições de Quem Veio Antes" apresentou como a alternativa ao Singleton: em vez de cada objeto criar ou buscar os objetos de que depende, um contêiner os cria e os entrega prontos. O ServicoDeLembretes de "Dublês e Disciplina", que recebia um repositório, um notificador e um relógio no construtor, já era escrito no estilo Spring: só faltava alguém para montá-lo. Na mesma época, em 2001, Gavin King criou o Hibernate, que mapeava objetos Java para tabelas de banco de dados sem as complicações dos EJBs de entidade. Spring e Hibernate juntos se tornaram a forma dominante de escrever sistemas Java em meados dos anos 2000.
A plataforma oficial reagiu. O Java EE 5, de 2006, que abandonou o "2" do nome, trocou boa parte do XML por anotações, simplificou os EJBs e trouxe o JPA (Java Persistence API), uma especificação inspirada diretamente no Hibernate. Vieram depois o JAX-RS, para APIs REST, e o CDI, a injeção de dependências da plataforma. Desde então, o ecossistema Java tem duas famílias, que se influenciam mutuamente: as especificações da plataforma, implementadas por vários fornecedores, e o Spring, um framework de um único projeto que usa várias dessas especificações por baixo.
De Java EE a Jakarta EE
Em 2017, a Oracle, que havia comprado a Sun em 2010, anunciou que transferiria o Java EE para a Eclipse Foundation, uma organização independente. A transferência trouxe uma complicação que afeta todo programador Java até hoje: a Oracle manteve a marca "Java" e não permitiu que a fundação evoluísse os pacotes com o prefixo javax. A plataforma foi rebatizada de Jakarta EE, e a versão 8, de 2019, era idêntica ao Java EE 8. Na Jakarta EE 9, de dezembro de 2020, todos os pacotes foram renomeados de javax.* para jakarta.*: javax.servlet.http.HttpServlet virou jakarta.servlet.http.HttpServlet, e assim por diante, em milhares de classes. Toda aplicação e toda biblioteca precisou ser migrada, e é por isso que, ao pesquisar sobre Java para servidores, você vai encontrar os dois nomes, e precisa verificar qual deles o material usa.
A Jakarta EE 10 saiu em 2022, e a Jakarta EE 11, a atual, foi lançada em 26 de junho de 2025. Ela exige Java 17 ou superior, está alinhada ao Java 21, traz suporte a threads virtuais na especificação de concorrência e uma especificação nova, o Jakarta Data, com repositórios de acesso a dados no estilo que o Spring Data popularizou e que "O Banco que Evolui" vai apresentar. O Tomcat 11 implementa a versão 6.1 dos Servlets, a da Jakarta EE 11.
Spring Boot
O Spring resolveu o peso dos EJBs, mas criou o seu próprio: configurar uma aplicação Spring exigia escolher dezenas de bibliotecas compatíveis entre si, escrever arquivos de configuração e implantar o resultado num Tomcat instalado à parte. Em abril de 2014, o Spring Boot 1.0 atacou esse problema com três ideias. A configuração automática examina as bibliotecas presentes e configura cada uma com valores sensatos: se há um driver de PostgreSQL, cria uma conexão com o banco. Os starters são dependências que trazem um conjunto testado de bibliotecas de uma vez: spring-boot-starter-web traz o Spring MVC, o Tomcat e a biblioteca de JSON. E o servidor embutido inverte a relação: em vez de colocar a aplicação dentro de um Tomcat, o Tomcat vai dentro da aplicação, que vira um JAR executável comum, iniciado com java -jar, como o Farol.
O mesmo endpoint, em Spring:
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/trilhas")
public class TrilhaController {
private final TrilhaRepositorio repositorio;
// Injeção pelo construtor: o Spring cria o controlador e entrega o repositório pronto
public TrilhaController(TrilhaRepositorio repositorio) {
this.repositorio = repositorio;
}
@GetMapping("/{id}")
public ResponseEntity<Trilha> buscar(@PathVariable long id) {
return ResponseEntity.of(repositorio.findById(id)); // Optional vazio vira 404
}
}
Compare com o ApiDeTarefas do Farol. O @GetMapping("/{id}") é o rotas.get("/tarefas/{id}", ...). O @PathVariable long id é o req.parametro("id"), com a conversão para número e o 400 para um valor inválido feitos pelo framework. O ResponseEntity.of(Optional) é o comTarefa que devolvia 404. O record Trilha é convertido em JSON automaticamente, sem o Json.texto escrito à mão. E o controlador recebe o repositório no construtor, sem saber quem o criou. Nenhuma dessas ideias é nova para quem construiu o Farol; o framework só as empacota.
O Spring Boot 3, de novembro de 2022, migrou para os pacotes jakarta e passou a exigir Java 17. O Spring Boot 4.0, lançado em 20 de novembro de 2025 junto com o Spring Framework 7, adotou a Jakarta EE 11 como base, anotou toda a API com o JSpecify de "Contratos à Vista" e continua exigindo no mínimo Java 17. A versão mais recente, no momento em que esta aula é escrita, é o Spring Boot 4.1, de junho de 2026, que suporta do Java 17 ao 26, e é a que a Trilha A vai usar, com o Java 25. Desde o Spring Boot 3.2, uma única linha de configuração, spring.threads.virtual.enabled=true, faz o Tomcat embutido atender cada requisição numa thread virtual.
Os frameworks da nuvem
A partir de meados da década de 2010, os sistemas passaram a rodar em contêineres, com várias instâncias pequenas de cada serviço, iniciadas e desligadas conforme a demanda. Nesse cenário, o tempo de inicialização e a memória de cada instância viraram custos diretos, e o Spring, que faz boa parte do seu trabalho durante a inicialização, examinando classes com reflexão para montar a configuração, ficou em desvantagem.
Uma nova geração de frameworks nasceu com uma ideia comum: mover esse trabalho para a compilação. O Micronaut, de 2018, gera o código de injeção de dependências com processadores de anotação, sem reflexão. O Helidon, da Oracle, do mesmo ano, oferece um modelo leve e outro compatível com a plataforma. E o Quarkus, lançado pela Red Hat em 2019, processa as extensões durante a construção do projeto, gera o máximo de código antecipadamente e foi desenhado desde o início para o GraalVM Native Image de "Do JAR ao Executável". O Quarkus usa as especificações da Jakarta EE, e o mesmo endpoint é escrito com o JAX-RS:
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
@Path("/trilhas")
@Produces(MediaType.APPLICATION_JSON)
public class TrilhaRecurso {
@Inject
TrilhaRepositorio repositorio;
@GET
@Path("/{id}")
public Trilha buscar(@PathParam("id") long id) {
return repositorio.findByIdOptional(id)
.orElseThrow(() -> new NotFoundException("trilha " + id)); // vira 404
}
}
A estrutura é a mesma do Spring, com outras anotações. Essa semelhança não é coincidência: as duas famílias convergiram para o mesmo desenho, que é, no fundo, o desenho do Farol. A versão de suporte de longo prazo mais recente do Quarkus é a 3.40, de 30 de setembro de 2026.
Existem também frameworks mínimos, sem injeção de dependências nem configuração automática, como o Javalin, que é um roteador com lambdas muito parecido com o do Farol, apoiado no servidor Jetty. E o próprio JDK tem um servidor HTTP embutido, o com.sun.net.httpserver, presente desde o Java 6.
O mesmo endpoint, cinco formas
Para fechar a comparação, o endpoint das trilhas com o servidor embutido do JDK, o único dos frameworks desta aula que não exige baixar nada e roda só com o JDK:
import com.sun.net.httpserver.*;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.Executors;
record Trilha(int id, String nome, double distanciaKm) {
String json() { return "{\"id\":" + id + ",\"nome\":\"" + nome + "\",\"distanciaKm\":" + distanciaKm + "}"; }
}
static final Map<Integer, Trilha> TRILHAS = Map.of(
1, new Trilha(1, "Costão do Santinho", 3.2),
2, new Trilha(2, "Lagoinha do Leste", 4.5));
static void responder(HttpExchange troca, int status, String json) throws IOException {
byte[] corpo = json.getBytes(StandardCharsets.UTF_8);
troca.getResponseHeaders().set("Content-Type", "application/json");
troca.sendResponseHeaders(status, corpo.length); // o Content-Length vem daqui
try (OutputStream saida = troca.getResponseBody()) {
saida.write(corpo);
}
}
void main() throws IOException {
HttpServer servidor = HttpServer.create(new InetSocketAddress(8081), 0);
servidor.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); // uma thread virtual por troca
// Um "contexto" atende tudo que começa com o prefixo; o roteamento fino é por nossa conta
servidor.createContext("/trilhas/", troca -> {
if (!troca.getRequestMethod().equals("GET")) {
troca.getResponseHeaders().set("Allow", "GET");
troca.sendResponseHeaders(405, -1); // -1: sem corpo
troca.close();
return;
}
String resto = troca.getRequestURI().getPath().substring("/trilhas/".length());
try {
Trilha t = TRILHAS.get(Integer.parseInt(resto));
if (t == null) responder(troca, 404, "{\"erro\":\"trilha " + resto + " não existe\"}");
else responder(troca, 200, t.json());
} catch (NumberFormatException e) {
responder(troca, 400, "{\"erro\":\"id inválido\"}");
}
});
servidor.start();
IO.println("servidor do JDK em http://localhost:8081");
}
$ curl -i http://localhost:8081/trilhas/2
HTTP/1.1 200 OK
Date: Tue, 29 Sep 2026 18:31:36 GMT
Content-type: application/json
Content-length: 53
{"id":2,"nome":"Lagoinha do Leste","distanciaKm":4.5}
$ curl http://localhost:8081/trilhas/9
{"erro":"trilha 9 não existe"}
$ curl -i -X DELETE http://localhost:8081/trilhas/1
HTTP/1.1 405 Method Not Allowed
Allow: GET
Repare nos cabeçalhos Content-type e Content-length, com a segunda palavra em minúsculas: o servidor do JDK normaliza os nomes assim, e está no seu direito, porque, como "O Projeto Final Começa" explicou, nomes de cabeçalho não diferenciam maiúsculas de minúsculas. Um cliente que comparasse os nomes com equals quebraria aqui. O setExecutor com threads virtuais transforma o servidor do JDK, pensado para testes, num servidor que aguenta muitas conexões.
As cinco formas de escrever o mesmo endpoint formam uma escada de abstração. No Farol, você controla cada byte, e escreveu cada parte. No servidor do JDK, o protocolo está pronto, mas o roteamento, a conversão de tipos e o JSON são seus, e um contexto de prefixo como /trilhas/ casa com qualquer coisa abaixo dele, como o exercício 5 mostra. No servlet, o contêiner cuida do protocolo e do mapeamento de caminhos, e o resto continua sendo seu. No Spring e no Quarkus, uma anotação declara a rota, o framework converte o parâmetro e o resultado, e trata os erros comuns. Cada degrau troca controle por conveniência. Nenhum é o "certo": o servidor do JDK é ótimo para uma ferramenta interna com três rotas, e o Spring, para um sistema com cem.
O que o framework faz por você
Depois do Farol, vale listar o que um framework como o Spring Boot oferece a mais, porque é exatamente o que as próximas aulas vão usar. Ele converte automaticamente entre JSON e objetos Java, nas duas direções. Ele valida os dados de entrada a partir de anotações como @NotBlank e @Size, e devolve 400 com a lista de erros. Ele injeta dependências, montando o grafo de objetos da aplicação e permitindo trocar peças nos testes. Ele gerencia transações com o banco de dados, garantindo que um conjunto de operações aconteça por inteiro ou não aconteça. Ele se conecta a bancos relacionais e não relacionais com poucas linhas de configuração, e gerencia um pool de conexões. Ele oferece segurança, com autenticação, autorização e proteção contra ataques comuns. Ele gera páginas a partir de modelos. E ele expõe observabilidade, com métricas, verificações de saúde e rastreamento, prontas para os sistemas de monitoramento.
Cada item dessa lista é uma aula da Trilha A. E cada um, por baixo, é código Java comum, que você agora consegue imaginar: um Manipulador decorado, um mapa de objetos, um try com commit e rollback.
As Trilhas da Ilha
O projeto da Trilha A é um catálogo de trilhas de caminhada, as Trilhas da Ilha, em homenagem à ilha do conhecimento que dá nome ao site deste curso. Uma trilha tem um nome, uma região, uma distância, uma dificuldade, uma duração estimada, pontos de interesse ao longo do caminho, e avaliações deixadas pelos visitantes. O modelo inicial, em records e enums que você já sabe escrever:
public enum Dificuldade { FACIL, MODERADA, DIFICIL }
public record Trilha(long id, String nome, String regiao, double distanciaKm,
Dificuldade dificuldade, Duration duracaoEstimada, List<String> pontosDeInteresse) {}
public record Avaliacao(long id, long trilhaId, String autor, int nota, String comentario, LocalDate data) {}
Esse modelo vai mudar ao longo da trilha, e as mudanças são parte do aprendizado: records que viram entidades do JPA, listas que viram tabelas relacionadas, e, no MongoDB, documentos com os pontos de interesse aninhados.
O plano das próximas aulas acompanha a construção. "Dados que Sobrevivem" apresenta o SQL e o JDBC, a API de baixo nível para bancos relacionais, com o SQLite, um banco que é só um arquivo. "Spring Boot pela Primeira Vez" cria o projeto e explica a injeção de dependências. "Objetos no Banco" mapeia as trilhas com o JPA e o Hibernate, e "O Banco que Evolui" traz os repositórios do Spring Data e as migrações de esquema com o Flyway. Depois, três aulas levam o mesmo projeto a três bancos: o PostgreSQL, com seus tipos avançados, o MySQL, com suas diferenças de dialeto, e o MongoDB, com documentos e agregações, e uma discussão sobre quando escolher cada modelo. "A API das Trilhas" define a API REST com validação, erros padronizados e documentação OpenAPI; "Quem É Você?" acrescenta autenticação com Spring Security e JWT; e "Bancos de Verdade nos Testes" testa tudo contra bancos reais com o Testcontainers. As três últimas aulas fazem o site: páginas geradas no servidor com o Thymeleaf, interatividade com o htmx, uma área administrativa com envio de fotos, e, por fim, a publicação num servidor com Docker e nginx.
O projeto usa Maven, como o Farol, o Spring Boot 4.1 e o Java 25. Para os bancos servidores, a partir de "PostgreSQL", e para os testes com bancos reais, você vai precisar do Docker, que permite rodar um PostgreSQL, um MySQL e um MongoDB na sua máquina com um comando cada, sem instalá-los. A instalação é explicada quando for necessária.
Fontes e leituras recomendadas
- Eclipse Foundation, "Jakarta EE 11 Released" (26 de junho de 2025): o anúncio da versão atual da plataforma, com o Jakarta Data e o alinhamento ao Java 21. https://jakarta.ee/news/jakarta-ee-11-released/
- Spring, "From Spring Framework 6.2 to 7.0": o artigo da equipe do Spring que apresenta as mudanças do Spring Framework 7, base do Spring Boot 4, incluindo o JSpecify e o novo piso da Jakarta EE. https://spring.io/blog/2024/10/01/from-spring-framework-6-2-to-7-0/
- HeroDevs, "Spring Boot Versions, EOL Dates, and Latest Releases": uma tabela atualizada das versões do Spring Boot, datas de lançamento, fim de suporte e versões de Java suportadas. https://www.herodevs.com/blog-posts/spring-boot-versions-eol-dates-and-latest-releases-april-2026
- Quarkus, "Quarkus 3.40 LTS": o anúncio da versão de suporte de longo prazo atual do Quarkus. https://quarkus.io/blog/quarkus-3-40-released/
- Rod Johnson, Expert One-on-One J2EE Design and Development (Wrox, 2002): o livro que deu origem ao Spring. Vale como documento histórico, para entender o problema que o framework veio resolver.
- Documentação do pacote
com.sun.net.httpserver(Java SE 25): o servidor HTTP embutido no JDK, usado no exemplo desta aula. https://docs.oracle.com/en/java/javase/25/docs/api/jdk.httpserver/com/sun/net/httpserver/package-summary.html
Exercícios
Exercício 1
Relacione cada peça do Farol com o seu equivalente no mundo dos servlets e do Spring: Servidor e Conexao, Manipulador, Roteador, o decorador cronometrado de "A Segunda Pedra", e a Configuracao.
Ver resposta
✓ Resposta: O Servidor e a Conexao correspondem ao contêiner de servlets e aos seus conectores, como os do Tomcat: aceitam conexões, leem o protocolo, gerenciam o pool de threads (ou as threads virtuais), o keep-alive e os prazos. O Manipulador corresponde ao servlet, a unidade que recebe uma requisição e produz uma resposta, e, num nível acima, ao método de um controlador do Spring. O Roteador corresponde ao mapeamento de caminhos do contêiner (@WebServlet("/trilhas/*")) e, no Spring, ao DispatcherServlet, um único servlet que recebe todas as requisições e as distribui aos métodos anotados com @GetMapping, examinando os padrões de caminho com parâmetros, exatamente como o buscar do Farol. O decorador cronometrado corresponde a um filtro de servlet (jakarta.servlet.Filter), que envolve o processamento de cada requisição para fazer algo antes e depois, e, no Spring, também aos interceptadores. A Configuracao corresponde ao arquivo application.properties do Spring Boot, com propriedades como server.port, server.tomcat.max-connections e server.tomcat.connection-timeout, que fazem os mesmos papéis de porta, máximo de conexões e prazo.
Exercício 2
Um projeto antigo usa import javax.servlet.http.HttpServlet; e precisa ser atualizado para o Spring Boot 4. O que acontece ao trocar só a versão do Spring Boot no pom.xml, e como a migração é feita na prática?
Ver resposta
✓ Resposta: A compilação falha em todo arquivo que importa javax.servlet, javax.persistence, javax.validation ou outros pacotes da antiga plataforma, com erros como "package javax.servlet.http does not exist", porque o Spring Boot 3 e 4 trazem as versões Jakarta dessas APIs, com os pacotes jakarta.*. As classes existem, mas com outro nome de pacote. O problema é maior do que parece: bibliotecas de terceiros compiladas contra javax também param de funcionar, e às vezes só em tempo de execução, com ClassNotFoundException ou NoClassDefFoundError, quando o código delas tenta usar uma classe javax que não está mais lá. A migração é feita em etapas: atualizar primeiro para a última versão do Spring Boot 2.7, corrigindo os avisos de descontinuação; depois trocar os pacotes, o que ferramentas como o OpenRewrite, que aplica "receitas" de refatoração automática ao código-fonte, ou o Eclipse Transformer fazem na maior parte dos casos; trocar as bibliotecas de terceiros por versões compatíveis com Jakarta; e só então subir para o Spring Boot 3 e depois para o 4. Os testes automatizados, como os de "Provando que Funciona", são o que torna essa migração segura: sem eles, cada etapa seria uma aposta.
Exercício 3
Qual abordagem você escolheria em cada caso, e por quê? (a) uma ferramenta interna que expõe três rotas de consulta a um arquivo de log; (b) o sistema de uma loja virtual com cadastro, carrinho, pagamentos e uma equipe de dez pessoas; (c) funções em nuvem que sobem a cada requisição e precisam iniciar em milissegundos; (d) um sistema bancário que precisa rodar em servidores de aplicação certificados e seguir especificações formais.
Ver resposta
✓ Resposta: (a) O servidor embutido do JDK, com threads virtuais, ou um framework mínimo como o Javalin: três rotas não justificam dezenas de dependências, e sem nenhuma dependência externa não há bibliotecas a manter atualizadas por segurança. (b) O Spring Boot: a variedade de recursos necessários (banco de dados, transações, segurança, validação, integração com serviços de pagamento), o tamanho da equipe e a facilidade de contratar gente que já o conhece pesam mais que o tempo de inicialização de um serviço que fica no ar por dias. (c) O Quarkus ou o Micronaut compilados com o GraalVM Native Image, que iniciam em dezenas de milissegundos e usam pouca memória, ou, se o mundo fechado for um problema, o Spring Boot com o cache AOT do Leyden; o Spring Boot tradicional, com vários segundos de inicialização, seria caro e lento nesse cenário. (d) Uma implementação da Jakarta EE, como o WildFly, o Open Liberty ou o Payara, ou o Quarkus usando as especificações: quando um contrato ou uma norma exige aderência a especificações formais, com mais de um fornecedor possível, o padrão da Jakarta EE é o que oferece essa garantia. Em todos os casos, a escolha depende mais das pessoas, dos prazos e das restrições do que do desempenho bruto.
Exercício 4
Os frameworks são frequentemente descritos com a expressão inversão de controle, também chamada de "princípio de Hollywood": "não nos ligue, nós ligamos para você". Explique a ideia usando o Farol como exemplo, e diferencie um framework de uma biblioteca.
Ver resposta
✓ Resposta: Com uma biblioteca, o seu código está no controle: ele decide quando chamar, por exemplo, List.copyOf ou Files.readString, e a biblioteca só responde. Com um framework, o controle se inverte: o framework tem o laço principal e decide quando chamar o seu código, que você entrega a ele. No Farol, a aplicação não lê sockets nem decide quando uma requisição chega: ela registra manipuladores no Roteador, entrega o roteador ao Servidor e chama iniciar(). A partir daí, é o Servidor, com seu aceitador e suas conexões, que chama o Manipulador da aplicação, quando e na thread que quiser. É por isso que o Farol, apesar de ser pequeno, é um framework, e não uma biblioteca. A injeção de dependências é a mesma inversão aplicada à criação de objetos: em vez de o ServicoDeLembretes criar o seu repositório, alguém de fora o cria e o entrega. A vantagem é que o código da aplicação fica focado no seu problema e fácil de testar; a desvantagem é que o fluxo do programa fica menos visível, porque quem chama o quê está no framework, e é por isso que entender o que o framework faz por baixo, como o Farol ensinou, faz tanta diferença quando algo dá errado.
Exercício 5
No exemplo com o servidor do JDK, o que acontece com GET /trilhas/, GET /trilhas e GET /trilhas/1/fotos? Quais respostas estão erradas pelo ponto de vista do HTTP, e como corrigi-las?
Ver resposta
✓ Resposta: Testando com o curl: GET /trilhas/ devolveu {"erro":"id inválido"} com 400, porque o resto do caminho é uma String vazia e o parseInt falha; GET /trilhas, sem a barra final, devolveu o 404 padrão do próprio servidor, "No context found for request", porque o contexto /trilhas/ só casa com caminhos que começam com a barra; e GET /trilhas/1/fotos devolveu 400 "id inválido", porque o resto é 1/fotos. O terceiro caso está errado: o identificador não é inválido, o recurso /trilhas/1/fotos é que não existe, e a resposta certa é 404. O primeiro é discutível, mas a convenção de APIs REST é que /trilhas/ e /trilhas representem a coleção, e a resposta esperada seria a lista de trilhas, ou pelo menos um 404 ou 405, nunca um "id inválido". A correção é fazer o roteamento que o contexto de prefixo não faz: dividir o resto do caminho em segmentos, tratar zero segmentos como a coleção, exatamente um segmento como uma trilha (400 se não for número, 404 se não existir), e mais de um segmento como 404, e registrar um segundo contexto para /trilhas sem barra. É, em miniatura, o Roteador do Farol, e é o motivo pelo qual até quem usa o servidor do JDK acaba escrevendo um roteador, ou adotando um framework que já tenha um.