Mastering Java Dynamic Proxies: JDK vs CGLIB Implementation and Pitfalls

This article explains the concepts, advantages, and limitations of Java's JDK dynamic proxy and CGLIB proxy, provides step‑by‑step code examples for user registration validation, compares their mechanisms, and summarizes when to choose each approach in real‑world applications.

Programmer DD
Programmer DD
Programmer DD
Mastering Java Dynamic Proxies: JDK vs CGLIB Implementation and Pitfalls

Preface

Dynamic proxies are widely used in open‑source frameworks such as Spring AOP and MyBatis. They rely on reflection and bytecode manipulation to create subclasses or interface implementations at runtime, enabling non‑intrusive code enhancement.

1. JDK Dynamic Proxy

1.1 Overview

The JDK implementation consists of two key components: Proxy and InvocationHandler.

Proxy : the superclass of all dynamic proxies, providing a static method to create proxy class objects and instances.

InvocationHandler : each proxy instance holds an InvocationHandler; method calls on the proxy are forwarded to the handler's invoke method.

1.2 Diagram

JDK proxy diagram
JDK proxy diagram

1.3 Code Example

We simulate a user registration service where the proxy validates username and password length before proceeding.

package com.taolong;

public class User {
    private String name;
    private Integer age;
    private String password;
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + ", password=" + password + "]";
    }
}
package com.taolong.jdk;

import com.taolong.User;

public interface UserService {
    void addUser(User user);
}
package com.taolong.jdk;

import com.taolong.User;

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(User user) {
        System.out.println("jdk...正在注册用户,用户信息为:" + user);
    }
}
package com.taolong.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import com.taolong.User;

public class UserServiceInterceptor implements InvocationHandler {
    private Object realObj;
    public UserServiceInterceptor(Object realObject) {
        this.realObj = realObject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (args != null && args.length > 0 && args[0] instanceof User) {
            User user = (User) args[0];
            if (user.getName().length() <= 1) {
                throw new RuntimeException("用户名长度必须大于1");
            }
            if (user.getPassword().length() <= 6) {
                throw new RuntimeException("密码长度必须大于6");
            }
        }
        Object result = method.invoke(realObj, args);
        System.out.println("用户注册成功...");
        return result;
    }
}
package com.taolong.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import com.taolong.User;

public class ClientTest {
    public static void main(String[] args) {
        User user = new User();
        user.setName("hongtaolong");
        user.setPassword("hong");
        user.setAge(23);
        UserService delegate = new UserServiceImpl();
        InvocationHandler handler = new UserServiceInterceptor(delegate);
        UserService proxy = (UserService) Proxy.newProxyInstance(
                delegate.getClass().getClassLoader(),
                delegate.getClass().getInterfaces(),
                handler);
        System.out.println("动态代理类:" + proxy.getClass());
        proxy.addUser(user);
    }
}

When the password length is less than 6, the proxy throws an exception, demonstrating dynamic enhancement.

JDK proxy error result
JDK proxy error result

2. CGLIB Dynamic Proxy

2.1 Overview

CGLIB (Code Generation Library) is built on ASM and generates bytecode at runtime. It creates proxies by subclassing the target class, which means it can proxy classes without interfaces but cannot intercept final methods.

CGLIB also relies on two main components: Enhancer and MethodInterceptor, analogous to Proxy and InvocationHandler in the JDK approach.

Enhancer : specifies the target class and creates the proxy via create(). All non‑final method calls are delegated to the MethodInterceptor.

MethodInterceptor : intercepts method calls and performs enhancement in its intercept method.

2.2 Diagram

CGLIB proxy diagram
CGLIB proxy diagram

2.3 Code Example

package com.taolong.cglib;

import java.lang.reflect.Method;
import com.taolong.User;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class UserServiceCglibInterceptor implements MethodInterceptor {
    private Object realObject;
    public UserServiceCglibInterceptor(Object realObject) {
        this.realObject = realObject;
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        if (args != null && args.length > 0 && args[0] instanceof User) {
            User user = (User) args[0];
            if (user.getName().length() <= 1) {
                throw new RuntimeException("用户名长度必须大于1");
            }
            if (user.getPassword().length() <= 6) {
                throw new RuntimeException("密码长度必须大于6");
            }
        }
        Object result = method.invoke(realObject, args);
        System.out.println("用户注册成功...");
        return result;
    }
}
package com.taolong.cglib;

import com.taolong.User;

public class UserServiceImplCglib {
    final void addUser(User user) {
        System.out.println("cglib...正在注册用户,用户信息为:" + user);
    }
}
package com.taolong.cglib;

import com.taolong.User;
import net.sf.cglib.proxy.Enhancer;

public class ClientTest {
    public static void main(String[] args) {
        User user = new User();
        user.setName("hongtaolong");
        user.setPassword("hong");
        user.setAge(23);
        UserServiceImplCglib delegate = new UserServiceImplCglib();
        UserServiceCglibInterceptor interceptor = new UserServiceCglibInterceptor(delegate);
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(delegate.getClass());
        enhancer.setCallback(interceptor);
        UserServiceImplCglib cglibProxy = (UserServiceImplCglib) enhancer.create();
        System.out.println("动态代理类父类:" + cglibProxy.getClass().getSuperclass());
        cglibProxy.addUser(user);
    }
}

Running the program shows that the proxy class’s superclass is UserServiceImplCglib. When the addUser method is declared final, CGLIB cannot intercept it, confirming its limitation.

CGLIB final method result
CGLIB final method result

Summary

1. JDK dynamic proxy is built‑in, requires no external dependencies, but can only proxy interfaces because it extends Proxy and Java does not support multiple inheritance.

2. CGLIB creates proxies by subclassing, works for classes without interfaces, but cannot enhance final methods.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaaopbytecodeReflectionJDKDynamic Proxycglib
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

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.