When would you reach for a @DeclareParents introduction versus alternatives, and what are its limitations in Spring AOP versus full AspectJ?
answer
- use case: uniform orthogonal capability on many/uneditable beans
- prefer interfaces/base class/composition if you own the code
- Spring = proxy, interfaces + defaultImpl only
- AspectJ weaving = real fields/methods on the type itself
- last resort; document it — poor discoverability
basics
~20 sUse an introduction to give many beans a uniform interface/capability without editing each class — e.g. change-tracking. Prefer plain interfaces or composition when you can edit the classes. Spring can only introduce interfaces via proxies; full AspectJ weaving can add fields and methods to the class itself.
solid answer
~50 sIntroductions fit when you must add a uniform, orthogonal capability (change tracking, usage metrics, tagging) to a whole family of beans you'd rather not modify — third-party or generated classes, or many classes where boilerplate would be repetitive. But they're a niche tool: the capability is only visible on the proxy, callers must cast, self-invocation doesn't work, and it's interfaces-only in Spring. If you own the classes, explicit interfaces, a shared base class, or composition/delegation are usually clearer and testable without a container. Compared to full AspectJ (compile- or load-time weaving), Spring AOP introductions are proxy-based and limited to introducing interfaces with a defaultImpl; native AspectJ inter-type declarations can add real fields and methods directly to the target type, change its supertypes, and work without a proxy. So reach for introductions rarely, and document why.
code
java · 20 lines// Canonical fit: dirty-tracking across many services without editing them.
public interface IsModified { boolean isModified(); }
public class DefaultIsModified implements IsModified {
private volatile boolean modified = false;
public void markModified() { modified = true; }
public boolean isModified() { return modified; }
}
@Aspect
@Component
public class ModificationTrackingAspect {
@DeclareParents(value = "com.example.repo.*+", defaultImpl = DefaultIsModified.class)
public static IsModified mixin;
@After("execution(* com.example.repo.*.save*(..)) && this(m)")
public void flag(DefaultIsModified m) { m.markModified(); }
}
// If you owned com.example.repo classes, having each implement IsModified
// directly (or via a base class) would usually be clearer and testable.go deeper
Just know introductions exist and add an interface; deep trade-offs not expected.
Name at least one valid use case and that Spring is interfaces-only.
Contrast with composition/base-class alternatives and note proxy limitations.
Give a crisp decision framework and the Spring-AOP-vs-AspectJ weaving distinction, including when to escalate to AspectJ.
This is a judgment question: introductions are powerful but easy to misuse, and a principal engineer should frame the trade-offs. **Legitimate use cases.** - **Change/dirty tracking:** give an `IsModified` interface to a family of domain-service beans so infrastructure can ask "did anything change?" without each class implementing the flag. (This is the canonical Spring reference example.) - **Usage/metrics tagging:** a `UsageTracked` counter driven by advice on every business call. - **Cross-cutting capability on classes you can't edit:** third-party or code-generated beans where adding an interface at the source isn't possible. The common thread: an **orthogonal capability**, applied **uniformly across many beans**, where per-class boilerplate is undesirable and the behavior is genuinely cross-cutting. **Why it's usually not the answer.** - **Discoverability:** the interface appears out of nowhere on the proxy; readers grepping the class won't find it. This hurts maintainability. - **Casting + proxy coupling:** callers must cast the proxy; self-invocation fails; testing outside the container is awkward. - **Interfaces only in Spring:** you can't add fields or new concrete methods to the target class itself. If you *own* the classes, the clearer alternatives are: implement the interface explicitly, extract a shared **base class**, or use **composition/delegation** (a collaborator object). These are container-independent, unit-testable, and obvious to readers. **Spring AOP vs full AspectJ.** - **Spring AOP** is **proxy-based** (JDK dynamic proxies or CGLIB). Its introduction support is limited to **introducing an interface plus a `defaultImpl`** onto the proxy. It cannot weave into the target class's bytecode. - **Native AspectJ** uses **compile-time or load-time weaving** and supports full **inter-type declarations**: adding real fields, constructors, and concrete methods to a type; declaring new supertypes/interfaces on the type itself; and privileged access. It works without a proxy, so self-invocation and internal use "just work," and there's no cast-the-proxy requirement in the same way. **Design guidance.** Treat introductions as a last resort within Spring AOP. Prefer them only when: (a) you cannot modify the target classes, (b) the capability is truly cross-cutting and stateful-per-bean, and (c) the small set of callers that need it can be given the proxy. Document the introduction prominently, and consider whether migrating to explicit interfaces later would reduce surprise. If your needs exceed proxy limits (fields on the target, no-proxy semantics), that's a signal to adopt AspectJ weaving rather than fight Spring's proxy model. **Ordering / interaction notes.** Introductions participate in proxy creation like other aspects; if multiple aspects apply, standard `@Order`/`Ordered` precedence governs advice, and the introduced interface is available once the proxy is built. Be mindful that adding an interface via CGLIB vs JDK proxy can affect which proxy type Spring chooses and what casts succeed elsewhere.
- What can native AspectJ inter-type declarations do that Spring AOP introductions cannot?AspectJ can weave into the target type directly — adding real fields, constructors, and concrete methods, and declaring new supertypes/interfaces on the class itself — because it uses compile/load-time weaving, not a proxy. Spring AOP is limited to introducing an interface plus a defaultImpl onto the proxy.
- You need many owned classes to expose the same capability. Introduction or plain interface?If you own the classes, prefer implementing the interface directly or via a shared base class / composed collaborator — it's discoverable, unit-testable without the container, and free of the cast-the-proxy and self-invocation caveats. Reserve introductions for classes you can't edit.
saying these in an interview costs you the question
- Claiming Spring AOP can introduce new fields/concrete methods onto the target class
- Recommending introductions as a first-choice pattern for code you control
- Not knowing introductions are proxy-based and interfaces-only in Spring
- Believing self-use inside the bean works the same as with AspectJ weaving