skip to content

@annotation, @within, @target, @args

@annotation, @within, @target and @args match on an annotation being present on the method, the type, or a runtime argument. This is exactly how @Transactional and @Cacheable get matched, so it is a natural interview bridge.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What does the @annotation pointcut designator match in Spring AOP, and how is it different from designators like execution() or within()?

level: juniorimportance: must knowfreq 55%

answer

  1. method carries the annotation
  2. opt-in by annotation, not by name
  3. RUNTIME retention mandatory
  4. static match, no runtime residue
  5. method annotations not inherited

basics

~10 s

@annotation(SomeAnnotation) matches any method-execution join point where the method being called carries that annotation. Unlike execution()/within(), which match by name or package pattern, @annotation matches by the presence of an annotation on the method.

solid answer

~40 s

@annotation is an annotation-presence pointcut designator: @annotation(com.example.Audited) matches a method-execution join point when the executing method is annotated with @Audited. execution() and within() select join points by signature/type-name patterns; @annotation selects them by the metadata (annotation) present on the method itself, so advice applies wherever developers opt in by adding the annotation, regardless of package or name. The annotation's @Retention must be RUNTIME, or Spring cannot see it. Because it targets the method, it is resolved statically (the method is known at proxy-creation time). It is the conceptual basis for how declarative features like @Transactional and @Cacheable match method-level annotations. You can also bind the annotation instance as an advice parameter to read its attributes.

code

java · 15 lines
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Audited { String level() default "INFO"; }

@Aspect
@Component
public class AuditAspect {

    // Matches ANY method annotated with @Audited, regardless of class/package.
    @Around("@annotation(audited)")
    public Object audit(ProceedingJoinPoint pjp, Audited audited) throws Throwable {
        log.info("[{}] {}", audited.level(), pjp.getSignature().toShortString());
        return pjp.proceed();
    }
}

go deeper

for a junior

Know that @annotation matches methods that carry a given annotation, and that retention must be RUNTIME.

for a middle

Contrast with execution()/within(); know it is a static matcher and that you can bind the annotation to read attributes.

for a senior

Articulate the opt-in design, the non-inheritance of method annotations, and how this underpins @Transactional/@Cacheable method-level matching.

for a principal

Discuss trade-offs vs @within/@target for API design, retention/meta-annotation strategy, and why annotation-based designators decouple aspects from package structure.

## What it is Spring AOP pointcut expressions use AspectJ's pointcut language. Besides the common `execution(...)`, `within(...)`, `args(...)` designators, AspectJ provides four **annotation-presence** designators: `@annotation`, `@within`, `@target`, and `@args`. They select join points based on where an annotation is present rather than on class/method names. `@annotation(SomeAnnotation)` matches a **method-execution join point when the method being executed is directly annotated** with `SomeAnnotation`. (In Spring AOP the only join point kind is method execution — there is no field or constructor weaving.) ```java @Retention(RetentionPolicy.RUNTIME) // MANDATORY — Spring reads annotations reflectively at runtime @Target(ElementType.METHOD) public @interface Audited {} @Aspect @Component public class AuditAspect { @Before("@annotation(com.example.Audited)") public void audit(JoinPoint jp) { /* runs before any @Audited method */ } } ``` ## How it differs from execution()/within() - `execution(* com.example.service.*.*(..))` selects by **method signature pattern** (return type, package, class, name, args). - `within(com.example.service..*)` selects by the **type** in which the join point occurs (name pattern). - `@annotation(X)` selects by the **annotation present on the method** — no name or package coupling. Developers opt in per method by adding `@X`. This inversion of control is why annotation designators scale so well: the aspect declares *what capability* to apply, and any method across the codebase can request it by adding the annotation. ## Key requirements and mechanics - **Retention must be `RUNTIME`.** With `SOURCE` or `CLASS` retention the annotation is invisible at runtime and never matches. - `@annotation` is a **static** matcher: whether a method carries the annotation is known when Spring builds the proxy, so there is no per-invocation runtime residue check (unlike `@target`/`@args`). - **Java method annotations are NOT inherited.** If a subclass overrides an annotated method without re-declaring the annotation, `@annotation` no longer matches that overriding method. (Spring's own `@Transactional`/`@Cacheable` support works around this with `AnnotatedElementUtils`/merged-annotation lookup, but a raw `@annotation` pointcut does not.) ## Reading annotation attributes You can **bind** the annotation instance to an advice parameter whose name matches the designator reference, then read its attributes: ```java @Around("@annotation(audited)") public Object around(ProceedingJoinPoint pjp, Audited audited) throws Throwable { String level = audited.level(); // read attribute return pjp.proceed(); } ``` ## When to use Use `@annotation` for **cross-cutting behavior that individual methods opt into**: auditing, custom caching, retry, rate-limiting, metrics timing. It is the cleanest, most explicit of the annotation designators and the safest performance-wise.

  • Why must the annotation have RUNTIME retention?
    Spring AOP inspects annotations reflectively at runtime when matching pointcuts and creating proxies. With SOURCE or CLASS retention the annotation is discarded before runtime, so it is never visible and the pointcut can never match.
  • If a subclass overrides a method annotated with @Audited but doesn't repeat the annotation, does @annotation still match?
    No. Java method-level annotations are not inherited, so the overriding method has no @Audited and the pointcut skips it. You'd have to re-declare the annotation on the override.

saying these in an interview costs you the question

  • Claiming @annotation matches when the annotation is on the class (that's @within/@target)
  • Saying SOURCE/CLASS retention works for AOP matching
  • Believing method annotations are inherited by overrides
  • Confusing @annotation with the @Annotation-on-parameter idea (that's @args)

context

open as a page

What is the difference between the @within and @target pointcut designators?

level: middleimportance: must knowfreq 50%

basics

~20 s

Both match when a type carries the annotation, but @within uses the type that declares the executed method (resolved statically), while @target uses the runtime class of the target object (resolved at runtime). They differ when inheritance is involved.

open as a page

How do you bind an annotation instance to advice so you can read its attributes, and what are the parameter-name rules?

level: middleimportance: should knowfreq 40%

basics

~20 s

Reference the annotation by a lowercase name in the designator, e.g. @annotation(retry), and declare an advice parameter of that annotation type with the same name (Retry retry). Spring injects the actual annotation instance so you can call its attribute methods.

open as a page

What does the @args designator match, and how does it differ from args()? What are its runtime and performance implications?

level: seniorimportance: should knowfreq 30%

basics

~20 s

@args matches when the runtime types of the passed arguments are themselves annotated. @args(Validated) matches a call whose single argument's actual class carries @Validated. args() matches by argument type, not by annotation. @args is a runtime check.

open as a page

How do the annotation-presence designators relate to how Spring matches @Transactional and @Cacheable, and what subtleties (inheritance, interfaces, runtime designators) should you be aware of at scale?

level: principalimportance: should knowfreq 28%

basics

~20 s

@Transactional/@Cacheable are matched by dedicated Spring pointcuts that, like @annotation plus @within, look for the annotation on the method and fall back to the class. They add merged-annotation and meta-annotation support that a raw @annotation pointcut lacks.

open as a page