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
3.9 KiB
← What Spring AOP is · Index · Proxy types →
2. The designator reference
Generated by DesignatorAspect
and PointcutParserEndpoint.
Transcript: 01-designators.txt.
Supported
| Designator | Matches on | Evaluated | Cost |
|---|---|---|---|
execution(...) |
method signature | statically | cheap |
within(Type) |
the declaring type | statically | cheap |
this(Type) |
the proxy's type | at runtime | per call |
target(Type) |
the target object's type | at runtime | per call |
args(Types) |
argument runtime types; can bind | at runtime | per call |
@target(Ann) |
annotation on the executing object's class | at runtime | per call |
@args(Ann) |
annotation on argument runtime types | at runtime | per call |
@within(Ann) |
annotation on the declaring type | statically | cheap |
@annotation(Ann) |
annotation on the method | statically | cheap |
bean(name) |
Spring bean name, wildcards allowed | — | cheap |
bean(...) is Spring's own; it does not exist in AspectJ.
The runtime group cannot be decided from the signature alone, so Spring must check on every
candidate invocation. Prefer the static equivalent where one exists: @within instead of
@target, within instead of this, when the distinction does not matter.
What each one actually matched here
execution (full signature) -> DefaultOrderService.place(..)
execution (wildcards) -> DefaultOrderService.cancel(..)
within -> InventoryService.reserve(..)
this(OrderService) -> place, cancel, interfaceless
target(DefaultOrderService) -> place, cancel, interfaceless
args (bound: SKU-1/2) -> DefaultOrderService.place(..)
@target(Audited) -> DefaultOrderService.cancel(..)
@args(Trackable) -> DefaultOrderService.interfaceless(..)
@within(Audited) -> DefaultOrderService.place(..)
@annotation(Marker) -> DefaultOrderService.place(..)
bean(inventoryService) -> InventoryService.reserve(..)
bean(*OrderService) -> place, cancel, interfaceless
InventoryService.finalCheck was called and appears nowhere: it is final, so no proxy could
override it. That is failure 4.
Not supported
call, get, set, preinitialization, staticinitialization, initialization, handler,
adviceexecution, withincode, cflow, cflowbelow, if, @this, @withincode.
All fourteen were fed to the parser. All fourteen were rejected, with:
org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException
Pointcut expression 'call(* ...place(..))' contains unsupported pointcut primitive 'call'
The reference documentation says these throw IllegalArgumentException. They do not.
UnsupportedPointcutPrimitiveException extends RuntimeException directly — checked with
javap, pinned by AopContractTests.unsupportedDesignatorExceptionType. A
catch (IllegalArgumentException) will not catch it.
When the failure happens
setExpression(...) only stores the string. The expression is not parsed or validated until
something asks it to match. An unsupported designator therefore fails at the first candidate
invocation, not at startup — so a rarely-exercised aspect can ship broken.
Combining and naming
&&, || and ! compose designators. Name the result rather than repeating it:
@Pointcut("within(com.ankurm.aop.service..*)")
public void inServiceLayer() {}
@Before("this(OrderService) && inServiceLayer()")
public void advice(JoinPoint jp) { }
A named pointcut is referenced by its method name and is the single biggest readability win available here.