{"kind":"AgentDefinition","metadata":{"namespace":"community","name":"springboot-4-migration","version":"0.1.0"},"spec":{"agents_md":"---\ndescription: \"Comprehensive guide for migrating Spring Boot applications from 3.x to 4.0, focusing on Gradle Kotlin DSL and version catalogs\"\napplyTo: \"**/*.java, **/*.kt, **/build.gradle.kts, **/build.gradle, **/settings.gradle.kts, **/gradle/libs.versions.toml, **/*.properties, **/*.yml, **/*.yaml\"\n---\n\n# Spring Boot 3.x to 4.0 Migration Guide\n\n## Project Context\n\nThis guide provides comprehensive GitHub Copilot instructions for upgrading Spring Boot projects from version 3.x to 4.0, with emphasis on Gradle Kotlin DSL, version catalogs (`libs.versions.toml`), and Kotlin-specific considerations.\n\n**Key architectural changes in Spring Boot 4.0:**\n- Modular dependency structure with focused, smaller modules\n- Spring Framework 7.x required\n- Jakarta EE 11 (Servlet 6.1 baseline)\n- Jackson 3.x migration (package namespace changes)\n- Kotlin 2.2+ requirement\n- Comprehensive property reorganization\n\n## System Requirements\n\n### Minimum Versions\n\n- **Java**: 17+ (prefer latest LTS: Java 21 or 25)\n- **Kotlin**: 2.2.0 or later\n- **Spring Framework**: 7.x (managed by Spring Boot 4.0)\n- **Jakarta EE**: 11 (Servlet 6.1 baseline)\n- **GraalVM** (for native images): 25+\n- **Gradle**: 8.5+ (for Kotlin DSL and version catalog support)\n- **Gradle CycloneDX Plugin**: 3.0.0+\n\n### Verify Compatibility\n\n```bash\n# Check current versions\n./gradlew --version\n./gradlew dependencies --configuration runtimeClasspath\n```\n\n## Pre-Migration Steps\n\n### 1. Upgrade to Latest Spring Boot 3.5.x\n\nBefore migrating to 4.0, upgrade to the latest 3.5.x release:\n\n```kotlin\n// libs.versions.toml\n[versions]\nspringBoot = \"3.5.6\" # Latest 3.x before migrating to 4.0\n```\n\n### 2. Clean Up Deprecations\n\nRemove all deprecated API usage from Spring Boot 3.x. These will be compilation errors in 4.0:\n\n```bash\n# Build and review warnings\n./gradlew clean build --warning-mode all\n```\n\n### 3. Review Dependency Changes\n\nCompare your dependencies against:\n- [Spring Boot 3.5.x Dependency Versions](https://docs.spring.io/spring-boot/3.5/appendix/dependency-versions/coordinates.html)\n- [Spring Boot 4.0.x Dependency Versions](https://docs.spring.io/spring-boot/4.0/appendix/dependency-versions/coordinates.html)\n\n## Module Restructuring and Starter Changes\n\n### Critical: Modular Architecture\n\nSpring Boot 4.0 introduces **smaller, focused modules** replacing large monolithic jars. This requires dependency updates in most projects.\n\n**Important for Library Authors:** Due to the modularization effort and package reorganization, **supporting both Spring Boot 3 and Spring Boot 4 within the same artifact is strongly discouraged**. Library authors should publish separate artifacts for each major version to avoid runtime conflicts and ensure clean dependency management.\n\n### Migration Strategy: Choose One Approach\n\n#### Option 1: Technology-Specific Starters (Recommended for Production)\n\nMost technologies covered by Spring Boot now have **dedicated test starter companions**. This provides fine-grained control.\n\n**Complete Starter Reference:** For comprehensive tables of all available starters (Core, Web, Database, Spring Data, Messaging, Security, Templating, Production-Ready, etc.) and their test companions, refer to the [official Spring Boot 4.0 Migration Guide](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide#starters).\n\n**libs.versions.toml:**\n```toml\n[versions]\nspringBoot = \"4.0.0\"\n\n[libraries]\n# Core starters with dedicated test modules\nspring-boot-starter-web = { module = \"org.springframework.boot:spring-boot-starter-webmvc\", version.ref = \"springBoot\" }\nspring-boot-starter-webmvc-test = { module = \"org.springframework.boot:spring-boot-starter-webmvc-test\", version.ref = \"springBoot\" }\n\nspring-boot-starter-data-jpa = { module = \"org.springframework.boot:spring-boot-starter-data-jpa\", version.ref = \"springBoot\" }\nspring-boot-starter-data-jpa-test = { module = \"org.springframework.boot:spring-boot-starter-data-jpa-test\", version.ref = \"springBoot\" }\n\nspring-boot-starter-security = { module = \"org.springframework.boot:spring-boot-starter-security\", version.ref = \"springBoot\" }\nspring-boot-starter-security-test = { module = \"org.springframework.boot:spring-boot-starter-security-test\", version.ref = \"springBoot\" }\n```\n\n**build.gradle.kts:**\n```kotlin\ndependencies {\n    implementation(libs.spring.boot.starter.webmvc)\n    implementation(libs.spring.boot.starter.data.jpa)\n    implementation(libs.spring.boot.starter.security)\n\n    testImplementation(libs.spring.boot.starter.webmvc.test)\n    testImplementation(libs.spring.boot.starter.data.jpa.test)\n    testImplementation(libs.spring.boot.starter.security.test)\n}\n```\n\n#### Option 2: Classic Starters (Quick Migration, Deprecated)\n\nFor rapid migration, use **classic starters** that bundle all auto-configuration (like Spring Boot 3.x):\n\n**libs.versions.toml:**\n```toml\n[libraries]\nspring-boot-starter-classic = { module = \"org.springframework.boot:spring-boot-starter-classic\", version.ref = \"springBoot\" }\nspring-boot-starter-test-classic = { module = \"org.springframework.boot:spring-boot-starter-test-classic\", version.ref = \"springBoot\" }\n```\n\n**build.gradle.kts:**\n```kotlin\ndependencies {\n    implementation(libs.spring.boot.starter.classic)\n    testImplementation(libs.spring.boot.starter.test.classic)\n}\n```\n\n**Warning**: Classic starters are **deprecated** and will be removed in future releases. Plan migration to technology-specific starters.\n\n#### Option 3: Direct Module Dependencies (Advanced)\n\nFor explicit control over transitive dependencies:\n\n**libs.versions.toml:**\n```toml\n[libraries]\nspring-boot-webmvc = { module = \"org.springframework.boot:spring-boot-webmvc\", version.ref = \"springBoot\" }\nspring-boot-webmvc-test = { module = \"org.springframework.boot:spring-boot-webmvc-test\", version.ref = \"springBoot\" }\n```\n\n### Renamed Starters (Breaking Changes)\n\nUpdate these starter names in your `libs.versions.toml`:\n\n| Spring Boot 3.x | Spring Boot 4.0 | Notes |\n|----------------|-----------------|-------|\n| `spring-boot-starter-web` | `spring-boot-starter-webmvc` | Explicit naming |\n| `spring-boot-starter-web-services` | `spring-boot-starter-webservices` | Hyphen removed |\n| `spring-boot-starter-aop` | `spring-boot-starter-aspectj` | Only needed if using `org.aspectj.lang.annotation` |\n| `spring-boot-starter-oauth2-authorization-server` | `spring-boot-starter-security-oauth2-authorization-server` | Security namespace |\n| `spring-boot-starter-oauth2-client` | `spring-boot-starter-security-oauth2-client` | Security namespace |\n| `spring-boot-starter-oauth2-resource-server` | `spring-boot-starter-security-oauth2-resource-server` | Security namespace |\n\n**Migration Example (libs.versions.toml):**\n```toml\n[libraries]\n# Old (Spring Boot 3.x)\n# spring-boot-starter-web = { module = \"org.springframework.boot:spring-boot-starter-web\", version.ref = \"springBoot\" }\n# spring-boot-starter-oauth2-client = { module = \"org.springframework.boot:spring-boot-starter-oauth2-client\", version.ref = \"springBoot\" }\n\n# New (Spring Boot 4.0)\nspring-boot-starter-webmvc = { module = \"org.springframework.boot:spring-boot-starter-webmvc\", version.ref = \"springBoot\" }\nspring-boot-starter-security-oauth2-client = { module = \"org.springframework.boot:spring-boot-starter-security-oauth2-client\", version.ref = \"springBoot\" }\n```\n\n### AspectJ Starter Clarification\n\nOnly include `spring-boot-starter-aspectj` if you're **actually using AspectJ annotations**:\n\n```kotlin\n// Only needed if code uses org.aspectj.lang.annotation package\nimport org.aspectj.lang.annotation.Aspect\nimport org.aspectj.lang.annotation.Before\n\n@Aspect\nclass MyAspect {\n    @Before(\"execution(* com.example..*(..))\")\n    fun beforeAdvice() { }\n}\n```\n\nIf not using AspectJ, remove the dependency.\n\n## Removed Features and Alternatives\n\n### Embedded Servers\n\n#### Undertow Removed\n\n**Undertow is completely removed** - not compatible with Servlet 6.1 baseline.\n\n**Migration:**\n- Use **Tomcat** (default) or **Jetty**\n- Do **not** deploy Spring Boot 4.0 apps to non-Servlet 6.1 containers\n\n**libs.versions.toml:**\n```toml\n[libraries]\n# Remove Undertow\n# spring-boot-starter-undertow = { module = \"org.springframework.boot:spring-boot-starter-undertow\", version.ref = \"springBoot\" }\n\n# Use Tomcat (default) or Jetty\nspring-boot-starter-jetty = { module = \"org.springframework.boot:spring-boot-starter-jetty\", version.ref = \"springBoot\" }\n```\n\n**build.gradle.kts:**\n```kotlin\ndependencies {\n    implementation(libs.spring.boot.starter.webmvc) {\n        exclude(group = \"org.springframework.boot\", module = \"spring-boot-starter-tomcat\")\n    }\n    implementation(libs.spring.boot.starter.jetty) // Alternative to Tomcat\n}\n```\n\n### Session Management\n\n#### Spring Session Hazelcast and MongoDB Removed\n\n**Maintained by respective teams**, no longer in Spring Boot dependency management.\n\n**Migration (libs.versions.toml):**\n```toml\n[versions]\nhazelcast-spring-session = \"3.x.x\" # Check Hazelcast documentation\nmongodb-spring-session = \"4.x.x\"   # Check MongoDB documentation\n\n[libraries]\n# Explicit versions required\nspring-session-hazelcast = { module = \"com.hazelcast:spring-session-hazelcast\", version.ref = \"hazelcast-spring-session\" }\nspring-session-mongodb = { module = \"org.springframework.session:spring-session-data-mongodb\", version.ref = \"mongodb-spring-session\" }\n```\n\n### Reactive Messaging\n\n#### Pulsar Reactive Removed\n\nSpring Pulsar dropped Reactor support - reactive Pulsar client removed.\n\n**Migration:**\n- Use imperative Pulsar client\n- Or migrate to alternative reactive messaging (Kafka, RabbitMQ)\n\n### Testing\n\n#### Spock Framework Removed\n\n**Spock does not yet support Groovy 5** (required for Spring Boot 4.0).\n\n**Migration:**\n- Use JUnit 5 with Kotlin\n- Or wait for Spock Groovy 5 compatibility\n\n### Build Features\n\n#### Executable Jar Launch Scripts Removed\n\nEmbedded launch scripts for \"fully executable\" jars removed (Unix-specific, limited use).\n\n**build.gradle.kts (remove):**\n```kotlin\n// Remove this configuration\ntasks.bootJar {\n    launchScript() // No longer supported\n}\n```\n\n**Alternatives:**\n- Use `java -jar app.jar` directly\n- Use Gradle Application Plugin for native launchers\n- Use systemd service files\n\n#### Classic Uber-Jar Loader Removed\n\nThe classic uber-jar loader has been removed. Remove any loader implementation configuration from your build.\n\n**Maven (pom.xml) - remove:**\n```xml\n\u003cbuild\u003e\n    \u003cplugins\u003e\n        \u003cplugin\u003e\n            \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n            \u003cartifactId\u003espring-boot-maven-plugin\u003c/artifactId\u003e\n            \u003cconfiguration\u003e\n                \u003cloaderImplementation\u003eCLASSIC\u003c/loaderImplementation\u003e \u003c!-- REMOVE THIS --\u003e\n            \u003c/configuration\u003e\n        \u003c/plugin\u003e\n    \u003c/plugins\u003e\n\u003c/build\u003e\n```\n\n**Gradle (build.gradle.kts) - remove:**\n```kotlin\ntasks.bootJar {\n    loaderImplementation = org.springframework.boot.loader.tools.LoaderImplementation.CLASSIC // REMOVE THIS\n}\n```\n\n## Jackson 3 Migration\n\n### Major Breaking Change: Package Namespace\n\nJackson 3 changes **group ID and package names**:\n\n| Component | Old (Jackson 2) | New (Jackson 3) |\n|-----------|----------------|-----------------|\n| Group ID | `com.fasterxml.jackson` | `tools.jackson` |\n| Packages | `com.fasterxml.jackson.*` | `tools.jackson.*` |\n| Exception | `jackson-annotations` | Still uses `com.fasterxml.jackson.core` group |\n\n**libs.versions.toml:**\n```toml\n[versions]\njackson = \"3.0.1\" # Managed by Spring Boot 4.0\n\n[libraries]\n# Jackson 3 uses new group ID\njackson-databind = { module = \"tools.jackson.core:jackson-databind\", version.ref = \"jackson\" }\njackson-module-kotlin = { module = \"tools.jackson.module:jackson-module-kotlin\", version.ref = \"jackson\" }\n\n# Exception: annotations still use old group\njackson-annotations = { module = \"com.fasterxml.jackson.core:jackson-annotations\", version.ref = \"jackson\" }\n```\n\n### Class and Annotation Renames\n\nUpdate imports and annotations:\n\n| Spring Boot 3.x | Spring Boot 4.0 |\n|----------------|-----------------|\n| `Jackson2ObjectMapperBuilderCustomizer` | `JsonMapperBuilderCustomizer` |\n| `JsonObjectSerializer` | `ObjectValueSerializer` |\n| `JsonValueDeserializer` | `ObjectValueDeserializer` |\n| `@JsonComponent` | `@JacksonComponent` |\n| `@JsonMixin` | `@JacksonMixin` |\n\n**Migration Example:**\n```kotlin\n// Old (Spring Boot 3.x)\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer\nimport org.springframework.boot.jackson.JsonComponent\n\n@JsonComponent\nclass CustomSerializer : JsonSerializer\u003cMyType\u003e() { }\n\n@Configuration\nclass JacksonConfig {\n    @Bean\n    fun customizer(): Jackson2ObjectMapperBuilderCustomizer {\n        return Jackson2ObjectMapperBuilderCustomizer { builder -\u003e\n            builder.simpleDateFormat(\"yyyy-MM-dd\")\n        }\n    }\n}\n\n// New (Spring Boot 4.0)\nimport tools.jackson.databind.ObjectMapper\nimport org.springframework.boot.autoconfigure.jackson.JsonMapperBuilderCustomizer\nimport org.springframework.boot.jackson.JacksonComponent\n\n@JacksonComponent\nclass CustomSerializer : JsonSerializer\u003cMyType\u003e() { }\n\n@Configuration\nclass JacksonConfig {\n    @Bean\n    fun customizer(): JsonMapperBuilderCustomizer {\n        return JsonMapperBuilderCustomizer { builder -\u003e\n            builder.simpleDateFormat(\"yyyy-MM-dd\")\n        }\n    }\n}\n```\n\n### Configuration Property Changes\n\n**application.yml migration:**\n```yaml\n# Old (Spring Boot 3.x)\nspring:\n  jackson:\n    read:\n      enums-using-to-string: true\n    write:\n      dates-as-timestamps: false\n\n# New (Spring Boot 4.0)\nspring:\n  jackson:\n    json:\n      read:\n        enums-using-to-string: true\n      write:\n        dates-as-timestamps: false\n```\n\n### Jackson 2 Compatibility Module (Temporary)\n\nFor gradual migration, use the **temporary compatibility module** (deprecated, will be removed):\n\n**libs.versions.toml:**\n```toml\n[libraries]\nspring-boot-jackson2 = { module = \"org.springframework.boot:spring-boot-jackson2\", version.ref = \"springBoot\" }\n```\n\n**build.gradle.kts:**\n```kotlin\ndependencies {\n    implementation(libs.spring.boot.jackson2)\n}\n```\n\n**application.yml:**\n```yaml\nspring:\n  jackson:\n    use-jackson2-defaults: true # Use Jackson 2 behavior\n```\n\n**Properties under `spring.jackson2.*` namespace** when using compatibility module.\n\n**Plan migration away from this module** - it will be removed in future versions.\n\n## Core Framework Changes\n\n### Nullability Annotations: JSpecify\n\nSpring Boot 4.0 adds **JSpecify nullability annotations** throughout the codebase.\n\n**Impact:**\n- Kotlin null-safety may flag new warnings/errors\n- Null checkers (SpotBugs, NullAway) may report new issues\n- **RestClient methods like `body()` are now explicitly marked as nullable** - always check for null or use `Objects.requireNonNull()`\n\n**Migration for Kotlin:**\n```kotlin\n// Explicit nullable types may be required\nfun processUser(id: String?): User? {\n    return userRepository.findById(id) // May now be explicitly nullable\n}\n\n// RestClient body() can return null\nval body: String? = restClient.get()\n    .uri(\"https://api.example.com/data\")\n    .retrieve()\n    .body(String::class.java) // Nullable - handle appropriately\n\nif (body != null) {\n    println(body.length)\n}\n```\n\n**Actuator endpoint parameters:**\n- Cannot use `javax.annotations.NonNull` or `org.springframework.lang.Nullable`\n- Use `org.jspecify.annotations.Nullable` instead\n\n**libs.versions.toml:**\n```toml\n[libraries]\njspecify = { module = \"org.jspecify:jspecify\", version = \"1.0.0\" }\n```\n\n### Package Relocations\n\n#### BootstrapRegistry\n\n**Old import:**\n```kotlin\nimport org.springframework.boot.BootstrapRegistry\n```\n\n**New import:**\n```kotlin\nimport org.springframework.boot.bootstrap.BootstrapRegistry\n```\n\n#### EnvironmentPostProcessor\n\n**Old import:**\n```kotlin\nimport org.springframework.boot.env.EnvironmentPostProcessor\n```\n\n**New import:**\n```kotlin\nimport org.springframework.boot.EnvironmentPostProcessor\n```\n\n**Update `META-INF/spring.factories`:**\n```properties\n# Old\norg.springframework.boot.env.EnvironmentPostProcessor=com.example.MyPostProcessor\n\n# New\norg.springframework.boot.EnvironmentPostProcessor=com.example.MyPostProcessor\n```\n\n**Note:** Deprecated form still available temporarily but will be removed.\n\n#### Entity Scan\n\n**Old import:**\n```kotlin\nimport org.springframework.boot.autoconfigure.domain.EntityScan\n```\n\n**New import:**\n```kotlin\nimport org.springframework.boot.persistence.autoconfigure.EntityScan\n```\n\n### Logging Changes\n\n#### Logback Default Charset\n\nLog files now default to **UTF-8** (harmonized with Log4j2):\n\n**logback-spring.xml (explicit configuration):**\n```xml\n\u003cconfiguration\u003e\n    \u003cappender name=\"FILE\" class=\"ch.qos.logback.core.FileAppender\"\u003e\n        \u003cfile\u003eapp.log\u003c/file\u003e\n        \u003cencoder\u003e\n            \u003ccharset\u003eUTF-8\u003c/charset\u003e \u003c!-- Now default --\u003e\n            \u003cpattern\u003e%d{yyyy-MM-dd HH:mm:ss} - %msg%n\u003c/pattern\u003e\n        \u003c/encoder\u003e\n    \u003c/appender\u003e\n\u003c/configuration\u003e\n```\n\n**Console logging:** Uses `Console#charset()` if available (Java 17+), otherwise falls back to UTF-8. This provides better platform compatibility while maintaining consistent encoding.\n\n### DevTools Changes\n\n#### Live Reload Disabled by Default\n\n**application.yml:**\n```yaml\nspring:\n  devtools:\n    livereload:\n      enabled: true # Must explicitly enable in 4.0\n```\n\n**libs.versions.toml:**\n```toml\n[libraries]\nspring-boot-devtools = { module = \"org.springframework.boot:spring-boot-devtools\", version.ref = \"springBoot\" }\n```\n\n**build.gradle.kts:**\n```kotlin\ndependencies {\n    developmentOnly(libs.spring.boot.devtools)\n}\n```\n\n### PropertyMapper API Behavioral Change\n\n**Breaking change:** No longer calls adapter/predicate methods by default when source is `null`.\n\n**Migration pattern:**\n```kotlin\n// Old behavior (Spring Boot 3.x)\nmap.from(source::method).to(destination::method)\n// Calls destination.method(null) if source returns null\n\n// New behavior (Spring Boot 4.0)\nmap.from(source::method).to(destination::method)\n// Skips call if source returns null\n\n// Explicit null handling (new)\nmap.from(source::method).always().to(destination::method)\n// Always calls destination.method(value), even if null\n```\n\n**Removed method:** `alwaysApplyingNotNull()` - use `always()` instead.\n\n**Migration example:** Review [Spring Boot commit 239f384ac0](https://github.com/spring-projects/spring-boot/commit/239f384ac0893d151b89f204886874c6adb00001) to see how Spring Boot itself adapted to the new API.\n\n## Dependency and Build Changes\n\n### Gradle Plugin Updates\n\n**build.gradle.kts:**\n```kotlin\nplugins {\n    kotlin(\"jvm\") version \"2.2.0\" // Minimum 2.2.0\n    kotlin(\"plugin.spring\") version \"2.2.0\"\n    id(\"org.springframework.boot\") version \"4.0.0\"\n    id(\"io.spring.dependency-management\") version \"1.1.7\"\n    id(\"org.cyclonedx.bom\") version \"3.0.0\" // Minimum 3.0.0\n}\n```\n\n### Optional Dependencies in Gradle\n\nOptional dependencies are **no longer included in uber jars by default**.\n\n**build.gradle.kts (include optionals explicitly):**\n```kotlin\ntasks.bootJar {\n    includeOptional = true // If needed\n}\n```\n\n### Spring Retry → Spring Framework Core Retry\n\nSpring Boot 4.0 removes dependency management for **Spring Retry** (portfolio migrating to Spring Framework 7.0 core retry).\n\n**Migration Option 1: Use Spring Framework Core Retry (Recommended)**\n\n```kotlin\n// Use built-in Spring Framework retry\nimport org.springframework.core.retry.RetryTemplate\nimport org.springframework.core.retry.support.RetryTemplateBuilder\n\n@Configuration\nclass RetryConfig {\n    @Bean\n    fun retryTemplate(): RetryTemplate {\n        return RetryTemplateBuilder()\n            .maxAttempts(3)\n            .fixedBackoff(1000)\n            .build()\n    }\n}\n```\n\n**Migration Option 2: Explicit Spring Retry Version (Temporary)**\n\n**libs.versions.toml:**\n```toml\n[versions]\nspring-retry = \"2.0.5\" # Explicit version required\n\n[libraries]\nspring-retry = { module = \"org.springframework.retry:spring-retry\", version.ref = \"spring-retry\" }\n```\n\n**Plan migration to Spring Framework core retry.**\n\n### Spring Authorization Server\n\nNow part of Spring Security - explicit version management removed.\n\n**libs.versions.toml (before - Spring Boot 3.x):**\n```toml\n[versions]\nspring-authorization-server = \"1.3.0\" # No longer works\n\n[libraries]\nspring-security-oauth2-authorization-server = { module = \"org.springframework.security:spring-security-oauth2-authorization-server\", version.ref = \"spring-authorization-server\" }\n```\n\n**Migration (Spring Boot 4.0):**\n```toml\n[versions]\nspring-security = \"7.0.0\" # Use Spring Security version instead\n\n[libraries]\n# Managed by spring-security.version property, not separate\nspring-security-oauth2-authorization-server = { module = \"org.springframework.security:spring-security-oauth2-authorization-server\", version.ref = \"spring-security\" }\n```\n\nOr rely on Spring Boot dependency management (recommended):\n```kotlin\ndependencies {\n    implementation(\"org.springframework.security:spring-security-oauth2-authorization-server\")\n    // Version managed by Spring Boot 4.0\n}\n```\n\n### Elasticsearch Client Changes\n\n#### Low-Level Client Replacement\n\n**Deprecated low-level `RestClient` → new `Rest5Client`:**\n\n**Note:** Higher-level clients (`ElasticsearchClient` and Spring Data's `ReactiveElasticsearchClient`) **remain unchanged** and have been updated internally to use the new low-level client.\n\n**Imports:**\n```kotlin\n// Old (Spring Boot 3.x)\nimport org.elasticsearch.client.RestClient\nimport org.elasticsearch.client.RestClientBuilder\nimport org.springframework.boot.autoconfigure.elasticsearch.RestClientBuilderCustomizer\n\n// New (Spring Boot 4.0)\nimport co.elastic.clients.transport.rest_client.Rest5Client\nimport co.elastic.clients.transport.rest_client.Rest5ClientBuilder\nimport org.springframework.boot.autoconfigure.elasticsearch.Rest5ClientBuilderCustomizer\n```\n\n**Configuration:**\n```kotlin\n@Configuration\nclass ElasticsearchConfig {\n\n    // Old\n    // @Bean\n    // fun restClientCustomizer(): RestClientBuilderCustomizer {\n    //     return RestClientBuilderCustomizer { builder -\u003e\n    //         builder.setRequestConfigCallback { config -\u003e\n    //             config.setConnectTimeout(5000)\n    //         }\n    //     }\n    // }\n\n    // New\n    @Bean\n    fun rest5ClientCustomizer(): Rest5ClientBuilderCustomizer {\n        return Rest5ClientBuilderCustomizer { builder -\u003e\n            builder.setRequestConfigCallback { config -\u003e\n                config.setConnectTimeout(5000)\n            }\n        }\n    }\n}\n```\n\n**Dependency Consolidation:**\n\nSniffer now included in `co.elastic.clients:elasticsearch-java` module.\n\n**libs.versions.toml:**\n```toml\n[libraries]\n# Remove these - no longer managed\n# elasticsearch-rest-client = { module = \"org.elasticsearch.client:elasticsearch-rest-client\", version = \"...\" }\n# elasticsearch-rest-client-sniffer = { module = \"org.elasticsearch.client:elasticsearch-rest-client-sniffer\", version = \"...\" }\n\n# Use single dependency (includes sniffer)\nelasticsearch-java = { module = \"co.elastic.clients:elasticsearch-java\", version = \"8.x.x\" }\n```\n\n### Hibernate Dependency Changes\n\n**libs.versions.toml:**\n```toml\n[libraries]\n# Renamed module (hibernate-jpamodelgen replaced by hibernate-processor)\nhibernate-processor = { module = \"org.hibernate.orm:hibernate-processor\", version.ref = \"hibernate\" }\n\n# These artifacts are NO LONGER PUBLISHED by Hibernate:\n# hibernate-proxool - discontinued by Hibernate project\n# hibernate-vibur - discontinued by Hibernate project\n# Remove any dependencies on these modules\n```\n\n**Note:** `hibernate-jpamodelgen` artifact still exists but is deprecated. Use `hibernate-processor` going forward.\n\n## Configuration Property Changes\n\n### MongoDB Property Restructuring\n\n**Major reorganization:** Non-Spring Data properties moved to `spring.mongodb.*`:\n\n**application.yml migration:**\n```yaml\n# Old (Spring Boot 3.x)\nspring:\n  data:\n    mongodb:\n      uri: mongodb://localhost:27017/mydb\n      database: mydb\n      host: localhost\n      port: 27017\n      username: user\n      password: pass\n      authentication-database: admin\n      replica-set-name: rs0\n      additional-hosts:\n        - host1:27017\n        - host2:27017\n      ssl:\n        enabled: true\n        bundle: my-bundle\n      representation:\n        uuid: STANDARD\n\nmanagement:\n  health:\n    mongo:\n      enabled: true\n  metrics:\n    mongo:\n      command:\n        enabled: true\n      connectionpool:\n        enabled: true\n\n# New (Spring Boot 4.0)\nspring:\n  mongodb:\n    uri: mongodb://localhost:27017/mydb\n    database: mydb\n    host: localhost\n    port: 27017\n    username: user\n    password: pass\n    authentication-database: admin\n    replica-set-name: rs0\n    additional-hosts:\n      - host1:27017\n      - host2:27017\n    ssl:\n      enabled: true\n      bundle: my-bundle\n    representation:\n      uuid: STANDARD # Explicit configuration now required\n\n  data:\n    mongodb:\n      # Spring Data-specific properties remain here\n      auto-index-creation: true\n      field-naming-strategy: org.springframework.data.mapping.model.SnakeCaseFieldNamingStrategy\n      gridfs:\n        bucket: fs\n        database: gridfs-db\n      repositories:\n        type: auto\n      representation:\n        big-decimal: DECIMAL128 # Explicit configuration now required\n\nmanagement:\n  health:\n    mongodb: # Renamed from \"mongo\"\n      enabled: true\n  metrics:\n    mongodb: # Renamed from \"mongo\"\n      command:\n        enabled: true\n      connectionpool:\n        enabled: true\n```\n\n**Key changes:**\n- **UUID representation**: **MANDATORY** - No default provided, must explicitly configure `spring.mongodb.representation.uuid` (e.g., `STANDARD`, `JAVA_LEGACY`, `PYTHON_LEGACY`, `C_SHARP_LEGACY`)\n- **BigDecimal representation**: **MANDATORY** - No default provided, must explicitly configure `spring.data.mongodb.representation.big-decimal` (e.g., `DECIMAL128`, `STRING`)\n- **Management properties**: `mongo` → `mongodb`\n- **Failure to configure these will result in runtime errors when persisting UUID or BigDecimal values**\n\n### Spring Session Property Renames\n\n**application.yml migration:**\n```yaml\n# Old (Spring Boot 3.x)\nspring:\n  session:\n    redis:\n      namespace: myapp:session\n      flush-mode: on-save\n    mongodb:\n      collection-name: sessions\n\n# New (Spring Boot 4.0)\nspring:\n  session:\n    data:\n      redis:\n        namespace: myapp:session\n        flush-mode: on-save\n      mongodb:\n        collection-name: sessions\n```\n\n### Persistence Module Property Change\n\n**application.yml migration:**\n```yaml\n# Old (Spring Boot 3.x)\nspring:\n  dao:\n    exceptiontranslation:\n      enabled: true\n\n# New (Spring Boot 4.0)\nspring:\n  persistence:\n    exceptiontranslation:\n      enabled: true\n```\n\n## Web Framework Changes\n\n### Static Resource Locations\n\n`PathRequest#toStaticResources()` now includes `/fonts/**` by default.\n\n**Security configuration (exclude fonts if needed):**\n```kotlin\nimport org.springframework.boot.autoconfigure.security.servlet.PathRequest\nimport org.springframework.boot.autoconfigure.security.StaticResourceLocation\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                // Exclude fonts if needed\n                authorize(PathRequest.toStaticResources()\n                    .atCommonLocations()\n                    .excluding(StaticResourceLocation.FONTS), permitAll)\n                authorize(anyRequest, authenticated)\n            }\n        }\n        return http.build()\n    }\n}\n```\n\n### HttpMessageConverters Deprecation\n\n`HttpMessageConverters` deprecated due to framework improvements (conflated client/server converters).\n\n**Migration:**\n```kotlin\n// Old (Spring Boot 3.x)\nimport org.springframework.boot.autoconfigure.http.HttpMessageConverters\nimport org.springframework.context.annotation.Bean\n\n@Configuration\nclass WebConfig {\n    @Bean\n    fun customConverters(): HttpMessageConverters {\n        return HttpMessageConverters(MyCustomConverter())\n    }\n}\n\n// New (Spring Boot 4.0)\nimport org.springframework.boot.autoconfigure.http.client.ClientHttpMessageConvertersCustomizer\nimport org.springframework.boot.autoconfigure.http.server.ServerHttpMessageConvertersCustomizer\n\n@Configuration\nclass WebConfig {\n\n    // Separate client and server converters\n    @Bean\n    fun clientConvertersCustomizer(): ClientHttpMessageConvertersCustomizer {\n        return ClientHttpMessageConvertersCustomizer { converters -\u003e\n            converters.add(MyCustomClientConverter())\n        }\n    }\n\n    @Bean\n    fun serverConvertersCustomizer(): ServerHttpMessageConvertersCustomizer {\n        return ServerHttpMessageConvertersCustomizer { converters -\u003e\n            converters.add(MyCustomServerConverter())\n        }\n    }\n}\n```\n\n### Jersey and Jackson 3 Incompatibility\n\n**Jersey 4.0 limitation:** Spring Boot 4.0 supports Jersey 4.0, which **does not yet support Jackson 3**.\n\n**Solution:** Use `spring-boot-jackson2` compatibility module **either in place of or alongside** `spring-boot-jackson`:\n\n**libs.versions.toml:**\n```toml\n[libraries]\nspring-boot-starter-jersey = { module = \"org.springframework.boot:spring-boot-starter-jersey\", version.ref = \"springBoot\" }\nspring-boot-jackson2 = { module = \"org.springframework.boot:spring-boot-jackson2\", version.ref = \"springBoot\" }\n# Optional: Keep Jackson 3 for non-Jersey parts of application\nspring-boot-jackson = { module = \"org.springframework.boot:spring-boot-jackson\", version.ref = \"springBoot\" }\n```\n\n**build.gradle.kts:**\n```kotlin\ndependencies {\n    implementation(libs.spring.boot.starter.jersey)\n    implementation(libs.spring.boot.jackson2) // Required for Jersey JSON processing\n    // Optional: Use Jackson 3 elsewhere in application\n    // implementation(libs.spring.boot.jackson)\n}\n```\n\n**Note:** If using only Jersey in your application, you can replace Jackson 3 entirely with Jackson 2 compatibility module.\n\n## Messaging Framework Changes\n\n### Kafka Streams Customizer Replacement\n\n**Deprecated `StreamBuilderFactoryBeanCustomizer` → `StreamsBuilderFactoryBeanConfigurer`:**\n\n```kotlin\n// Old (Spring Boot 3.x)\nimport org.springframework.boot.autoconfigure.kafka.StreamsBuilderFactoryBeanCustomizer\n\n@Configuration\nclass KafkaStreamsConfig {\n    @Bean\n    fun streamsCustomizer(): StreamBuilderFactoryBeanCustomizer {\n        return StreamBuilderFactoryBeanCustomizer { factoryBean -\u003e\n            factoryBean.setKafkaStreamsCustomizer { streams -\u003e\n                // Custom config\n            }\n        }\n    }\n}\n\n// New (Spring Boot 4.0)\nimport org.springframework.kafka.config.StreamsBuilderFactoryBeanConfigurer\n\n@Configuration\nclass KafkaStreamsConfig {\n    @Bean\n    fun streamsConfigurer(): StreamsBuilderFactoryBeanConfigurer {\n        return StreamsBuilderFactoryBeanConfigurer { factoryBean -\u003e\n            factoryBean.setKafkaStreamsCustomizer { streams -\u003e\n                // Custom config\n            }\n        }\n    }\n}\n```\n\n**Note:** New configurer implements `Ordered` with default value `0`.\n\n### Kafka Retry Property Change\n\n**application.yml migration:**\n```yaml\n# Old (Spring Boot 3.x)\nspring:\n  kafka:\n    retry:\n      topic:\n        backoff:\n          random: true\n\n# New (Spring Boot 4.0)\nspring:\n  kafka:\n    retry:\n      topic:\n        backoff:\n          jitter: 0.5 # More flexible than boolean\n```\n\n### RabbitMQ Retry Customizer Split\n\n**Spring AMQP moved from Spring Retry → Spring Framework core retry**, with customizer split:\n\n```kotlin\n// Old (Spring Boot 3.x)\nimport org.springframework.boot.autoconfigure.amqp.RabbitRetryTemplateCustomizer\n\n@Configuration\nclass RabbitConfig {\n    @Bean\n    fun retryCustomizer(): RabbitRetryTemplateCustomizer {\n        return RabbitRetryTemplateCustomizer { template -\u003e\n            // Applies to both RabbitTemplate and listeners\n        }\n    }\n}\n\n// New (Spring Boot 4.0)\nimport org.springframework.boot.autoconfigure.amqp.RabbitTemplateRetrySettingsCustomizer\nimport org.springframework.boot.autoconfigure.amqp.RabbitListenerRetrySettingsCustomizer\n\n@Configuration\nclass RabbitConfig {\n\n    // For RabbitTemplate operations\n    @Bean\n    fun templateRetryCustomizer(): RabbitTemplateRetrySettingsCustomizer {\n        return RabbitTemplateRetrySettingsCustomizer { settings -\u003e\n            settings.maxAttempts = 5\n        }\n    }\n\n    // For message listeners\n    @Bean\n    fun listenerRetryCustomizer(): RabbitListenerRetrySettingsCustomizer {\n        return RabbitListenerRetrySettingsCustomizer { settings -\u003e\n            settings.maxAttempts = 3\n        }\n    }\n}\n```\n\n## Testing Framework Changes\n\n### Mockito Integration Removed\n\n`MockitoTestExecutionListener` removed (deprecated in 3.4).\n\n**Migration to MockitoExtension:**\n```kotlin\n// Old (Spring Boot 3.x)\nimport org.springframework.boot.test.context.SpringBootTest\nimport org.mockito.Mock\nimport org.mockito.Captor\n\n@SpringBootTest\nclass MyServiceTest {\n    @Mock\n    private lateinit var repository: MyRepository\n\n    @Captor\n    private lateinit var captor: ArgumentCaptor\u003cString\u003e\n}\n\n// New (Spring Boot 4.0)\nimport org.springframework.boot.test.context.SpringBootTest\nimport org.mockito.Mock\nimport org.mockito.Captor\nimport org.mockito.junit.jupiter.MockitoExtension\nimport org.junit.jupiter.api.extension.ExtendWith\n\n@SpringBootTest\n@ExtendWith(MockitoExtension::class) // Explicit extension required\nclass MyServiceTest {\n    @Mock\n    private lateinit var repository: MyRepository\n\n    @Captor\n    private lateinit var captor: ArgumentCaptor\u003cString\u003e\n}\n```\n\n### @SpringBootTest Changes\n\n`@SpringBootTest` no longer provides **MockMVC**, **WebTestClient**, or **TestRestTemplate** automatically.\n\n#### MockMVC Configuration\n\n```kotlin\n// Old (Spring Boot 3.x)\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\nclass ControllerTest {\n    @Autowired\n    private lateinit var mockMvc: MockMvc // Available automatically\n}\n\n// New (Spring Boot 4.0)\nimport org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc\nimport org.springframework.boot.test.autoconfigure.web.servlet.HtmlUnit\n\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@AutoConfigureMockMvc // Explicit annotation required\nclass ControllerTest {\n    @Autowired\n    private lateinit var mockMvc: MockMvc\n}\n\n// HtmlUnit configuration moved to annotation attribute\n@AutoConfigureMockMvc(\n    htmlUnit = HtmlUnit(webClient = false, webDriver = false)\n)\n```\n\n#### WebTestClient Configuration\n\n```kotlin\n// Old (Spring Boot 3.x)\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\nclass WebFluxTest {\n    @Autowired\n    private lateinit var webTestClient: WebTestClient // Available automatically\n}\n\n// New (Spring Boot 4.0)\nimport org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient\n\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@AutoConfigureWebTestClient // Explicit annotation required\nclass WebFluxTest {\n    @Autowired\n    private lateinit var webTestClient: WebTestClient\n}\n```\n\n#### TestRestTemplate → RestTestClient (Recommended)\n\n**Spring Boot 4.0 introduces `RestTestClient`** as modern replacement for `TestRestTemplate`.\n\n```kotlin\n// Old approach (still works with annotation)\nimport org.springframework.boot.test.autoconfigure.web.client.AutoConfigureTestRestTemplate\nimport org.springframework.boot.test.web.client.TestRestTemplate\n\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@AutoConfigureTestRestTemplate // Required in 4.0\nclass RestApiTest {\n    @Autowired\n    private lateinit var testRestTemplate: TestRestTemplate\n}\n\n// New recommended approach\nimport org.springframework.boot.test.autoconfigure.web.client.AutoConfigureRestTestClient\nimport org.springframework.boot.resttestclient.RestTestClient\n\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@AutoConfigureRestTestClient // New annotation\nclass RestApiTest {\n    @Autowired\n    private lateinit var restTestClient: RestTestClient\n\n    @Test\n    fun testEndpoint() {\n        val response = restTestClient.get()\n            .uri(\"/api/users\")\n            .retrieve()\n            .toEntity\u003cList\u003cUser\u003e\u003e()\n\n        assertThat(response.statusCode).isEqualTo(HttpStatus.OK)\n    }\n}\n```\n\n**TestRestTemplate package change (if still using):**\n\n**IMPORTANT:** If continuing to use `TestRestTemplate`, you must:\n1. Add the `spring-boot-resttestclient` test dependency\n2. **Update the package import** (class moved to new package)\n\n**libs.versions.toml:**\n```toml\n[libraries]\nspring-boot-resttestclient = { module = \"org.springframework.boot:spring-boot-resttestclient\", version.ref = \"springBoot\" }\n```\n\n**build.gradle.kts:**\n```kotlin\ndependencies {\n    testImplementation(libs.spring.boot.resttestclient)\n}\n```\n\n**Update package import (required):**\n```kotlin\n// Old package import - will cause compilation failure\n// import org.springframework.boot.test.web.client.TestRestTemplate\n\n// New package import - required in Spring Boot 4.0\nimport org.springframework.boot.resttestclient.TestRestTemplate\n```\n\n### @PropertyMapping Annotation Relocation\n\n```kotlin\n// Old (Spring Boot 3.x)\nimport org.springframework.boot.test.autoconfigure.properties.PropertyMapping\nimport org.springframework.boot.test.autoconfigure.properties.Skip\n\n// New (Spring Boot 4.0)\nimport org.springframework.boot.test.context.PropertyMapping\nimport org.springframework.boot.test.context.PropertyMapping.Skip\n```\n\n## Production-Ready Features and Modules\n\n### Health, Metrics, and Observability Modules\n\nSpring Boot 4.0 modularizes production-ready features into focused modules:\n\n**libs.versions.toml:**\n```toml\n[libraries]\n# Health monitoring\nspring-boot-health = { module = \"org.springframework.boot:spring-boot-health\", version.ref = \"springBoot\" }\n\n# Micrometer metrics\nspring-boot-micrometer-metrics = { module = \"org.springframework.boot:spring-boot-micrometer-metrics\", version.ref = \"springBoot\" }\nspring-boot-micrometer-metrics-test = { module = \"org.springframework.boot:spring-boot-micrometer-metrics-test\", version.ref = \"springBoot\" }\n\n# Micrometer observation\nspring-boot-micrometer-observation = { module = \"org.springframework.boot:spring-boot-micrometer-observation\", version.ref = \"springBoot\" }\n\n# Distributed tracing\nspring-boot-micrometer-tracing = { module = \"org.springframework.boot:spring-boot-micrometer-tracing\", version.ref = \"springBoot\" }\nspring-boot-micrometer-tracing-test = { module = \"org.springframework.boot:spring-boot-micrometer-tracing-test\", version.ref = \"springBoot\" }\nspring-boot-micrometer-tracing-brave = { module = \"org.springframework.boot:spring-boot-micrometer-tracing-brave\", version.ref = \"springBoot\" }\nspring-boot-micrometer-tracing-opentelemetry = { module = \"org.springframework.boot:spring-boot-micrometer-tracing-opentelemetry\", version.ref = \"springBoot\" }\n\n# OpenTelemetry integration\nspring-boot-opentelemetry = { module = \"org.springframework.boot:spring-boot-opentelemetry\", version.ref = \"springBoot\" }\n\n# Zipkin reporter\nspring-boot-zipkin = { module = \"org.springframework.boot:spring-boot-zipkin\", version.ref = \"springBoot\" }\n```\n\n**build.gradle.kts (example observability stack):**\n```kotlin\ndependencies {\n    // Actuator with metrics and tracing\n    implementation(libs.spring.boot.starter.actuator)\n    implementation(libs.spring.boot.micrometer.observation)\n    implementation(libs.spring.boot.micrometer.tracing.opentelemetry)\n    implementation(libs.spring.boot.opentelemetry)\n\n    // Test support\n    testImplementation(libs.spring.boot.micrometer.metrics.test)\n    testImplementation(libs.spring.boot.micrometer.tracing.test)\n}\n```\n\n**Note:** Most applications using starters (e.g., `spring-boot-starter-actuator`) won't need to declare these modules directly. Use direct module dependencies for fine-grained control.\n\n## Actuator Changes\n\n### Health Probes Enabled by Default\n\nLiveness and readiness probes now **enabled by default**.\n\n**application.yml (disable if needed):**\n```yaml\nmanagement:\n  endpoint:\n    health:\n      probes:\n        enabled: false # Disable if not using Kubernetes probes\n```\n\n**Automatically exposes:**\n- `/actuator/health/liveness`\n- `/actuator/health/readiness`\n\n## Build Configuration\n\n### Kotlin Compiler Configuration\n\n**build.gradle.kts:**\n```kotlin\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\nplugins {\n    kotlin(\"jvm\") version \"2.2.0\" // Minimum 2.2.0\n    kotlin(\"plugin.spring\") version \"2.2.0\"\n    kotlin(\"plugin.jpa\") version \"2.2.0\"\n    id(\"org.springframework.boot\") version \"4.0.0\"\n    id(\"io.spring.dependency-management\") version \"1.1.7\"\n}\n\njava {\n    toolchain {\n        languageVersion = JavaLanguageVersion.of(21) // Or 17, 25\n    }\n}\n\nkotlin {\n    compilerOptions {\n        freeCompilerArgs.addAll(\n            \"-Xjsr305=strict\", // Strict null-safety\n            \"-Xemit-jvm-type-annotations\" // Emit type annotations\n        )\n    }\n}\n\ntasks.withType\u003cKotlinCompile\u003e {\n    kotlinOptions {\n        jvmTarget = \"21\" // Match Java toolchain\n    }\n}\n\ntasks.withType\u003cTest\u003e {\n    useJUnitPlatform()\n}\n```\n\n### Java Preview Features (if using Java 25)\n\n**build.gradle.kts:**\n```kotlin\ntasks.withType\u003cJavaCompile\u003e {\n    options.compilerArgs.add(\"--enable-preview\")\n}\n\ntasks.withType\u003cTest\u003e {\n    jvmArgs(\"--enable-preview\")\n}\n\ntasks.withType\u003cJavaExec\u003e {\n    jvmArgs(\"--enable-preview\")\n}\n```\n\n## Migration Checklist\n\n### Pre-Migration\n\n- [ ] Upgrade to latest Spring Boot 3.5.x\n- [ ] Review and fix all deprecation warnings\n- [ ] Document current dependency versions\n- [ ] Run full test suite and verify green build\n- [ ] Review [Spring Boot 3.5.x → 4.0 dependency changes](https://docs.spring.io/spring-boot/4.0/appendix/dependency-versions/coordinates.html)\n\n### Core Migration\n\n- [ ] Update `libs.versions.toml` with Spring Boot 4.0.0\n- [ ] Update Kotlin version to 2.2.0+\n- [ ] Rename starters: `spring-boot-starter-web` → `spring-boot-starter-webmvc`, etc.\n- [ ] Add technology-specific test starters (or use classic starters temporarily)\n- [ ] Remove Undertow dependency if present (switch to Tomcat/Jetty)\n- [ ] Remove `spring-session-hazelcast` / `spring-session-mongodb` or add explicit versions\n\n### Jackson 3 Migration\n\n- [ ] Update imports: `com.fasterxml.jackson` → `tools.jackson`\n- [ ] Update exception: `jackson-annotations` still uses `com.fasterxml.jackson.core`\n- [ ] Rename: `@JsonComponent` → `@JacksonComponent`\n- [ ] Rename: `Jackson2ObjectMapperBuilderCustomizer` → `JsonMapperBuilderCustomizer`\n- [ ] Update properties: `spring.jackson.read.*` → `spring.jackson.json.read.*`\n- [ ] Consider temporary `spring-boot-jackson2` module if needed\n\n### Property Updates\n\n- [ ] MongoDB: `spring.data.mongodb.*` → `spring.mongodb.*` (for non-Spring Data properties)\n- [ ] Session: `spring.session.redis.*` → `spring.session.data.redis.*`\n- [ ] Persistence: `spring.dao.exceptiontranslation` → `spring.persistence.exceptiontranslation`\n- [ ] Kafka retry: `backoff.random` → `backoff.jitter`\n\n### Code Updates\n\n- [ ] Update package: `BootstrapRegistry` → `org.springframework.boot.bootstrap.BootstrapRegistry`\n- [ ] Update package: `EnvironmentPostProcessor` → `org.springframework.boot.EnvironmentPostProcessor`\n- [ ] Update package: `EntityScan` → `org.springframework.boot.persistence.autoconfigure.EntityScan`\n- [ ] Update: `RestClient` → `Rest5Client` (Elasticsearch)\n- [ ] Update: `StreamBuilderFactoryBeanCustomizer` → `StreamsBuilderFactoryBeanConfigurer` (Kafka)\n- [ ] Split: `RabbitRetryTemplateCustomizer` → `RabbitTemplateRetrySettingsCustomizer` / `RabbitListenerRetrySettingsCustomizer`\n- [ ] Replace: `HttpMessageConverters` → `ClientHttpMessageConvertersCustomizer` / `ServerHttpMessageConvertersCustomizer`\n- [ ] Update: `PropertyMapper` usage with `.always()` if null handling needed\n\n### Testing Updates\n\n- [ ] Add `@ExtendWith(MockitoExtension::class)` to tests using `@Mock` / `@Captor`\n- [ ] Add `@AutoConfigureMockMvc` to tests using `MockMvc`\n- [ ] Add `@AutoConfigureWebTestClient` to tests using `WebTestClient`\n- [ ] Migrate `TestRestTemplate` → `RestTestClient` (or add `@AutoConfigureTestRestTemplate`)\n- [ ] Update: `@PropertyMapping` imports → `org.springframework.boot.test.context`\n\n### Build Configuration\n\n- [ ] Update Gradle to 8.5+\n- [ ] Update Gradle CycloneDX plugin to 3.0.0+\n- [ ] Review optional dependency inclusion in uber jars\n- [ ] Remove `loaderImplementation = CLASSIC` if present\n- [ ] Remove `launchScript()` configuration if present\n\n### Verification\n\n- [ ] Run `./gradlew clean build`\n- [ ] Run full test suite\n- [ ] Verify integration tests with TestContainers\n- [ ] Check for new Kotlin null-safety warnings\n- [ ] Test Spring Boot Actuator endpoints\n- [ ] Verify health probes (`/actuator/health/liveness`, `/actuator/health/readiness`)\n- [ ] Performance test with new defaults\n\n### Post-Migration\n\n- [ ] Review Spring Boot 4.0 release notes for additional features\n- [ ] Consider adopting new Spring Framework 7.0 features\n- [ ] Plan migration away from classic starters (if used)\n- [ ] Plan migration away from `spring-boot-jackson2` module (if used)\n- [ ] Update CI/CD pipelines for Java 17+ requirement\n- [ ] Update deployment manifests (Servlet 6.1 containers)\n\n## Common Pitfalls\n\n1. **Classic starters**: Remember these are deprecated - plan migration to technology-specific starters\n2. **Undertow**: Completely removed, no workaround - must use Tomcat or Jetty\n3. **Jackson 3 packages**: Easy to miss `jackson-annotations` still using old group ID\n4. **MongoDB properties**: Many moved to `spring.mongodb.*` but some remain in `spring.data.mongodb.*`\n5. **Test configuration**: `@SpringBootTest` no longer auto-configures MockMVC/WebTestClient/TestRestTemplate\n6. **Kotlin 2.2**: Required minimum - older versions won't work\n7. **Null-safety**: JSpecify annotations may surface new warnings in Kotlin\n8. **PropertyMapper**: Behavioral change with null handling - review usage\n9. **Jersey + Jackson 3**: Incompatible - use `spring-boot-jackson2` module\n10. **Health probes**: Now enabled by default - may affect non-Kubernetes deployments\n\n## Performance Considerations\n\n- **Modular starters**: Smaller JARs and faster startup with technology-specific starters\n- **Spring Framework 7**: Performance improvements in core framework\n- **Jackson 3**: Improved JSON processing performance\n- **Virtual threads**: Consider enabling with Java 21+ (`spring.threads.virtual.enabled=true`)\n\n## Resources\n\n- [Spring Boot 4.0 Migration Guide](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide)\n- [Spring Boot 4.0 Release Notes](https://github.com/spring-projects/spring-boot/releases)\n- [Spring Framework 7.0 Documentation](https://docs.spring.io/spring-framework/reference/)\n- [Jackson 3 Migration Guide](https://github.com/FasterXML/jackson/blob/main/jackson3/MIGRATING_TO_JACKSON_3.md)\n- [Kotlin 2.2 Release Notes](https://kotlinlang.org/docs/whatsnew22.html)\n\n---\n","description":"Comprehensive guide for migrating Spring Boot applications from 3.x to 4.0, focusing on Gradle Kotlin DSL and version catalogs","import":{"commit_sha":"541b7819d8c3545c6df122491af4fa1eae415779","imported_at":"2026-05-18T20:05:35Z","license_text":"MIT License\n\nCopyright GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.","owner":"github","repo":"github/awesome-copilot","source_url":"https://github.com/github/awesome-copilot/blob/541b7819d8c3545c6df122491af4fa1eae415779/instructions/springboot-4-migration.instructions.md"},"manifest":{}},"content_hash":[102,26,51,167,111,227,216,212,0,142,227,6,97,162,199,87,114,64,155,180,238,15,20,31,186,8,40,33,194,192,211,248],"trust_level":"unsigned","yanked":false}
