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.3 KiB
1. What Spring AOP actually is
Spring AOP is a proxy mechanism that borrows AspectJ's pointcut language. Both halves of that sentence explain a failure mode.
It is proxies
Spring does not modify your bytecode. When a bean matches a pointcut, the container puts a proxy in the bean registry in its place. Callers get the proxy; the proxy runs advice and then forwards to the real object.
Everything that follows from this:
- Only beans can be advised. An object created with
newhas no proxy. - Only calls through the proxy are intercepted. A call the object makes to itself is not.
- Only overridable methods can be advised: not private, not final, not static.
- Join points are method executions. Field access, constructor calls and exception handlers are not available, no matter what the pointcut language allows you to write.
It borrows the pointcut language
aspectjweaver on the classpath supplies the pointcut parser. Spring uses it to decide which
methods match and then does its own weaving with proxies. AspectJ's own weaver is not involved.
This is why the designator list is a subset: the language can express call() and cflow(),
and a proxy cannot implement them. See chapter 2.
The Spring Boot 4 starter rename
In Spring Boot 3 the dependency was spring-boot-starter-aop. In Spring Boot 4 it is
spring-boot-starter-aspectj.
spring-boot-starter-aop last published 4.0.0-M2 (last GA: 3.5.16)
spring-boot-starter-aspectj first published 4.0.0-M3
The contents are unchanged: spring-boot-starter, spring-aop, aspectjweaver. But the old
artifact is no longer in the Boot BOM, so copying a dependency block out of any pre-4 tutorial
fails resolution with a missing-version error rather than a helpful message.
Without the starter, @Aspect classes are ordinary beans, no pointcut is ever parsed, and
every aspect in the application silently matches nothing.
When to use something else
If you need to advise field access, constructors, or calls between objects you do not own, you need real AspectJ weaving (compile-time or load-time), not Spring AOP. If you need to advise one internal call, you need to refactor — see chapter 5.