Explain the proxyTargetClass and exposeProxy attributes of @EnableAspectJAutoProxy and when you'd set each.
answer
- proxyTargetClass=true -> always CGLIB
- JDK proxy = interfaces only
- CGLIB can't proxy final class/method
- exposeProxy -> AopContext.currentProxy()
- Fixes self-invocation bypass
basics
~10 sproxyTargetClass=true forces CGLIB class proxies instead of JDK interface proxies. exposeProxy=true publishes the current proxy via AopContext.currentProxy() so a bean can call its own advised methods through the proxy instead of bypassing it.
solid answer
~40 sproxyTargetClass controls the proxy strategy. Default false: if the target implements at least one interface, Spring makes a JDK dynamic proxy (only the interfaces are advisable); otherwise it falls back to CGLIB. Set true to always use CGLIB (a runtime subclass), which is needed when callers depend on the concrete type or non-interface methods — Spring Boot defaults this to true. exposeProxy addresses self-invocation: when a bean calls this.someAdvisedMethod(), the call goes straight to the target and skips the proxy, so advice like @Transactional or @Around doesn't run. With exposeProxy=true, Spring binds the proxy to a ThreadLocal, and you retrieve it via ((MyType) AopContext.currentProxy()).someAdvisedMethod() so the interceptor chain executes. Both are last resorts; refactoring to separate beans is usually cleaner than exposeProxy.
code
java · 17 lines@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
public class AopConfig {}
@Service
public class OrderService {
@Transactional
public void placeOrder(Order o) { /* ... */ }
public void placeBatch(List<Order> orders) {
for (Order o : orders) {
// this.placeOrder(o); // BUG: bypasses proxy, no transaction
((OrderService) AopContext.currentProxy()).placeOrder(o); // routes through proxy
}
}
}go deeper
Know CGLIB vs JDK exists and self-invocation skips advice.
Explain proxyTargetClass values and the self-invocation problem.
Detail CGLIB/final limitations and exposeProxy plus cleaner alternatives.
Weigh weaving vs proxying, context-wide impact on @Transactional/@Cacheable, and Objenesis/no-arg-ctor nuances.
## proxyTargetClass — which proxy technology Spring can build a proxy two ways: - **JDK dynamic proxy**: `java.lang.reflect.Proxy` implements the target's **interfaces**. The proxy is *not* an instance of the concrete class; only interface methods can be advised. Requires ≥1 interface. - **CGLIB proxy**: a runtime-generated **subclass** of the target class. Works with or without interfaces; can advise public/protected methods but **cannot** proxy `final` classes or `final`/`private` methods (they aren't overridable). `proxyTargetClass` attribute: - `false` (default in plain Spring): use JDK proxy **if** the bean implements an interface, else CGLIB. - `true`: **always** use CGLIB. When to force `true`: - Callers autowire the bean by its **concrete class** or call methods not on any interface — a JDK proxy would fail `ClassCastException`/`NoSuchMethod` because it only exposes interfaces. - You want consistent behavior. Spring Boot sets this to `true` by default for exactly this reason. CGLIB caveats: needs a non-final class; a no-arg constructor is called (Spring uses Objenesis to sidestep this in modern versions); `final` methods silently aren't advised. ## exposeProxy — solving self-invocation **Self-invocation problem**: proxies only intercept calls that come *through* the proxy. If code inside a bean calls another of its own methods via `this.foo()`, that call goes directly to the target instance — the proxy is bypassed, so any advice on `foo()` (transactions, caching, custom `@Around`) **does not run**. This is the #1 AOP gotcha. `exposeProxy=true` makes Spring store the current proxy in a `ThreadLocal` for the duration of a proxied call. Inside the bean you can then do: ```java ((MyService) AopContext.currentProxy()).foo(); ``` Now the internal call routes back through the proxy and the advice fires. ### Trade-offs / better options - `AopContext.currentProxy()` couples your business code to Spring AOP internals and throws `IllegalStateException` if `exposeProxy` is off — ugly. - **Preferred fixes**: move `foo()` into a separate bean and inject it; or self-inject the bean (`@Autowired MyService self;` / `ObjectProvider`); or use AspectJ load-time weaving which advises the real object (no proxy, so self-invocation works). Use `exposeProxy` only when refactoring isn't feasible. ## Putting it together ```java @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) public class AopConfig {} ``` This forces CGLIB proxies and exposes the proxy on the thread. Note both settings affect **all** auto-proxied beans in the context, not just aspects — they also govern `@Transactional`/`@Cacheable` proxies since those share the infrastructure.
- Why can't a JDK dynamic proxy advise a method that isn't declared on an interface?A JDK proxy is generated purely from the interfaces; the proxy instance doesn't extend the concrete class, so only interface-declared methods exist on it. Non-interface methods aren't visible to intercept — you need CGLIB (proxyTargetClass=true).
- Besides exposeProxy, name a cleaner way to fix self-invocation.Extract the advised method into a separate bean and inject it, self-inject the bean via ObjectProvider/@Autowired, or switch to AspectJ load-time/compile-time weaving which advises the actual object rather than a proxy.
saying these in an interview costs you the question
- Claiming self-invocation still triggers advice by default
- Saying CGLIB can proxy final classes/methods
- Thinking exposeProxy changes the proxy type
- Believing proxyTargetClass only affects @Aspect beans, not @Transactional