Files
spring-boot-demo/custom-validation/docs/01-jakarta-namespace-and-bean-validation-3-1.md
T
Claude e4b5636f7c Add custom-validation, etag-caching, restclient-basic-auth: Boot 4.1 API pass
Three companion modules verifying and rewriting the Boot 4.1.1 / Framework
7.0.9 story for three older articles: the javax->jakarta.validation namespace
fix plus Jakarta Validation 3.1 record-validation clarification, ETag/
conditional-request APIs re-verified unchanged plus the starter rename, and
RestTemplate Basic Auth rebuilt on RestClient with the exchange() trap called
out. 19 real passing tests generate every transcript quoted from the three
companion articles.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EQNA6DJ9VgCtW6zhCE8Xud
2026-09-19 10:17:09 +00:00

4.2 KiB

1. The jakarta.validation namespace, and what Bean Validation 3.1 actually changed

README | Next: Record validation

Source: ContactForm.java, SpamMessageCheck.java. Transcripts: docs/output/00-jakarta-validation-api-manifest.txt, docs/output/00b-hibernate-validator-manifest.txt.

The defect this module replaces

The original version of the companion article this module backs used javax.validation.constraints.NotBlank, javax.validation.Constraint, and so on throughout -- the pre-Jakarta-EE-9 namespace. That package was already wrong for any Spring Boot 3+ application by the time the article was first published: Spring Boot 3.0 moved its entire dependency tree from javax.* to jakarta.* in December 2022, and javax.validation.* classes are simply not on the classpath of a spring-boot-starter-validation 3.x or 4.x application. Every class in this module uses jakarta.validation.* instead. If you have a codebase still on javax.validation, it did not survive the Boot 2 to 3 upgrade and needs the same mechanical rename this module already reflects.

What Spring Boot 4.1.1 actually pins, checked directly

Rather than trust a blog's version claim (including an older draft of this one), the fact was checked the way this repository always checks it: build a throwaway project against spring-boot-starter-parent:4.1.1 and run mvn dependency:tree.

[INFO] +- org.springframework.boot:spring-boot-starter-validation:jar:4.1.1:compile
[INFO] |  \- org.springframework.boot:spring-boot-validation:jar:4.1.1:compile
[INFO] |     \- org.hibernate.validator:hibernate-validator:jar:9.1.3.Final:compile
[INFO] |        +- jakarta.validation:jakarta.validation-api:jar:3.1.1:compile

Then confirmed a second, independent way: open the actual jars this application loads at runtime and read their own manifests, rather than trusting the dependency tree's coordinates alone.

$ mvn test   # ClasspathVersionTest
Class: jakarta.validation.Validation
Jar file: jakarta.validation-api-3.1.1.jar
Bundle-SymbolicName: jakarta.validation.jakarta.validation-api
Bundle-Version: 3.1.1
Implementation-Version: null

(docs/output/00-jakarta-validation-api-manifest.txt)

Class: org.hibernate.validator.internal.engine.ValidatorFactoryImpl
Jar file: hibernate-validator-9.1.3.Final.jar
Implementation-Title: hibernate-validator
Implementation-Version: 9.1.3.Final

(docs/output/00b-hibernate-validator-manifest.txt)

Spring Boot 4.1.1 pins Hibernate Validator 9.1.3.Final, the reference implementation of Jakarta Validation 3.1 -- the spec revision released in 2024 as part of Jakarta EE 11.

What is actually new in 3.1 (not a rename of 3.0)

Three real changes, not a version-number bump for its own sake:

  • The specification itself was renamed from "Jakarta Bean Validation" to "Jakarta Validation." The namespace (jakarta.validation.*) and the annotations you already know (@NotBlank, @Size, @Valid, @Constraint) are unchanged -- this is a spec-title change, not an API break.
  • The minimum required Java version moved to 17. Not a concern for anything already running JDK 21 or 25, but it is the reason Hibernate Validator 9.x cannot be backported to run on Java 11.
  • Record validation is now explicitly specified, closing a real gap in 3.0 where the spec was silent on how a constraint on a record component should behave. That is substantial enough to earn its own chapter -- see below.

Going deeper