SpringApplication e o embedded server
TL;DR
SpringApplication.run()faz três coisas de uma vez: cria oApplicationContextcorreto para o tipo de aplicação, dispara o ciclo de auto-configuration e sobe o servidor embutido. O servidor padrão é Tomcat; trocar por Jetty ou Undertow é uma questão de mover starters no POM. O resultado é um fat jar — um único artefato autocontido que roda comjava -jarsem precisar de servidor externo.CommandLineRunnereApplicationRunnersão os ganchos canônicos para executar código logo após o contexto estar pronto, antes de o servidor aceitar tráfego.
O que é
SpringApplication é a classe de entrada do Spring Boot. Seu método estático run() orquestra toda a inicialização: detecta o tipo de aplicação (web Servlet, Reactive ou standalone), instancia o ApplicationContext adequado, ativa a auto-configuration e levanta o servidor embutido configurado.
O embedded server (servidor embutido) é um servidor HTTP que reside dentro do fat jar — não é necessário instalar nem gerenciar um contêiner externo. Tomcat, Jetty e Undertow são as opções suportadas pelo Spring Boot 3.x, com Tomcat como padrão via spring-boot-starter-web.
Por que importa
O modelo de servidor embutido muda o paradigma de deploy: a aplicação não é mais um WAR implantado num servidor externo, mas um processo autossuficiente. Isso alinha com arquitetura de microsserviços e containers Docker — cada serviço carrega sua própria versão do servidor, elimina conflitos de classpath entre aplicações num mesmo contêiner e simplifica pipelines de CI/CD.
Para entrevistas de nível sênior, o entrevistador espera que o candidato compreenda não só “como funciona” mas também os trade-offs: startup mais lento com lazy-initialization desligada, risco de lógica pesada travar o boot, e quando faz sentido permanecer no modelo WAR.
Como funciona
SpringApplication.run(): cria o contexto, dispara auto-config, sobe o servidor
O ponto de entrada mínimo de uma aplicação Boot é:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}Internamente, run() executa a seguinte sequência:
- Publica
ApplicationStartingEvent(listeners registrados antes do contexto recebem). - Prepara o
Environment(carregaapplication.properties, perfis) →ApplicationEnvironmentPreparedEvent. - Determina o tipo de contexto:
AnnotationConfigServletWebServerApplicationContextpara Servlet,AnnotationConfigReactiveWebServerApplicationContextpara Reactive,AnnotationConfigApplicationContextpara standalone. - Executa os
ApplicationContextInitializer→ApplicationContextInitializedEvent. - Carrega definições de beans; aplica auto-configuration →
ApplicationPreparedEvent. - Chama
context.refresh()— aqui o servidor embutido é instanciado e inicia a escuta. - Publica
ApplicationStartedEvent+AvailabilityChangeEvent(LivenessState.CORRECT). - Executa
ApplicationRunners eCommandLineRunners. - Publica
ApplicationReadyEvent+AvailabilityChangeEvent(ReadinessState.ACCEPTING_TRAFFIC).
Se qualquer etapa falhar, o Boot publica ApplicationFailedEvent e exibe uma mensagem diagnóstica de FailureAnalyzer — por exemplo, “Port 8080 was already in use” com sugestão de ação.
A personalização do SpringApplication pode ser feita programaticamente antes de chamar run():
SpringApplication app = new SpringApplication(MyApplication.class);
app.setBannerMode(Banner.Mode.OFF);
app.setLazyInitialization(true);
app.run(args);Ou via application.properties:
spring.main.lazy-initialization=true
spring.main.banner-mode=offApplicationRunner/CommandLineRunner e listeners do startup
CommandLineRunner e ApplicationRunner são interfaces funcionais executadas depois que o contexto foi totalmente inicializado e o servidor está no ar, mas antes de ApplicationReadyEvent ser publicado. São beans Spring normais — use @Order para sequenciar múltiplos runners.
@Component
@Order(1)
public class CargaDadosRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List<String> arquivos = args.getNonOptionArgs();
// inicialização após o boot
}
}
@Component
@Order(2)
public class VerificacaoRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// recebe args raw como String[]
}
}A diferença prática: ApplicationRunner recebe ApplicationArguments, que parse --chave=valor estruturado; CommandLineRunner recebe String[] direto do main.
Para lógica que precisa reagir a eventos antes do contexto existir (ex.: ajustar Environment), o caminho é ApplicationListener<ApplicationEnvironmentPreparedEvent> registrado via META-INF/spring.factories ou via SpringApplication.addListeners().
Embedded server (Tomcat default; Jetty/Undertow via starter)
Ao detectar spring-boot-starter-web no classpath, a auto-configuration instancia um TomcatServletWebServerFactory e sobe o Tomcat embutido na porta 8080.
Para trocar para Jetty:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>O mesmo padrão vale para Undertow — substitua spring-boot-starter-jetty por spring-boot-starter-undertow. A auto-configuration detecta qual factory está disponível e usa.
Configuração básica via application.properties:
server.port=8080
server.servlet.context-path=/apiCustomizações avançadas (thread pool, timeouts, connectors) são feitas via WebServerFactoryCustomizer<TomcatServletWebServerFactory>. O pipeline web sobre esse servidor é o galho Web e APIs REST; tuning profundo de servidor e configurações de produção ficam para o Galho 17 (config e recursos em produção).
O servidor embutido roda o Servlet container
O embedded server implementa o contêiner de Servlets que a Servlet API (Jakarta EE) define. O Spring MVC usa
DispatcherServletcomo front controller, que é registrado nesse contêiner. Para entender a spec que o embedded Tomcat implementa, veja Servlet API — o alicerce HTTP.
Fat/executable jar e layered jar (conceito)
Um fat jar (ou executable jar) é um único arquivo .jar que contém:
- O bytecode da aplicação
- Todas as dependências (jars dentro do jar)
- O servidor embutido
- O Spring Boot Launcher, que sabe como carregar classes de JARs aninhados
Para executar:
java -jar minha-aplicacao-1.0.0.jar --server.port=9090O Maven Plugin do Spring Boot cuida do empacotamento:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>O layered jar é uma variante que organiza o conteúdo em camadas (dependencies, spring-boot-loader, snapshot-dependencies, application) para otimizar cache de layers em imagens Docker — a camada de dependências externas raramente muda; só a camada application é reconstruída no pipeline. O detalhamento de packaging (fat/thin/uber jar, Maven Shade/Gradle Shadow) e layered jars é coberto em Empacotamento (Galho 15); imagens nativas (GraalVM) são o Galho 17 (GraalVM Native Image).
Na prática
Exemplo completo de main com um CommandLineRunner para aquecimento:
@SpringBootApplication
public class CatalogoApplication {
public static void main(String[] args) {
SpringApplication.run(CatalogoApplication.class, args);
}
// Runner como bean inline (alternativa ao @Component separado)
@Bean
public CommandLineRunner aquecimento(CatalogoService service) {
return args -> {
System.out.println("Servidor no ar. Aquecendo cache...");
service.precarregarCatalogo();
};
}
}Usar @Bean CommandLineRunner com injeção de parâmetro é idiomático quando o runner precisa de um colaborador — evita @Autowired num @Component.
Armadilhas
1. Lógica pesada diretamente no main
// Errado — bloqueia o bootstrap antes do contexto existir
public static void main(String[] args) {
SpringApplication.run(App.class, args);
carregarDados(); // nunca chega aqui; mesmo que chegue, contexto pode já estar fechando
}// Correto — delega para runner que roda com o contexto pronto
@Bean
public ApplicationRunner inicializacao(DataService service) {
return args -> service.carregarDados();
}2. Assumir modelo WAR/servidor externo por hábito
<!-- Errado — empacotar como WAR e depender de servidor externo num projeto novo -->
<packaging>war</packaging>// Errado — extends SpringBootServletInitializer sem necessidade real
public class App extends SpringBootServletInitializer { ... }O modelo padrão do Spring Boot é fat jar + servidor embutido. Herdar SpringBootServletInitializer e empacotar como WAR só faz sentido quando o deploy é obrigatoriamente num servidor externo legado (ex.: WebSphere corporativo). Em projetos greenfield, use fat jar.
3. Bloquear o startup num runner
// Errado — operação bloqueante longa impede ApplicationReadyEvent
@Component
public class SyncRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
sincronizarBaseDados(); // leva 5 minutos; saúde/readiness nunca é reportada
}
}// Correto — disparar assíncrono ou limitar o trabalho síncrono a operações rápidas
@Component
public class SyncRunner implements CommandLineRunner {
private final TaskExecutor executor;
@Override
public void run(String... args) {
executor.execute(this::sincronizarBaseDados);
}
}Runners síncronos que demoram impedem ReadinessState.ACCEPTING_TRAFFIC — o load balancer considera a instância não pronta e pode desregistrá-la.
Em entrevista
Frase pronta (inglês)
- “When you call
SpringApplication.run(), Spring Boot determines the correct application context type, triggers auto-configuration based on the classpath, and starts the embedded server — all before control returns to your code.” - “
CommandLineRunnerandApplicationRunnerrun after the context is fully refreshed but before the application signals readiness, making them the right place for post-startup initialization — as long as they don’t block for too long.” - “A fat jar bundles the application code, all dependencies, and an embedded servlet container into a single executable artifact. You just
java -jarit — no external application server required.” - “Switching the embedded server from Tomcat to Jetty is purely a dependency swap: exclude
spring-boot-starter-tomcat, includespring-boot-starter-jetty— Spring Boot’s auto-configuration picks it up automatically.”
Vocabulário
| Termo PT | Termo EN |
|---|---|
| servidor embutido | embedded server |
| jar gordo / jar executável | fat jar / executable jar |
| jar em camadas | layered jar |
| inicialização preguiçosa | lazy initialization |
| runner de inicialização | startup runner |
| evento de disponibilidade | availability change event |
| fábrica de servidor web | web server factory |
| analisador de falha | failure analyzer |
Veja também
- Auto-configuration e starters
- Actuator e observabilidade
- Servlet API — o alicerce HTTP — o embedded server roda o Servlet container que a spec define; esta nota não repete a spec, apenas usa o contrato
- Spring Core e Boot (MOC do galho)
- Trilha Java
- SpringApplication
- embedded server
- fat jar