The key breaking changes in Spring Boot 3 — Jakarta namespace, Java 17 baseline, Actuator changes, Spring Security updates — and a practical migration path from Spring Boot 2.x.
Spring Boot 3.0 was a significant version bump: Java 17 is the minimum, the javax namespace became jakarta, Spring Security 6 rewrote its configuration model, and Actuator endpoints changed. The changes are well-defined, but an existing 2.x application has several migration steps before it runs on 3.x. Working through them systematically avoids surprises.
Java 17: Boot 3 requires Java 17. If you are on Java 11 or 8, upgrade the JDK first, compile your code with --release 17, and fix any deprecations before touching Boot.
Spring Boot 2.7: Migrate to the latest 2.7.x before jumping to 3.0. Spring Boot 2.7 ships deprecation warnings for APIs removed in 3.0 — they are your to-do list.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version> <!-- latest 2.7 first -->
</parent>
Fix all deprecation warnings from the compiler. Then change the version to 3.0.x.
Every javax.* import that belongs to Jakarta EE (not Java SE) changed to jakarta.*. This is the most widespread change.
// Before
import javax.persistence.Entity;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
// After
import jakarta.persistence.Entity;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.constraints.NotNull;
There are thousands of these in a typical application. Automate the rename with IntelliJ’s “Migrate to Jakarta EE 9” refactoring, or a simple sed script:
find src -name "*.java" -exec sed -i \
's/import javax\.persistence\./import jakarta.persistence./g;
s/import javax\.servlet\./import jakarta.servlet./g;
s/import javax\.validation\./import jakarta.validation./g;
s/import javax\.annotation\./import jakarta.annotation./g' {} \;
Check for javax in:
@Entity, @Column, @Table (persistence)HttpServletRequest/Response (servlet)@Valid, @NotNull, @Size (validation)@PostConstruct, @PreDestroy (annotation)@Resource, @Inject (injection — rare in Spring Boot but present)Spring Security 6 removed the WebSecurityConfigurerAdapter that most 2.x applications extend:
// Before — adapter style
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/public/**").permitAll()
.anyRequest().authenticated();
}
}
// After — component-based
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.build();
}
}
The method chain equivalents:
authorizeRequests() → authorizeHttpRequests()antMatchers() → requestMatchers()and() chaining → lambda DSL/actuator/health groups are configured differently:
# Before
management.health.probes.enabled: true
# After — same property, but new group names
management.endpoint.health.probes.enabled: true
management.health.livenessstate.enabled: true
management.health.readinessstate.enabled: true
The /actuator/prometheus endpoint requires explicit inclusion in Boot 3:
management:
endpoints:
web:
exposure:
include: health, info, prometheus, metrics
Spring Data JPA 3.x dropped support for Page<T> with @Query count queries that don’t specify countQuery explicitly. Add countQuery to any @Query pagination:
@Query(value = "SELECT o FROM Order o WHERE o.marketId = :id",
countQuery = "SELECT COUNT(o) FROM Order o WHERE o.marketId = :id")
Page<Order> findByMarketId(@Param("id") String marketId, Pageable pageable);
Hibernate 6 (the JPA provider) ships with Boot 3. It changed:
@Type(type = "json") → @JdbcTypeCode(SqlTypes.JSON) for JSON columns@CreationTimestamp / @UpdateTimestamp from org.hibernate.annotations still worksThird-party libraries must also be on Jakarta-compatible versions. Check the Spring Boot 3 compatibility matrix for common libraries.
Boot 3 enables circular dependency detection by default — if your application has circular bean dependencies it will fail to start. Fix the circular dependency (usually by introducing an interface or a @Lazy injection). If you genuinely cannot break the cycle immediately:
spring:
main:
allow-circular-references: true # temporary — fix properly
Several property keys were deprecated in 2.7 and removed in 3.0. Run your application with Boot 2.7 and look for DeprecatedPropertiesDetector warnings in the logs. Common ones:
# Removed in Boot 3 — use the replacement
spring.redis.* → spring.data.redis.*
spring.datasource.initialization-mode → spring.sql.init.mode
logging.file → logging.file.name
@SpringBootTest defaults for properties changed. If your tests rely on the old test property resolution order, review @TestPropertySource and @DynamicPropertySource usage.
MockMvc is auto-configured in @WebMvcTest without any change — the configuration model carries over.
javax → jakarta renamecountQuery to paginated @Query methodsIf you’re migrating a Spring Boot 2.x service to Boot 3 and want help with the migration plan, get in touch.