Three new article modules: configuration binding, profiles and config data, Spring AOP

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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Gip4srpzMwjgoba6uEfbr5
This commit is contained in:
2026-09-08 16:36:17 +00:00
co-authored by Claude Opus 5
parent 958b401f0f
commit ff37f44a84
107 changed files with 5072 additions and 0 deletions
@@ -0,0 +1,85 @@
[&larr; When @Value wins](06-when-value-still-wins.md) &middot; [Index](../README.md) &middot; [Diagnosing a value &rarr;](08-diagnosing-a-value.md)
# 7. IDE metadata, and the JDK 23 change that silently breaks it
Transcript: [`05-metadata-generation.txt`](output/05-metadata-generation.txt).
## What the metadata is
`spring-boot-configuration-processor` is an annotation processor. At compile time it reads your
`@ConfigurationProperties` types and writes `META-INF/spring-configuration-metadata.json`:
```json
{
"groups": [
{ "name": "demo.mail", "type": "com.ankurm.configprops.props.MailProperties" }
],
"properties": [
{ "name": "demo.mail.port", "type": "java.lang.Integer", "defaultValue": 587 }
]
}
```
That file is what makes property names auto-complete in an IDE, and what shows the Javadoc on
a record component as hover documentation. Nothing at runtime reads it.
## The failure
Declaring the processor as a dependency — the way every tutorial written before 2024 shows —
stops working on JDK 23 and later:
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
```
`javac` used to discover annotation processors on the classpath. That discovery was deprecated
with a warning in JDK 21 and switched off in JDK 23. A processor that is only on the classpath
is now simply not run.
The build still succeeds. The jar is still valid. The application behaves identically. The only
symptom is that property auto-completion quietly stops working, which is the kind of thing
people blame on the IDE.
Two compilations of identical sources with the identical processor jar:
```
A) javac -cp <deps>:spring-boot-configuration-processor.jar -d a $SOURCES
spring-configuration-metadata.json files produced: 0
B) javac -proc:full -cp <deps>:spring-boot-configuration-processor.jar -d b $SOURCES
spring-configuration-metadata.json files produced: 1
```
## The fix
Declare it as an annotation processor path, which puts it on `--processor-path` where discovery
was never disabled:
```xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${project.parent.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
```
`-proc:full` also works and is a smaller change, but it re-enables classpath scanning for every
processor, which is the behaviour that was turned off for a reason.
## Checking
`ls target/classes/META-INF/spring-configuration-metadata.json`. If it is not there, the
processor did not run. Add `@ConfigurationProperties` metadata to your build's definition of
done, because nothing else will tell you.