Dynamically generate proxy classes in Java
- Tutorial
Probably every java developer will need to use proxy classes sooner or later.
Below the cat are simple examples made using the JDK proxy, cglib, javassist and byte buddy.

We set ourselves the simplest task:
- create proxy class for instance of User class
- in the proxy class you need to intercept a method called "getName"
- the output of the intercepted method should be in the upper case
public class User implements IUser {
private final String name;
public User() {
this(null);
}
public User(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
}1 Standard Tools - JDK proxy
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;User user = new User("Вася");
InvocationHandler handler = (proxy, method, args) -> {
if(method.getName().equals("getName")){
return ((String)method.invoke(user, args)).toUpperCase();
}
return method.invoke(user, args);
};
IUser userProxy = (IUser) Proxy.newProxyInstance(user.getClass().getClassLoader(), User.class.getInterfaces(), handler);
assertEquals("ВАСЯ", userProxy.getName());The disadvantage is that we can only create a proxy class that implements the interfaces of the User class. That is, you cannot cast a proxy class in User
2 cglib
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;User user = new User("Вася");
MethodInterceptor handler = (obj, method , args, proxy) -> {
if(method.getName().equals("getName")){
return ((String)proxy.invoke(user, args)).toUpperCase() ;
}
return proxy.invoke(user, args);
};
User userProxy = (User) Enhancer.create(User.class, handler);
assertEquals("ВАСЯ", userProxy.getName());3 javassist
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyObject;User user = new User("Вася");
MethodHandler handler = (self, overridden, forwarder, args) -> {
if(overridden.getName().equals("getName")){
return ((String)overridden.invoke(user, args)).toUpperCase();
}
return overridden.invoke(user, args);
};
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(User.class);
Object instance = factory.createClass().newInstance();
((ProxyObject) instance).setHandler(handler);
User userProxy = (User) instance;
assertEquals("ВАСЯ", userProxy.getName());4 Byte Buddy
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import static net.bytebuddy.matcher.ElementMatchers.named;User user = new User("Вася");
User userProxy = new ByteBuddy()
.subclass(User.class)
.method(named("getName"))
.intercept(MethodDelegation.to(new MyInterceptor(user)))
.make()
.load(User.class.getClassLoader())
.getLoaded()
.newInstance();
assertEquals("ВАСЯ", userProxy.getName());public class MyInterceptor {
User user;
public MyInterceptor(User user) {
this.user = user;
}
public String getName() {
return user.getName().toUpperCase();
}
}Productivity, simplicity, modernity - choose for yourself what suits your project more.
For performance comparison, I propose to read the article
Testing the performance of 4 Java runtime code generators: cglib, javassist, JDK proxy & Byte Buddy