What kinds of join points can proxy-based Spring AOP advise, and what can't it?
answer
- Only method execution join points
- No field / constructor / static / private
- Proxy wraps, can't hook new()
- Self-invocation bypasses proxy
- Need more → AspectJ weaving
basics
~10 sSpring AOP only intercepts method executions on Spring beans. It cannot advise field reads/writes, constructors, static methods, or private methods — because it works by wrapping the bean in a proxy object.
solid answer
~40 sProxy-based Spring AOP supports exactly one join point type: the execution of a public (or protected) instance method on a Spring-managed bean. Because interception happens in a proxy that wraps the target, there is no hook for field access (get/set), object construction (constructors), static methods, or private/final methods — the proxy can only stand in front of overridable, externally-invoked methods. AspectJ's pointcut language technically has `execution`, `call`, `get`, `set`, `initialization`, etc., but Spring implements only the equivalent of method `execution`. If you need those other join points you must switch to full AspectJ (compile-time or load-time weaving). This is why the Spring reference calls its AOP 'method interception'.
code
java · 19 lines@Aspect
@Component
public class AuditAspect {
// WORKS: advises the *execution* of public methods on Spring beans.
@Before("execution(* com.example.service.*.*(..))")
public void beforeServiceCall(JoinPoint jp) {
System.out.println("calling " + jp.getSignature());
}
// DOES NOT WORK in Spring AOP: field 'set' join point.
// Spring rejects unsupported pointcut primitives at startup.
// @Before("set(* com.example..*)") // UnsupportedPointcutPrimitiveException
// public void beforeFieldWrite() { }
// DOES NOT WORK in Spring AOP: constructor 'initialization' join point.
// @After("initialization(com.example..*.new(..))") // not supported
// public void afterConstruct() { }
}go deeper
Know the one-liner: Spring AOP only intercepts method executions on Spring beans; not fields, constructors, or statics.
Explain WHY via proxy mechanics and list what's excluded (private/final/static/self-invocation).
Contrast with AspectJ's full pointcut designators and know Spring rejects unsupported primitives at startup.
Frame the trade-off: proxy simplicity vs. AspectJ weaving's reach, and its impact on transaction/cache annotation design.
## Background: what a 'join point' is In Aspect-Oriented Programming (AOP), a **join point** is a point in the running program where cross-cutting behavior (an **aspect**) could be inserted — e.g. 'when a method runs', 'when a field is read', 'when an object is constructed'. A **pointcut** is a predicate that selects a subset of join points; an **advice** is the code (like `@Before`, `@Around`) that runs at matched join points. ## Spring AOP supports only method-execution join points Spring AOP is **proxy-based**. When a bean has an applicable aspect, the container does not give you the raw object — it gives you a **proxy** that wraps it (a JDK dynamic proxy if the bean implements an interface, or a CGLIB subclass proxy otherwise). Calls go proxy → advice chain → real target method. Because interception lives in that proxy wrapper, the **only** thing it can intercept is a **method execution** on the target bean. Concretely, Spring AOP **cannot** advise: - **Field access** — reading or writing a field (`this.count`, `obj.name = x`). There is no proxy hook for field get/set. - **Constructors** — object creation join points (`new Foo()`). The proxy exists only after construction; it can't wrap `new`. - **Static methods** — no instance, nothing to proxy. - **`private` methods** — not visible to a subclass/interface proxy, so never intercepted. - **`final` methods / `final` classes** — CGLIB can't override `final`, so they slip through. - **Self-invocation** — when a bean calls its own method via `this.other()`, the call bypasses the proxy entirely, so no advice runs (a very common gotcha). ## Why: proxy mechanics A JDK proxy implements the same interfaces and delegates each interface method to an `InvocationHandler`. A CGLIB proxy subclasses the target and overrides its methods. In **both** cases the interception is 'run some code around this overridable, externally-dispatched method call'. Fields, constructors, statics, and private/final members are simply not overridable dispatch points, so there is nothing to wrap. ## The granularity gap → AspectJ The full **AspectJ** language defines many join-point kinds via pointcut designators: `execution`, `call` (the call *site*, in the caller), `get`/`set` (field access), `initialization`/`preinitialization` (constructors), `staticinitialization`, `handler` (exception handlers), etc. Spring's `@Aspect` support reuses AspectJ's **annotations and pointcut expression syntax** but implements **only** the runtime equivalent of `execution` — Spring will actually reject a Spring-AOP pointcut that uses unsupported designators like `call`, `get`, `set`, or `initialization` with an `IllegalArgumentException`/`UnsupportedPointcutPrimitiveException`. When this method-only granularity isn't enough — e.g. you must trace every field write, intercept constructors, or weave into non-Spring objects like JPA entities or domain objects created with `new` — you move to **full AspectJ weaving**: - **Compile-time weaving (CTW)** via the AspectJ compiler (`ajc`). - **Load-time weaving (LTW)** via a Java agent (`spring-instrument` / `-javaagent`), enabled with `@EnableLoadTimeWeaving` or `<context:load-time-weaver/>`. AspectJ modifies bytecode rather than using proxies, so it can reach fields, constructors, `final`, `private`, and self-invocation — at the cost of a build/agent step and more complexity. ## When to use which - Stick with **Spring AOP** for the 95% case: transactions (`@Transactional`), security, caching (`@Cacheable`), logging around service/repository **public method calls between beans**. Simple, no extra build step. - Reach for **AspectJ** only when you genuinely need non-method join points, must advise objects Spring doesn't create, or must catch self-invocation. ## Key gotcha to remember `@Transactional` and `@Cacheable` are Spring AOP under the hood, so all the same limitations apply: annotate `public` methods, and don't rely on **self-invocation** — an internal `this.method()` call won't trigger the aspect.
- Why can't Spring AOP intercept a private method?The proxy either implements the target's interfaces (JDK) or subclasses it (CGLIB). Private methods aren't part of an interface and can't be overridden by a subclass, so the proxy never sees the call — it dispatches straight on the target instance.
- If I need to advise constructors or field writes, what do I use?Full AspectJ weaving — compile-time weaving with ajc, or load-time weaving via the spring-instrument Java agent and @EnableLoadTimeWeaving. AspectJ rewrites bytecode instead of using proxies, so it can hit constructors, fields, statics, and self-invocation.
saying these in an interview costs you the question
- Claiming Spring AOP can intercept field access or constructors out of the box
- Saying private methods can be advised if the pointcut matches them
- Thinking @EnableAspectJAutoProxy gives full AspectJ join-point coverage (it only enables the proxy-based method interception)