After introducing an interface with @DeclareParents, how do you actually call the new methods, and where does the mixin's state live?
answer
- cast the PROXY, not the target
- state lives in defaultImpl instance (one per bean)
- self-invocation fails — this = raw target
- get proxy via context/@Autowired or AopContext
- guard casts with instanceof
basics
~20 sYou hold the proxied bean reference and cast it to the introduced interface, then call its methods. The state lives in the defaultImpl instance that backs the proxy — typically one instance per advised bean — not in the original target object.
solid answer
~40 sThe introduced interface lives on the proxy, so you must obtain the proxied reference (from the context or via injection) and cast it to that interface: `((UsageTracked) orderService).getUseCount()`. Calls to the introduced methods are delegated to an instance of defaultImpl that Spring associates with that proxy; the target object itself has no idea the interface exists. State held by the mixin (a counter, a dirty flag) lives in that defaultImpl instance, and there is generally one per advised bean, so each bean tracks its own state. Crucially, self-invocation fails: inside the target you can't call the introduced method because `this` is the raw target, not the proxy, and doesn't implement the interface. You either route through the injected proxy or bind the interface in advice via this().
code
java · 18 lines@Service
public class OrderService { /* no UsageTracked here */ }
// Elsewhere, holding the injected/looked-up PROXY:
@Autowired
private OrderService orderService; // this is the proxy
public void report() {
if (orderService instanceof UsageTracked tracked) { // safe cast
System.out.println("used " + tracked.getUseCount() + " times");
}
}
// WRONG — inside OrderService itself:
// public void doWork() {
// ((UsageTracked) this).incrementUseCount(); // ClassCastException:
// // 'this' is the raw target, not the proxy that implements UsageTracked
// }go deeper
Know you cast the bean to the interface to call the new methods.
Understand you must cast the proxy specifically and that state is separate from the target.
Explain per-bean defaultImpl state and the self-invocation ClassCastException clearly.
Reason about proxy identity, exposeProxy/AopContext escape hatch, and when the coupling is justified.
This question probes whether you understand the **proxy semantics** behind introductions. **Where the new interface lives.** Spring AOP wraps the target bean in a **proxy**. When an aspect introduces an interface, it's the *proxy* that implements it — not the underlying target instance. The target class is unchanged and unaware. So the interface only exists on references that point at the proxy. **How to call the introduced methods.** Because only the proxy has the interface, you must: 1. Obtain the **proxied reference** — e.g. `ctx.getBean("orderService")` (the container hands out the proxy), or `@Autowired OrderService orderService` (also the proxy). 2. **Cast** it to the introduced interface: `UsageTracked ut = (UsageTracked) orderService;`. 3. Call the method: `ut.incrementUseCount();`. Alternatively, advice bound with `this(usageTracked)` receives the proxy already typed as the interface, letting the aspect drive the mixin on each intercepted call. **Where the state lives.** The introduced methods are backed by an instance of `defaultImpl`. Spring maintains (typically) **one `defaultImpl` instance per advised bean/proxy**, so per-bean mixin state — a use counter, an `isModified` flag — is isolated per bean. This is what makes introductions useful for stateful capabilities like change-tracking: each entity-service bean keeps its own flag. **The self-invocation gotcha.** This is the classic trap. Inside the target's own code, `this` refers to the **raw target**, which does *not* implement the introduced interface. So: - You cannot call `this.incrementUseCount()` from inside the target — it won't even compile against the target type, and casting `this` to the interface throws `ClassCastException` at runtime because the raw target isn't the proxy. - The capability is only reachable through the proxy: from outside callers, or from advice, or by injecting the proxy back into the bean (`AopContext.currentProxy()` with `exposeProxy = true`, then cast). **Other edge cases:** - **Interfaces only.** Spring can introduce an interface + `defaultImpl`; it cannot graft new concrete methods onto the target's own class (that needs full AspectJ). So the new capability is always accessed via the interface type. - **Casting safety.** Guard with `instanceof` if not every bean of a type is advised: `if (bean instanceof UsageTracked ut) { ut.getUseCount(); }`. - **Serialization / equals / hashCode** on the proxy behave per Spring's proxy rules, not the mixin's — don't assume the mixin participates in identity.
- Why does calling the introduced method via 'this' inside the target fail?Because the interface is implemented by the proxy, not the target. Inside the target, 'this' is the raw object, which doesn't implement the introduced interface, so the cast throws ClassCastException — the same self-invocation limitation that affects all Spring proxy-based AOP.
- If you truly need the bean to use its own introduced capability, how can you reach the proxy from inside?Enable exposeProxy (e.g. @EnableAspectJAutoProxy(exposeProxy = true)) and inside the method call AopContext.currentProxy(), then cast that to the introduced interface. It couples the bean to Spring AOP, so it's a last resort.
saying these in an interview costs you the question
- Claiming the introduced methods can be called via this inside the target
- Thinking the mixin state lives on the target object
- Assuming the raw target implements the introduced interface
- Casting without checking instanceof when not all beans are advised