skip to content

Write a @DeclareParents declaration and explain each of its attributes (value, defaultImpl) and the field type.

level: middleimportance: should knowfreq 18%

answer

  1. field type = interface introduced
  2. value = AspectJ TYPE pattern (not method)
  3. '+' = include subtypes
  4. defaultImpl = concrete impl class
  5. pair with this(mixin) to bind in advice

basics

~10 s

Put @DeclareParents on a field in an @Aspect. The field's type is the interface to introduce, value is an AspectJ pattern for target beans (e.g. "com.example.service.*+"), and defaultImpl is the class implementing that interface.

solid answer

~40 s

@DeclareParents lives on a field declared inside an @Aspect class. Three things carry meaning: (1) the field's declared type is the interface Spring adds to the matched beans; (2) the value attribute is an AspectJ type-matching expression selecting which types receive the interface — the trailing + matches subtypes; (3) defaultImpl is the concrete class supplying the methods. At runtime Spring makes the proxy implement that interface, delegating its methods to a defaultImpl instance. To combine it with advice, you often pair it with a this() pointcut that binds the introduced interface, e.g. this(usageTracked), so advice can call the mixin methods. The field itself is just a declaration hook — its value is irrelevant; only its type matters.

code

java · 15 lines
java
@Aspect
@Component
public class UsageTrackingAspect {

    @DeclareParents(
        value = "com.example.service.*+",   // target TYPE pattern (+ = subtypes)
        defaultImpl = DefaultUsageTracked.class) // concrete implementation
    public static UsageTracked mixin;            // field TYPE = introduced interface

    // Bind the proxy-as-interface so advice can drive the mixin
    @Before("execution(* com.example.service.*.*(..)) && this(usageTracked)")
    public void recordUsage(UsageTracked usageTracked) {
        usageTracked.incrementUseCount();
    }
}

go deeper

for a junior

Recognize the annotation and that it names an interface to add.

for a middle

Correctly map each part: field type, value pattern, defaultImpl; know value is a type pattern.

for a senior

Also wire it to advice via this() binding and explain subtype matching with +.

for a principal

Discuss maintainability of type patterns and how introductions interact with proxy creation across a module.

The **anatomy of a `@DeclareParents` declaration** has three load-bearing parts, and it is a common interview trap to confuse them. ```java @Aspect @Component public class UsageTrackingAspect { @DeclareParents( value = "com.example.service.*+", defaultImpl = DefaultUsageTracked.class) public static UsageTracked mixin; } ``` **1. The field's declared type (`UsageTracked`)** — this is the **interface being introduced**. Spring reads the field's *type*, not its value; the field is never actually assigned or used as data. People sometimes make it `static` to signal that. The point is purely to name the interface. **2. `value` — the AspectJ type pattern.** This selects *which types* gain the interface. `"com.example.service.*+"` means "any type directly in package `com.example.service`, plus (`+`) all subtypes." You can use fuller AspectJ type patterns here. Note this is a **type pattern**, not a method pointcut — introductions attach to types, not to individual join points. **3. `defaultImpl` — the implementation class.** This concrete class must implement the introduced interface and supply the actual method bodies. Spring instantiates it (via its no-arg constructor) to back the introduction. **Combining with advice.** Introductions become powerful when advice can call the mixin. Spring's reference example wires it with a `this()` designator that binds the proxy *as* the introduced interface: ```java @Before("execution(* com.example.service.*.*(..)) && this(usageTracked)") public void recordUsage(UsageTracked usageTracked) { usageTracked.incrementUseCount(); } ``` Here `this(usageTracked)` matches only when the proxy actually implements `UsageTracked` (which, thanks to the introduction, it now does), and binds it as the argument so the advice can update the mixin state on every business call. **Common mistakes:** - Writing a *method pointcut* into `value` (e.g. `execution(...)`) — wrong; `value` is a **type** pattern. - Forgetting the `+` and being surprised subclasses aren't matched. - Expecting the field's *value* to matter — only its *type* does. - Making `defaultImpl` an interface or an abstract class — it must be concrete and instantiable.

  • Does the value attribute take a method pointcut like execution(...)?
    No. value is an AspectJ type-matching pattern selecting the target types (e.g. com.example.service.*+). Method pointcuts belong on the advice you pair with the introduction, not on @DeclareParents itself.
  • Why is the annotated field often declared static and never read?
    Because only the field's declared type matters — it names the interface to introduce. The field is a declaration hook, never assigned or used as data, so static just underlines that it holds no per-instance state.

saying these in an interview costs you the question

  • Putting an execution() method pointcut in the value attribute
  • Thinking defaultImpl can be an interface or abstract class
  • Believing the field's value (not type) is what's introduced

context