CGLIB proxying vs. regular Java proxying in Java

In Java, there are two commonly used approaches for creating proxies: CGLIB proxying and regular Java proxying. Both methods allow for dynamic generation of proxy objects, which can intercept method calls and perform additional logic before or after the method is invoked.

CGLIB Proxying

CGLIB (Code Generation Library) is a powerful dynamic bytecode generation library that allows the creation of proxy objects at runtime. It is often used when implementing method-level interception in Java frameworks like Spring.

CGLIB proxies work by subclassing the target class and overriding non-final methods to add the desired behavior. This approach allows for seamless interception of method calls, as the proxy object extends the target class and can intercept all method invocations.

To create a CGLIB proxy, you typically use a library like Spring or expose a bean as a proxy through configuration. For example, in Spring, you can use the @EnableAspectJAutoProxy annotation to enable CGLIB proxying for classes annotated with @Aspect.

CGLIB proxying has some advantages over regular Java proxying, such as:

However, CGLIB proxying also has some limitations to be aware of:

Regular Java Proxying

Regular Java proxying is based on the java.lang.reflect.Proxy class, which is a built-in Java API for creating dynamic proxies. This approach is most commonly used when working with interface-based abstractions and is supported by the java.lang.reflect.InvocationHandler interface.

Regular Java proxies work by implementing the target interface and delegating method invocations to an implementation of the InvocationHandler interface. The InvocationHandler intercepts method calls and performs additional logic before or after invoking the actual target method.

To create a regular Java proxy, you need to use the Proxy.newProxyInstance() method and provide an implementation of the InvocationHandler interface. This approach allows you to define custom behavior for the proxy object.

Regular Java proxying has some advantages over CGLIB proxying, such as:

However, regular Java proxying also has limitations:

Conclusion

Both CGLIB proxying and regular Java proxying offer dynamic generation of proxy objects in Java. The choice between them depends on the specific requirements and constraints of your project. If you need to proxy concrete classes, CGLIB proxying is a suitable option, while regular Java proxying works well for interface-based abstractions. Consider the advantages and limitations of each approach to make an informed decision for your application.

// Example code for creating a CGLIB proxy with Spring

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class MyInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // Perform additional logic before invoking the method
        System.out.println("Before method invocation");

        // Invoke the actual target method
        Object result = proxy.invokeSuper(obj, args);

        // Perform additional logic after invoking the method
        System.out.println("After method invocation");

        return result;
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MyService.class);
        enhancer.setCallback(new MyInterceptor());

        MyService proxy = (MyService) enhancer.create();

        // Invoke a method on the proxy object
        proxy.doSomething();
    }
}
// Example code for creating a regular Java proxy

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler {

    private final Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Perform additional logic before invoking the method
        System.out.println("Before method invocation");

        // Invoke the actual target method
        Object result = method.invoke(target, args);

        // Perform additional logic after invoking the method
        System.out.println("After method invocation");

        return result;
    }

    public static void main(String[] args) {
        MyService target = new MyService();
        MyInvocationHandler handler = new MyInvocationHandler(target);

        MyService proxy = (MyService) Proxy.newProxyInstance(
                MyService.class.getClassLoader(),
                new Class<?>[]{MyService.class},
                handler);

        // Invoke a method on the proxy object
        proxy.doSomething();
    }
}