Source code of my website
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

✨ : update spring boot 4 migration

+360
content/posts/2025-12-05-openrewrite-spring-boot-4/cover.png content/posts/2025-12-05-openrewrite-spring-boot-4/v1/cover.png
+4
content/posts/2025-12-05-openrewrite-spring-boot-4/index.md content/posts/2025-12-05-openrewrite-spring-boot-4/v1/index.md
··· 1 1 --- 2 2 date: 2025-12-12 3 + lastmod: 2025-12-19 3 4 language: fr 4 5 title: Montée de version Spring Boot 4 avec OpenRewrite 5 6 tags: 6 7 - java 7 8 - spring-boot 9 + url: '/:year/:month/:day/:slug/v1' 10 + params: 11 + updated: "v2.md" 8 12 --- 9 13 10 14 Un des projets que je maintiens activement est [GitLab Classrooms](projects/gitlab-classrooms).
content/posts/2025-12-05-openrewrite-spring-boot-4/tests.png content/posts/2025-12-05-openrewrite-spring-boot-4/v1/tests.png
content/posts/2025-12-05-openrewrite-spring-boot-4/v2/cover.png

This is a binary file and will not be displayed.

+356
content/posts/2025-12-05-openrewrite-spring-boot-4/v2/index.md
··· 1 + --- 2 + date: 2025-12-12 3 + lastmod: 2025-12-19 4 + language: fr 5 + title: Montée de version Spring Boot 4 avec OpenRewrite 6 + tags: 7 + - java 8 + - spring-boot 9 + params: 10 + original: "v1.md" 11 + --- 12 + 13 + Un des projets que je maintiens activement est [GitLab Classrooms](projects/gitlab-classrooms). 14 + 15 + Le code de ce projet est écrit en Spring Boot 3 et Java 25. 16 + Avec la sortie récente de Spring Boot 4, je voulais faire la montée de version de ce projet rapidement. 17 + 18 + Pour ça, j'ai donc deux possibilités : soit je fais la montée de version à la main, soit j'utilise un outil pour le faire automatiquement. 19 + 20 + J'en ai donc profité pour tester OpenRewrite. 21 + 22 + <!--more--> 23 + 24 + ## OpenRewrite 25 + 26 + [OpenRewrite](https://docs.openrewrite.org/) est un outil qui permet d'exécuter des opérations de refactoring sur du code Java. 27 + Il s'appuie sur un concept de recette (le terme utilisé est _recipes_), qui implémente des transformations sur le code. 28 + 29 + J'avais découvert cet outil lors du [talk de Jérôme Tama à Devoxx France 2025](https://www.youtube.com/watch?v=aYHb7sLhsoQ). 30 + 31 + ## La recette Spring Boot 4 32 + 33 + Une [recette de migration Spring Boot 4](https://docs.openrewrite.org/recipes/java/spring/boot4/upgradespringboot_4_0-community-edition) est disponible pour la version communautaire. 34 + 35 + En parcourant le code de la recette sur [Github](https://github.com/openrewrite/rewrite-spring/blob/main/src/main/resources/META-INF/rewrite/spring-boot-40.yml), il semble que la recette fait une grande partie de ce qui est indiqué dans le guide de migration de Spring : 36 + 37 + * la montée de version du pom `spring-boot-starter-parent` 38 + * les modifications liées aux changements de coordonnées de certains artifacts maven 39 + * la migration vers Spring Framework et Spring Security 7 40 + * la mise à jour des properties dépréciées 41 + * la mise à jour vers testcontainers 2 42 + * la migration vers les starters modulaires 43 + 44 + > [!INFO] 45 + > La recette évolue régulièrement, donc peut-être qu'elle fait encore plus de choses au moment où vous lisez cet article. 46 + 47 + La recette prend la forme d'un fichier YAML, et elle est accompagnée de code qui implémente les différentes transformations : 48 + 49 + > Je ne rentre pas dans les détails du fonctionnement d'OpenRewrite, allez voir le talk de Jérôme Tama indiqué plus haut pour plus d'informations. 50 + 51 + ```yaml 52 + type: specs.openrewrite.org/v1beta/recipe 53 + name: org.openrewrite.java.spring.boot4.UpgradeSpringBoot_4_0 54 + displayName: Migrate to Spring Boot 4.0 55 + description: >- 56 + Migrate applications to the latest Spring Boot 4.0 release. This recipe will modify an application's build files, 57 + make changes to deprecated/preferred APIs. 58 + tags: 59 + - spring 60 + - boot 61 + recipeList: 62 + - org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5 63 + - org.openrewrite.java.spring.framework.UpgradeSpringFramework_7_0 64 + - org.openrewrite.java.spring.security7.UpgradeSpringSecurity_7_0 65 + - org.openrewrite.java.spring.batch.SpringBatch5To6Migration 66 + - org.openrewrite.java.spring.boot4.SpringBootProperties_4_0 67 + - org.openrewrite.java.spring.boot4.ReplaceMockBeanAndSpyBean 68 + - org.openrewrite.hibernate.MigrateToHibernate71 69 + - org.openrewrite.java.testing.testcontainers.Testcontainers2Migration 70 + - org.openrewrite.java.spring.boot4.MigrateToModularStarters 71 + - org.openrewrite.java.dependencies.UpgradeDependencyVersion: 72 + groupId: org.springframework.boot 73 + artifactId: "*" 74 + newVersion: 4.0.x 75 + overrideManagedVersion: false 76 + - org.openrewrite.java.dependencies.UpgradeDependencyVersion: 77 + groupId: org.springframework.boot 78 + artifactId: spring-boot-dependencies 79 + newVersion: 4.0.x 80 + overrideManagedVersion: true 81 + - org.openrewrite.maven.UpgradePluginVersion: 82 + groupId: org.springframework.boot 83 + artifactId: spring-boot-maven-plugin 84 + newVersion: 4.0.x 85 + - org.openrewrite.maven.UpgradeParentVersion: 86 + groupId: org.springframework.boot 87 + artifactId: spring-boot-starter-parent 88 + newVersion: 4.0.x 89 + - org.openrewrite.gradle.plugins.UpgradePluginVersion: 90 + pluginIdPattern: org.springframework.boot 91 + newVersion: 4.0.x 92 + 93 + # Replace deprecated starters with their new names 94 + # https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide#deprecated-starters 95 + - org.openrewrite.java.dependencies.ChangeDependency: 96 + oldGroupId: org.springframework.boot 97 + oldArtifactId: spring-boot-starter-oauth2-authorization-server 98 + newArtifactId: spring-boot-starter-security-oauth2-authorization-server 99 + - org.openrewrite.java.dependencies.ChangeDependency: 100 + oldGroupId: org.springframework.boot 101 + oldArtifactId: spring-boot-starter-oauth2-client 102 + newArtifactId: spring-boot-starter-security-oauth2-client 103 + - org.openrewrite.java.dependencies.ChangeDependency: 104 + oldGroupId: org.springframework.boot 105 + oldArtifactId: spring-boot-starter-oauth2-resource-server 106 + newArtifactId: spring-boot-starter-security-oauth2-resource-server 107 + - org.openrewrite.java.dependencies.ChangeDependency: 108 + oldGroupId: org.springframework.boot 109 + oldArtifactId: spring-boot-starter-web 110 + newArtifactId: spring-boot-starter-webmvc 111 + - org.openrewrite.java.dependencies.ChangeDependency: 112 + oldGroupId: org.springframework.boot 113 + oldArtifactId: spring-boot-starter-web-services 114 + newArtifactId: spring-boot-starter-webservices 115 + # https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide#aop-starter-pom 116 + - org.openrewrite.java.dependencies.RemoveDependency: 117 + groupId: org.springframework.boot 118 + artifactId: spring-boot-starter-aop 119 + unlessUsing: org.aspectj.lang.annotation.* 120 + - org.openrewrite.java.dependencies.ChangeDependency: 121 + oldGroupId: org.springframework.boot 122 + oldArtifactId: spring-boot-starter-aop 123 + newArtifactId: spring-boot-starter-aspectj 124 + ``` 125 + 126 + La doc d'OpenRewrite indique qu'on peut utiliser une simple commande _Maven_ pour effectuer la migration : 127 + 128 + ```shell 129 + mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \ 130 + -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:RELEASE \ 131 + -Drewrite.activeRecipes=org.openrewrite.java.spring.boot4.UpgradeSpringBoot_4_0 \ 132 + -Drewrite.exportDatatables=true 133 + ``` 134 + 135 + > Plutôt pratique, car je n'aurai pas à modifier mon `pom.xml`, ni ajouter de fichier de configuration dans mon projet pour pouvoir effectuer cette migration en one-shot. 136 + 137 + L'exécution de la commande prend quelques secondes et affiche les opérations effectuées (j'ai beaucoup nettoyé les logs pour que ce soit plus lisible) : 138 + 139 + ```shell 140 + [INFO] --- rewrite:6.25.0:run (default-cli) @ gitlab-classrooms --- 141 + [INFO] Using active recipe(s) [org.openrewrite.java.spring.boot4.UpgradeSpringBoot_4_0] 142 + [INFO] Using active styles(s) [] 143 + [INFO] Validating active recipes... 144 + [INFO] Project [gitlab-classrooms] Resolving Poms... 145 + [INFO] Project [gitlab-classrooms] Parsing source files 146 + [INFO] Running recipe(s)... 147 + [INFO] Printing available datatables to: target/rewrite/datatables/2025-12-12_17-33-51-247 148 + 149 + [WARNING] Changes have been made to pom.xml by: 150 + [WARNING] org.openrewrite.java.spring.boot4.MigrateToModularStarters 151 + [WARNING] org.openrewrite.maven.UpgradeParentVersion: {groupId=org.springframework.boot, artifactId=spring-boot-starter-parent, newVersion=4.0.x} 152 + [WARNING] org.openrewrite.java.testing.testcontainers.Testcontainers2Migration 153 + 154 + [WARNING] Changes have been made to src/main/resources/application-local.properties by: 155 + [WARNING] org.openrewrite.text.FindAndReplace: {find=javax., replace=jakarta., filePattern=**/*.js;**/*.ts;**/*.properties} 156 + 157 + [WARNING] Changes have been made to src/test/java/fr/univ_lille/gitlab/classrooms/adapters/jpa/PostgresqlJPAAdaptersTest.java by: 158 + [WARNING] org.openrewrite.java.testing.testcontainers.Testcontainers2Migration 159 + [WARNING] org.openrewrite.java.spring.boot4.MigrateToModularStarters 160 + 161 + [WARNING] Changes have been made to src/test/java/fr/univ_lille/gitlab/classrooms/mvc/ExportControllerMVCTest.java by: 162 + [WARNING] org.openrewrite.java.spring.boot4.ReplaceMockBeanAndSpyBean 163 + [WARNING] org.openrewrite.java.ChangeType: {oldFullyQualifiedTypeName=org.springframework.boot.test.mock.mockito.MockBean, newFullyQualifiedTypeName=org.springframework.test.context.bean.override.mockito.MockitoBean} 164 + 165 + [...] 166 + 167 + [WARNING] Please review and commit the results. 168 + [WARNING] Estimate time saved: 1h 16m 169 + [INFO] ------------------------------------------------------------------------ 170 + [INFO] BUILD SUCCESS 171 + [INFO] ------------------------------------------------------------------------ 172 + [INFO] Total time: 20.769 s 173 + [INFO] Finished at: 2025-12-12T17:33:51+01:00 174 + [INFO] ------------------------------------------------------------------------ 175 + ``` 176 + 177 + OpenRewrite semble s'être correctement exécuté et indique que plusieurs fichiers ont été modifiés. Un `git status` permet de se rendre compte de ce qui a été impacté : 178 + 179 + ```shell 180 + git status 181 + On branch feature/migration-spring-boot-4 182 + Changes not staged for commit: 183 + (use "git add <file>..." to update what will be committed) 184 + (use "git restore <file>..." to discard changes in working directory) 185 + modified: pom.xml 186 + modified: src/main/resources/application-local.properties 187 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/adapters/jpa/PostgresqlJPAAdaptersTest.java 188 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/api/UploadJunitGradingRestControllerMVCTest.java 189 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/assignments/AssignmentScoreServiceImplTest.java 190 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/assignments/AssignmentServiceImplTest.java 191 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/domain/classrooms/ClassroomStudentControllerTest.java 192 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/mvc/ClassroomControllerMVCTest.java 193 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/mvc/ExportControllerMVCTest.java 194 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/mvc/assignments/AssignmentMVCControllerMVCTest.java 195 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/mvc/assignments/StudentAssignmentResetGradeMVCControllerMVCTest.java 196 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/mvc/dashboard/DashboardControllerMVCTest.java 197 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/quiz/QuizAnswerControllerMVCTest.java 198 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/quiz/QuizEditionControllerMVCTest.java 199 + modified: src/test/java/fr/univ_lille/gitlab/classrooms/quiz/QuizServiceImplTest.java 200 + 201 + no changes added to commit (use "git add" and/or "git commit -a") 202 + ``` 203 + 204 + * le `pom.xml` (ce qui était attendu en premier lieu) 205 + * les fichiers de configuration properties (certaines properties ont été renommées) 206 + * les fichiers de test (surtout pour la dépréciation de `@MockBean` et `@SpyBean`) 207 + 208 + Un `git diff` permet de vérifier tout ça : 209 + 210 + ```shell 211 + git diff pom.xml 212 + 213 + @@ -6,7 +6,7 @@ 214 + <parent> 215 + <groupId>org.springframework.boot</groupId> 216 + <artifactId>spring-boot-starter-parent</artifactId> 217 + - <version>3.5.6</version> 218 + + <version>4.0.0</version> 219 + <relativePath/> <!-- lookup parent from repository --> 220 + </parent> 221 + 222 + @@ -44,7 +44,7 @@ 223 + - <jacoco-maven-plugin.version>0.8.13</jacoco-maven-plugin.version> 224 + + <jacoco-maven-plugin.version>0.8.14</jacoco-maven-plugin.version> 225 + 226 + @@ -57,7 +57,7 @@ 227 + <dependency> 228 + <groupId>org.springframework.boot</groupId> 229 + - <artifactId>spring-boot-starter-web</artifactId> 230 + + <artifactId>spring-boot-starter-webmvc</artifactId> 231 + </dependency> 232 + 233 + @@ -67,12 +67,12 @@ 234 + <dependency> 235 + <groupId>org.springframework.boot</groupId> 236 + - <artifactId>spring-boot-starter-oauth2-client</artifactId> 237 + + <artifactId>spring-boot-starter-security-oauth2-client</artifactId> 238 + </dependency> 239 + 240 + <dependency> 241 + <groupId>org.springframework.boot</groupId> 242 + - <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> 243 + + <artifactId>spring-boot-starter-security-oauth2-resource-server</artifactId> 244 + </dependency> 245 + 246 + @@ -91,8 +91,8 @@ 247 + <dependency> 248 + - <groupId>org.flywaydb</groupId> 249 + - <artifactId>flyway-core</artifactId> 250 + + <groupId>org.springframework.boot</groupId> 251 + + <artifactId>spring-boot-starter-flyway</artifactId> 252 + </dependency> 253 + 254 + @@ -127,22 +127,32 @@ 255 + + <dependency> 256 + + <groupId>org.springframework.boot</groupId> 257 + + <artifactId>spring-boot-starter-webmvc-test</artifactId> 258 + + <scope>test</scope> 259 + + </dependency> 260 + + 261 + + <dependency> 262 + + <groupId>org.springframework.boot</groupId> 263 + + <artifactId>spring-boot-starter-data-jpa-test</artifactId> 264 + + <scope>test</scope> 265 + + </dependency> 266 + 267 + @@ -136,7 +136,7 @@ 268 + 269 + <dependency> 270 + <groupId>org.testcontainers</groupId> 271 + - <artifactId>postgresql</artifactId> 272 + + <artifactId>testcontainers-postgresql</artifactId> 273 + <scope>test</scope> 274 + </dependency> 275 + 276 + <dependency> 277 + - <groupId>org.springframework.security</groupId> 278 + - <artifactId>spring-security-test</artifactId> 279 + + <groupId>org.springframework.boot</groupId> 280 + + <artifactId>spring-boot-starter-security-test</artifactId> 281 + <scope>test</scope> 282 + </dependency> 283 + ``` 284 + 285 + Au niveau du `pom.xml`, tout s'est bien passé, l'ensemble des modifications attendues ont bien été appliquées. 286 + 287 + La nouvelle architecture modulaire de Spring Boot 4 a bien été traitée. 288 + 289 + Le code de tests a aussi bien été nettoyé des anciens `@MockBean` dépréciés : 290 + 291 + ```text 292 + @@ -37,10 +37,10 @@ class ClassroomControllerMVCTest { 293 + @Autowired 294 + private MockMvc mockMvc; 295 + 296 + - @MockBean 297 + + @MockitoBean 298 + private ClassroomService classroomService; 299 + 300 + - @MockBean 301 + + @MockitoBean 302 + private Gitlab gitlab; 303 + ``` 304 + 305 + et certaines properties (qui étaient en commentaires) ont bien été renommées. 306 + 307 + ```text 308 + # generate full creation sql script if needed 309 + -spring.jpa.properties.javax.persistence.schema-generation.create-source=metadata 310 + -spring.jpa.properties.javax.persistence.schema-generation.scripts.action=update 311 + -spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=update.sql 312 + +spring.jpa.properties.jakarta.persistence.schema-generation.create-source=metadata 313 + +spring.jpa.properties.jakarta.persistence.schema-generation.scripts.action=update 314 + +spring.jpa.properties.jakarta.persistence.schema-generation.scripts.create-target=update.sql 315 + ``` 316 + 317 + ## Les ajustements que j'ai dû faire à la main 318 + 319 + En sortie de cette exécution, mon code ne compile pas. 320 + 321 + Un import dans ma configuration Spring Security n'est pas résolu 322 + 323 + ```java 324 + import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; 325 + ``` 326 + 327 + car cette classe a été déplacée dans un autre package : 328 + 329 + ```java 330 + import org.springframework.boot.security.autoconfigure.actuate.web.servlet.EndpointRequest; 331 + ``` 332 + 333 + Une fois ces petits ajustements faits, je lance mes tests unitaires. 334 + 335 + Cette fois-ci, j'obtiens un message d'erreur lié à Spring Security au démarrage : 336 + 337 + ```text 338 + Caused by: java.lang.IllegalArgumentException: pattern must start with a / 339 + ``` 340 + 341 + Je n'avais pas fait attention à cette modification dans les guides de migration, donc je suis peut-être passé à travers. Quoi qu'il en soit, ce n'est pas une modification très compliquée, je l'ai facilement appliquée. 342 + 343 + Une fois ces derniers ajustements faits, les tests passent correctement 🎉 : 344 + 345 + ![Screenshot de mes tests unitaires qui passent !](tests.png) 346 + 347 + ## Conclusion 348 + 349 + Cela m'a pris environ 1h pour migrer mon projet de Spring Boot 3.5 vers Spring Boot 4.0. 350 + 351 + OpenRewrite m'a clairement facilité le travail, il a modifié toutes mes dépendances, et migré les annotations dépréciées (ce qui aurait été fastidieux). 352 + J'ai quand même dû finaliser la migration à la main, et je n'ai pas pu me passer de la lecture du [Spring Boot 4.0 Migration Guide](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide). 353 + 354 + Je pense que le support de Spring Boot 4 dans OpenRewrite n'en est qu'à ses débuts (la version qui introduit le support a été publiée le 5 décembre 2025, et la mise à jour pour l'architecture modulaire a été publiée le 16 décembre), donc il n'est pas impossible que les opérations que j'ai dû faire manuellement soient automatisées dans le futur. 355 + 356 + Quoi qu'il en soit, 1h de travail pour migrer un projet d'environ 3 000 lignes de code, je pense que c'est plutôt efficace.
content/posts/2025-12-05-openrewrite-spring-boot-4/v2/tests.png

This is a binary file and will not be displayed.