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
2.5 KiB
2.5 KiB
← Profiles · Index · Why your profile file lost →
3. Seeing precedence instead of reasoning about it
Endpoint: PrecedenceEndpoint.
Transcript: 01-precedence.txt.
Set demo.greeting from five places at once and ask which won:
$ DEMO_GREETING=from-environment-variable \
java -Ddemo.greeting=from-system-property \
-jar profiles-and-config-1.0.0.jar --spring.profiles.active=prod \
--demo.greeting=from-command-line-argument
"effectiveValue": "from-command-line-argument",
"activeProfiles": ["prod", "prod-db", "prod-metrics"],
"holders": [
{ "rank": 1, "value": "from-command-line-argument", "source": "commandLineArgs" },
{ "rank": 2, "value": "from-system-property", "source": "systemProperties" },
{ "rank": 3, "value": "from-environment-variable", "source": "systemEnvironment" },
{ "rank": 4, "value": "from-application-prod-yaml", "origin": "application-prod.yaml - 4:13" },
{ "rank": 5, "value": "from-application-yaml", "origin": "application.yaml - 21:13" }
],
"shadowedCount": 4
Five sources hold the property. Four of them lose. Each one that came from a file names its line.
The whole implementation
for (ConfigurationPropertySource source : ConfigurationPropertySources.get(environment)) {
ConfigurationProperty property =
source.getConfigurationProperty(ConfigurationPropertyName.of(name));
if (property != null) {
// rank = position, property.getValue(), property.getOrigin()
}
}
ConfigurationPropertySources.get(...) returns the sources in precedence order. Iterate,
collect every hit, and the first is the winner. That is the entire diagnostic.
Why this beats reading the list
The documented order is correct but abstract. It does not tell you that a DEMO_GREETING left
over from a shell three weeks ago is sitting at rank 3, and that is the actual question.
Alternatives if you would rather not add an endpoint
- Actuator's
/actuator/envgives the same information with sanitisation, and/actuator/env/{name}narrows to one property. Prefer it in anything real. logging.level.org.springframework.boot.context.config=TRACElogs which config data resources were loaded and in what order.--debugdoes not show this. It prints the auto-configuration report.