Backend Development 12 min read

How CGLIB Generates and Executes Proxy Classes in Java

This article explains how CGLIB creates proxy classes, decompiles them, sets thread callbacks, and uses MethodInterceptor and FastClass to efficiently intercept and invoke methods in a Java 8 environment, complete with code examples and diagram illustrations.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How CGLIB Generates and Executes Proxy Classes in Java

Environment: Java 8

Using CGLIB Proxy

<code>System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cglib");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonDAOImpl.class);
enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("执行前...");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("执行后...");
        return result;
    }
});
PersonDAOImpl dao = (PersonDAOImpl) enhancer.create();
dao.save(new Person());
</code>

The System.setProperty line tells CGLIB to write generated proxy classes to the specified directory.

Decompiling Proxy Class

The decompilation tool used is Luyten.

Only the important code is shown due to size.

<code>public class PersonDAOImpl$EnhancerByCGLIB$6f07c3f7 extends PersonDAOImpl implements Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$save$0$Method;
    private static final MethodProxy CGLIB$save$0$Proxy;
    // ... other generated fields and methods ...

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        final Class<?> forName = Class.forName("com.pack.dao.PersonDAOImpl$EnhancerByCGLIB$6f07c3f7");
        final Class<?> forName2 = Class.forName("java.lang.Object");
        final Method[] methods = ReflectUtils.findMethods(new String[] {"equals","(Ljava/lang/Object;)Z","toString","()Ljava/lang/String;","hashCode","()I","clone","()Ljava/lang/Object;"}, forName2.getDeclaredMethods());
        CGLIB$equals$1$Method = methods[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(forName2, forName, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        // ... similar code for toString, hashCode, clone ...
        final Class<?> forName3 = Class.forName("com.pack.dao.PersonDAOImpl");
        CGLIB$save$0$Method = ReflectUtils.findMethods(new String[] {"save","(Lcom/pack/anno/config/Person;)V"}, forName3.getDeclaredMethods())[0];
        CGLIB$save$0$Proxy = MethodProxy.create(forName3, forName, "(Lcom/pack/anno/config/Person;)V", "save", "CGLIB$save$0");
    }

    final void CGLIB$save$0(final Person person) {
        super.save(person);
    }

    public final void save(final Person person) {
        MethodInterceptor cglib$CALLBACK_2;
        MethodInterceptor cglib$CALLBACK_0;
        if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
            CGLIB$BIND_CALLBACKS(this);
            cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
        }
        if (cglib$CALLBACK_0 != null) {
            cglib$CALLBACK_2.intercept(this, CGLIB$save$0$Method, new Object[]{person}, CGLIB$save$0$Proxy);
            return;
        }
        super.save(person);
    }

    public PersonDAOImpl$EnhancerByCGLIB$6f07c3f7() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(final Callback[] array) {
        PersonDAOImpl$EnhancerByCGLIB$6f07c3f7.CGLIB$THREAD_CALLBACKS.set(array);
    }

    private static final void CGLIB$BIND_CALLBACKS(final Object o) {
        PersonDAOImpl$EnhancerByCGLIB$6f07c3f7 instance = (PersonDAOImpl$EnhancerByCGLIB$6f07c3f7) o;
        if (!instance.CGLIB$BOUND) {
            instance.CGLIB$BOUND = true;
            Object o2;
            if ((o2 = PersonDAOImpl$EnhancerByCGLIB$6f07c3f7.CGLIB$THREAD_CALLBACKS.get()) != null || (o2 = PersonDAOImpl$EnhancerByCGLIB$6f07c3f7.CGLIB$STATIC_CALLBACKS) != null) {
                instance.CGLIB$CALLBACK_0 = (MethodInterceptor) ((Callback[]) o2)[0];
            }
        }
    }

    static { CGLIB$STATICHOOK1(); }
}
</code>

The generated proxy class extends PersonDAOImpl; for each method in PersonDAOImpl, CGLIB creates a direct super‑call method (e.g., CGLIB$save$0) and a proxy method that uses a Callback.

When the save method is invoked, it first checks whether CGLIB$CALLBACK_0 is null; if so, it runs initialization to bind callbacks.

Setting CGLIB$THREAD_CALLBACKS Value

During enhancer.create(), reflection invokes the generated class's CGLIB$SET_THREAD_CALLBACKS method, which stores the Callback array in a ThreadLocal field.

<code>public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
    setThreadCallbacks(callbacks);
    try {
        if (primaryConstructorArgTypes == argumentTypes || Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
            return ReflectUtils.newInstance(primaryConstructor, arguments);
        }
        return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
    } finally {
        setThreadCallbacks(null);
    }
}
</code>

The setThreadCallbacks line triggers the CGLIB$SET_THREAD_CALLBACKS method in the proxy class.

<code>private void setThreadCallbacks(Callback[] callbacks) {
    try {
        setThreadCallbacks.invoke(generatedClass, (Object) callbacks);
    } catch (IllegalAccessException e) {
        throw new CodeGenerationException(e);
    } catch (InvocationTargetException e) {
        throw new CodeGenerationException(e.getTargetException());
    }
}
</code>

After the callback is set, the proxy's save method executes the interceptor's intercept method:

<code>cglib$CALLBACK_2.intercept(this, CGLIB$save$0$Method, new Object[]{person}, CGLIB$save$0$Proxy);
</code>

Execution of intercept Method in MethodInterceptor

<code>@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    System.out.println("执行前...");
    Object result = proxy.invokeSuper(obj, args);
    System.out.println("执行后...");
    return result;
}
</code>

The MethodProxy object is created in the static block of the generated class:

<code>CGLIB$save$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Lcom/pack/anno/config/Person;)V", "save", "CGLIB$save$0");
</code>

MethodProxy.create builds a MethodProxy with signatures for the target and proxy methods.

<code>public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
    MethodProxy proxy = new MethodProxy();
    proxy.sig1 = new Signature(name1, desc);
    proxy.sig2 = new Signature(name2, desc);
    proxy.createInfo = new CreateInfo(c1, c2);
    return proxy;
}
</code>

The invokeSuper method uses a FastClass to call the target method efficiently, avoiding reflection overhead:

<code>public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        init();
        FastClassInfo fci = fastClassInfo;
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    }
}
</code>

The init method lazily creates FastClassInfo objects that map method signatures to index values, enabling direct method invocation.

<code>private void init() {
    if (fastClassInfo == null) {
        synchronized (initLock) {
            if (fastClassInfo == null) {
                CreateInfo ci = createInfo;
                FastClassInfo fci = new FastClassInfo();
                fci.f1 = helper(ci, ci.c1);
                fci.f2 = helper(ci, ci.c2);
                fci.i1 = fci.f1.getIndex(sig1);
                fci.i2 = fci.f2.getIndex(sig2);
                fastClassInfo = fci;
                createInfo = null;
            }
        }
    }
}
</code>

FastClass is a generated class that provides fast method dispatch by using the pre‑computed index.

Thus, CGLIB generates three classes—the proxy class, its FastClass, and the FastClass for the target—enabling efficient method interception.

javaProxyReflectionCGLIBMethodInterceptorFastClass
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.