skip to content

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

level: middleimportance: must knowfreq 50%

answer

  1. @within = declaring type (static)
  2. @target = runtime object class (dynamic)
  3. diverge under inheritance / @Inherited
  4. @target adds runtime residue + proxy-eligibility cost
  5. both need TYPE target + RUNTIME retention

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.

solid answer

~40 s

@within(Ann) matches method-execution join points declared within a type annotated with Ann — matching is based on the **declaring type** and is resolved **statically** at proxy-creation time. @target(Ann) matches when the **runtime class of the target object** (the proxied bean) is annotated with Ann — a **runtime** check evaluated per invocation. For a simple non-inherited class they behave the same. They diverge with inheritance: if @Ann is on a base class and you call a method declared in an unannotated subclass, @within does not match (declaring type is the subclass) whereas @target may match if the annotation is @Inherited on the runtime type — and vice versa. @target (like @args/this/target) forces a runtime residual check, which affects performance and auto-proxy eligibility; @within is cheaper. Both need RUNTIME retention and a @Target that allows TYPE.

code

java · 20 lines
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Monitored {}

@Monitored
public class BaseService { public void base() {} }
public class ExtService extends BaseService { public void ext() {} }

@Aspect @Component
class MonitorAspect {
    // Matches base() on any bean, because base() is DECLARED in @Monitored BaseService.
    // Does NOT match ext(), declared in the un-annotated ExtService.
    @Before("@within(com.example.Monitored)")
    public void byDeclaringType(JoinPoint jp) {}

    // Matches ANY method when the RUNTIME object's class is @Monitored.
    // For an ExtService instance this hinges on @Monitored being @Inherited.
    @Before("@target(com.example.Monitored)")
    public void byRuntimeType(JoinPoint jp) {}
}

go deeper

for a junior

Know both check for an annotation on a type, not a method.

for a middle

State clearly: @within = declaring type (static), @target = runtime object type (dynamic); give the inheritance example.

for a senior

Explain the @Inherited nuance and the runtime-residue/auto-proxy performance implications of @target.

for a principal

Advise designers to prefer @within/@annotation; discuss early-bean-instantiation and BeanPostProcessor ordering pitfalls caused by runtime designators at scale.

## The two designators Both `@within` and `@target` are **type-level annotation-presence** designators — they ask 'does a *type* carry annotation Ann?' The difference is **which type** and **when it is evaluated**. ### @within(Ann) — declaring type, static `@within(Ann)` matches a method-execution join point when the **type that declares the executing method** is annotated with `Ann`. Think of it as 'the code lexically *within* a type marked @Ann'. Because the declaring type of a method is known at compile/proxy time, this is a **static** match — no per-call runtime work. ### @target(Ann) — runtime target type, dynamic `@target(Ann)` matches when the **runtime class of the target object** (the actual bean instance being proxied) is annotated with `Ann`. Because the runtime type can only be known when a call happens, this introduces a **runtime residue** — Spring inserts a runtime check on each invocation. ## Where they differ: inheritance Consider: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Monitored {} @Monitored public class BaseService { public void base() {} } public class ExtService extends BaseService { public void ext() {} } ``` With a bean of runtime type `ExtService`: - Calling **base()** (declared in `BaseService`): `@within(Monitored)` MATCHES — the declaring type `BaseService` is annotated. `@target(Monitored)` checks `ExtService.class` — matches **only if** `@Monitored` is `@Inherited`. - Calling **ext()** (declared in `ExtService`): `@within(Monitored)` does NOT match — declaring type `ExtService` is not annotated. `@target(Monitored)` again depends on `@Inherited`. So: `@within` follows *where the method is written*; `@target` follows *the concrete object's class*. For a single, non-inherited, annotated class calling its own methods, they produce identical results — which is why people conflate them. ## @Inherited nuance Java's `@Inherited` meta-annotation only propagates a **type** annotation from a superclass to subclasses (never for interfaces, never for method annotations). This is precisely what can make `@target` on a subclass match even when the annotation is physically only on the superclass. ## Practical consequences of the static vs runtime split - **Performance / proxy eligibility:** `@target` (along with `@args`, `this`, `target`) cannot be fully resolved statically, so Spring must treat many beans as *potentially* matching. During auto-proxy creation this can cause Spring to instantiate beans early and log warnings like *'is not eligible for getting processed by all BeanPostProcessors'*. `@within` has no such cost. - **Recommendation:** prefer `@within` (or `@annotation`) when the declaring type is what you mean; reserve `@target` for cases where the *runtime* type genuinely matters. ## Requirements Both require the annotation to have `RUNTIME` retention and a `@Target` that permits `TYPE` (i.e. it can be placed on a class). ## Relation to declarative features A class-level `@Transactional` applies to all methods of the class — conceptually `@within`-like (declared-in behavior). Spring's actual implementation uses a dedicated pointcut with merged-annotation lookup, but the mental model of 'type carries the annotation ⇒ all its methods match' is the `@within` idea.

  • Why can @target hurt startup and trigger 'not eligible for BeanPostProcessor' warnings?
    @target needs the runtime object type, so its match can't be decided statically. During auto-proxy creation Spring must consider every bean as a possible match, which can force beans to be instantiated earlier than their BeanPostProcessors are ready, producing those warnings. @within avoids this since it's fully static.
  • When would @within and @target give identical results?
    For a single class that is annotated, not extended (or whose methods are all declared in the annotated class), and whose runtime instances are exactly that class. With no inheritance the declaring type and the runtime type coincide.

saying these in an interview costs you the question

  • Saying @within is the runtime one and @target the static one (reversed)
  • Claiming they are always interchangeable
  • Ignoring that method annotations and interface annotations are never @Inherited
  • Not knowing @target imposes a runtime check / proxy-eligibility cost

context