1
0
Files
spring-boot-demo/spring-aop/docs/02-designators.md
Ankur Mhatre 86246dc860 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gip4srpzMwjgoba6uEfbr5
2026-09-08 16:47:48 +00:00

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.