Backend Development 8 min read

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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Spring AOP Proxy Mechanisms: JDK vs CGLIB and Configuration Tips

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>&lt;aop:config proxy-target-class="true"&gt;&lt;/aop:config&gt;</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.

proxyAOPSpringSpring BootCglibJDK Proxyself-invocation
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.