Spring Boot 3.5 encerra suporte OSS no dia 30/06/2026. Se o seu projeto ainda está no Boot 3.x, você tem menos de 30 dias para planejar a migração. O Boot 4 exige Java 21, elimina JUnit 4 do classpath padrão, força Hibernate 7 e remove Undertow. Aqui está o roteiro completo com código before/after para cada breaking change.
Nesse artigo você vai encontrar: o checklist dos 5 breaking changes mais impactantes do Spring Boot 4 com código before/after funcional em Java, a ordem recomendada de migração para não quebrar tudo ao mesmo tempo, e os erros mais comuns que times cometem tentando fazer tudo de uma vez.
Por que migrar agora: o prazo real
Spring Boot 3.5 tem suporte OSS até 30/06/2026. Depois disso, sem correções de segurança, sem patches, sem releases. Se seu projeto está em produção no Boot 3.x, a janela de migração planejada é agora, não depois que aparecer o CVE.
Spring Boot 4.1 está em milestone ativo (4.1.0-M4). A versão GA do Boot 4.0 foi lançada em fevereiro de 2026 com a stack completa: Spring Framework 7, Jakarta EE 11, Hibernate 7, JUnit 5 obrigatório e Java 21 como baseline. Não é uma atualização incremental de versão, é uma mudança de plataforma.
Breaking Change 1: Java 21 é obrigatório
Spring Boot 4 não sobe com Java 17 ou 11. Se você tentar, o erro é direto:
Error: LinkageError occurred while loading main class
java.lang.UnsupportedClassVersionError: Unsupported major.minor version 65.0
No pom.xml:
<!-- ANTES (Spring Boot 3.x) -->
<properties>
<java.version>17</java.version>
</properties>
<!-- DEPOIS (Spring Boot 4) -->
<properties>
<java.version>21</java.version>
</properties>
Use o Maven Toolchains Plugin para garantir que o projeto compila com Java 21 em todos os ambientes antes de subir o Boot 4:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-toolchains-plugin</artifactId>
<configuration>
<toolchains>
<jdk>
<version>21</version>
<vendor>temurin</vendor>
</jdk>
</toolchains>
</configuration>
</plugin>
Breaking Change 2: JUnit 4 foi removido do classpath padrão
Spring Boot 4 exclui junit:junit e o spring-test não mais inclui o runner do JUnit 4. Se você tem testes com @RunWith, eles simplesmente não compilam.
// ANTES (JUnit 4 - nao compila mais no Boot 4)
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class PedidoServiceTest {
@Test
public void deveCriarPedidoComSucesso() { }
}
// DEPOIS (JUnit 5 - padrao no Spring Boot 4)
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class PedidoServiceTest {
@Test
void deveCriarPedidoComSucesso() { }
}
Mapeamento completo das anotações:
// org.junit.Test -> org.junit.jupiter.api.Test
// org.junit.Before -> org.junit.jupiter.api.BeforeEach
// org.junit.After -> org.junit.jupiter.api.AfterEach
// org.junit.BeforeClass -> org.junit.jupiter.api.BeforeAll
// org.junit.AfterClass -> org.junit.jupiter.api.AfterAll
// org.junit.Ignore -> org.junit.jupiter.api.Disabled
// @RunWith(MockitoJUnitRunner)-> @ExtendWith(MockitoExtension.class)
Use a recipe org.openrewrite.java.testing.junit5.JUnit4to5Migration do OpenRewrite para automatizar a conversão em projetos grandes.
Breaking Change 3: Hibernate 7 e a API de persistência que mudou
Hibernate 7 implementa Jakarta Persistence 3.2 e quebra a API de Session diretamente. Os métodos save(), update(), saveOrUpdate() e delete() foram removidos da interface Session.
// ANTES (Hibernate 6 / Spring Boot 3.x)
public Produto salvar(Produto produto) {
Session session = entityManager.unwrap(Session.class);
return (Produto) session.save(produto); // removido no Hibernate 7
}
public void atualizar(Produto produto) {
Session session = entityManager.unwrap(Session.class);
session.update(produto); // removido no Hibernate 7
}
// DEPOIS (Hibernate 7 / Spring Boot 4)
public Produto salvar(Produto produto) {
entityManager.persist(produto);
return produto;
}
public Produto atualizar(Produto produto) {
return entityManager.merge(produto);
}
public void excluir(Produto produto) {
entityManager.remove(
entityManager.contains(produto)
? produto
: entityManager.merge(produto)
);
}
Quem usou JpaRepository do Spring Data JPA não precisa mexer em quase nada. O problema fica nos repositórios customizados que fazem session.unwrap() diretamente.
Breaking Change 4: Undertow removido e Jakarta EE 11
Undertow foi removido do Spring Boot 4 porque não é compatível com Servlet 6.1 (Jakarta EE 11). Se você usava Undertow como embedded server:
<!-- ANTES (Spring Boot 3.x com Undertow) -->
<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-undertow</artifactId>
</dependency>
<!-- DEPOIS (Spring Boot 4 - Undertow nao existe mais, usar Tomcat ou Jetty) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- Tomcat e o padrao -->
</dependency>
Breaking Change 5: Spring Security 7 e CSRF habilitado por padrão
Spring Security 7 habilita proteção CSRF por padrão para todos os endpoints, incluindo os REST. Resultado: POST/PUT/DELETE voltam 403 sem nenhuma mudança visível no seu código.
// ANTES (Spring Boot 3.x - sem CSRF em APIs REST era comum)
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
// DEPOIS (Spring Boot 4 - precisa ser explicito sobre CSRF em APIs REST)
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll()
)
.csrf(csrf -> csrf
.ignoringRequestMatchers("/api/**") // desabilitar CSRF para REST stateless
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
Desabilitar CSRF para APIs REST é válido quando você usa autenticação stateless (JWT, OAuth2 Bearer Token). CSRF protege sessões baseadas em cookie.
A ordem correta de migração
Fase 1: Java 21 primeiro (sem mudar o Boot)
Suba o Java para 21 no projeto ainda no Spring Boot 3.5. Execute todos os testes. Resolva incompatibilidades de API. Só depois avance.
Fase 2: JUnit 5 (ainda no Boot 3.5)
Migre os testes de JUnit 4 para JUnit 5. Use o OpenRewrite para automatizar. Execute tudo. Certinho antes de avançar.
Fase 3: Boot 4 com feature flags
Atualize para Spring Boot 4. Execute os testes. Espere ver erros de Hibernate e CSRF. Corrija um por vez.
Fase 4: Limpeza e otimização
Remova dependências de Undertow. Elimine código legado. Habilite features do Java 21 que o Boot 4 suporta (Records em @ConfigurationProperties, Virtual Threads).
FAQ: Perguntas Frequentes
Posso usar Spring Boot 4 com Java 17?
Não. Java 21 é o baseline obrigatório. O Spring Framework 7 usa APIs introduzidas no Java 21 internamente.
Meu Spring Data JPA vai funcionar sem reescrita?
Na maioria dos casos sim. O problema fica em repositórios customizados que usam Session.save() ou Session.update() diretamente via Hibernate.
O que acontece com minha aplicação no Boot 3.5 depois de 30/06/2026?
A aplicação continua rodando. Sem suporte OSS significa sem novos patches de segurança. O risco real são CVEs novos descobertos depois do EOL ficarem sem fix oficial.
Vale pular o Boot 4.0 e esperar o Boot 4.1?
Boot 4.1 ainda está em milestone (M4). Para produção, Boot 4.0 GA é a versão estável. Começar agora com Boot 4.0 e depois atualizar para 4.1 é mais seguro do que esperar.
OpenRewrite consegue migrar tudo automaticamente?
Não completamente. A recipe cobre bastante coisa, mas não cobre Hibernate 7 (depende do comportamento de negócio) nem as regras de CSRF (decisão de arquitetura de segurança). OpenRewrite é um acelerador, não uma solução completa.
Migração para Spring Boot 4 não é opcional se você quer manter seu projeto com suporte ativo depois de junho. Com a sequência certa não precisa virar um sprint de emergência. Comenta aqui o breaking change que mais te preocupa no seu projeto.
Segue o Meu Universo Nerd para não perder o próximo conteúdo da série Spring Boot 4.