Mastering Spring AOP Proxy Mechanisms: JDK vs CGLIB and Configuration Tips
This article explains how Spring AOP creates proxies using JDK dynamic proxies or CGLIB, outlines the limitations of each approach, and shows multiple ways—including XML, YAML, and annotations—to force CGLIB usage and resolve self‑invocation issues in Spring Boot applications.
Environment: Spring Boot 3.2.5
1. Proxy Mechanism
Spring AOP creates proxies for target objects using either JDK dynamic proxies or CGLIB. If the target implements at least one interface, JDK dynamic proxy is used; otherwise, a CGLIB subclass is generated.
When forcing CGLIB, consider the following constraints:
Classes declared final cannot be proxied.
Methods declared final cannot be advised.
Private methods cannot be enhanced.
Package‑private methods from a different package behave like private methods.
CGLIB proxies are created via Objenesis, which bypasses constructors; JVM restrictions may cause constructor calls to appear twice.
Java module system restrictions may prevent creating CGLIB proxies for classes in java.lang without the --add-opens flag.
To force CGLIB, set proxy-target-class to true :
<code><aop:config proxy-target-class="true"></aop:config></code> <code>spring:
aop:
proxy-target-class: true</code> <code>@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {}</code>When both XML and annotation configurations are present, the annotation takes precedence.
2. Understanding AOP Proxies
Spring AOP works through proxies. A client that holds a proxy reference invokes methods on the proxy, which then delegates to interceptors before reaching the target object. Self‑invocation (calling this.method() ) bypasses the proxy, so advice is not applied.
Example of a plain object:
<code>public class SimplePojo implements Pojo {
public void foo() {
this.bar();
}
public void bar() {}
}</code>Calling foo() on a direct instance invokes bar() directly.
When using a proxy:
<code>public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
Pojo pojo = (Pojo) factory.getProxy();
pojo.foo();
}
}</code>Here the client holds a proxy, so method calls go through the proxy and its advice.
To address self‑invocation, you can:
Avoid self‑calls by refactoring code.
Inject the bean into itself and call the injected reference instead of this .
Use AopContext.currentProxy() (not recommended because it tightly couples code to Spring AOP). Example:
<code>public class SimplePojo implements Pojo {
public void foo() {
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {}
}</code>To enable AopContext.currentProxy() , expose the proxy:
<code>factory.setExposeProxy(true);</code> <code>@EnableAspectJAutoProxy(exposeProxy = true)
public class AppConfig {}</code>These techniques ensure that advice is applied even when a method calls another method within the same class.
Understanding these proxy mechanics is essential for avoiding common pitfalls when using Spring AOP.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.