configuration-properties/ @ConfigurationProperties vs @Value on Spring Boot 4.1.1. The relaxed-binding matrix is generated by binding each spelling rather than transcribed, and re-checked against real processes -- the in-process probe was wrong twice before it was right. Records the three findings that came out of it: @Value does get relaxed resolution inside Spring Boot (Boot attaches ConfigurationPropertySources), the configuration processor silently stops generating metadata on JDK 23+ when declared as a plain dependency, and @Valid is not what makes nested constraints run. profiles-and-config/ Precedence, profiles, spring.config.import and config trees. /precedence reports every source holding a property in rank order with file and line, which turns "my profile file had no effect" into a two-line answer. Also pins the counterintuitive one: an imported file outranks the file that imported it. spring-aop/ Designators, proxy types, and aspects that do not fire. One advice per supported designator so the reference table is generated from real matches; all fourteen unsupported designators fed to the parser. Two corrections to the reference documentation: unsupported designators throw UnsupportedPointcutPrimitiveException (extends RuntimeException, not IllegalArgumentException), and spring-boot-starter-aop was renamed to spring-boot-starter-aspectj in Boot 4. 19 contract tests across the three modules, 15 captured transcripts, all regenerated by scripts/run-all.sh. Verified on Spring Boot 4.1.1, Spring Framework 7.0.9, JDK 25.0.4.1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gip4srpzMwjgoba6uEfbr5
1.7 KiB
← Validation · Index · IDE metadata →
6. When @Value is still the right answer
Binding wins most of the time, and this project is largely an argument for it. The exceptions are real though, and pretending otherwise makes the advice easy to dismiss.
SpEL
@Value evaluates SpEL; the binder does not. If the value has to be computed, this is the
only one of the two that can do it:
@Value("#{T(java.lang.Runtime).getRuntime().availableProcessors() * 2}")
private int computedThreads;
There is no @ConfigurationProperties equivalent. The binder maps a value; it does not derive
one. (You can always compute in a compact constructor instead, which is usually clearer.)
A single value in a class that is not about configuration
A @Component that needs one feature flag does not benefit from a properties type. The type
would exist to hold one field and would be injected in one place.
The line is roughly: if you would name the type after the group of settings and the name would
be meaningful, use binding. If you would have to invent a name, use @Value.
Reading someone else's property
@Value("${server.port}") reads a property owned by Spring Boot. Declaring your own type for
it would imply you own it.
What is not a good reason
- "It's less code." For one property, marginally. For four, the properties type is shorter and it is also checked.
- "I need a default." Both support defaults.
- "@Value is faster." Neither is measurable next to a database call, and both happen once at startup.
- "Relaxed binding doesn't work with @Value." Inside Spring Boot, it does — chapter 2.