Detailed Explanation of Dynamic Proxy Mechanism in Java

Detailed Explanation of Dynamic Proxy Mechanism in Java

I. Concept and Role of Dynamic Proxy
Dynamic proxy is a technique for dynamically creating proxy classes and objects at runtime. It allows you to add additional functionality (such as logging, permission checking, transaction management, etc.) to method calls of an object without modifying the original class code. The core value of dynamic proxy lies in achieving the separation of "cross-cutting concerns."

II. Implementation Methods of Dynamic Proxy
Java provides two main dynamic proxy implementation methods:

  1. JDK Dynamic Proxy

    • Based on interface implementation, requiring the target class to implement at least one interface.
    • Core classes: java.lang.reflect.Proxy and java.lang.reflect.InvocationHandler
  2. CGLIB Dynamic Proxy

    • Based on inheritance implementation, creating a proxy by generating a subclass of the target class.
    • Can proxy ordinary classes that do not implement interfaces.

III. Detailed Implementation Steps of JDK Dynamic Proxy

Step 1: Define Business Interface

// User Service Interface
public interface UserService {
    void addUser(String username);
    void deleteUser(String username);
}

Step 2: Implement Business Interface (Target Class)

// Target Class - Implementation of actual business logic
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        System.out.println("Add user: " + username);
    }
    
    @Override
    public void deleteUser(String username) {
        System.out.println("Delete user: " + username);
    }
}

Step 3: Implement the InvocationHandler Interface

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

public class LoggingHandler implements InvocationHandler {
    private final Object target;  // Target object
    
    public LoggingHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Pre-enhancement: Logic before method execution
        System.out.println("[Log] Start executing method: " + method.getName());
        System.out.println("[Log] Parameters: " + (args != null ? String.join(",", args) : "None"));
        
        long startTime = System.currentTimeMillis();
        
        // Call the original method of the target object
        Object result = method.invoke(target, args);
        
        // Post-enhancement: Logic after method execution
        long endTime = System.currentTimeMillis();
        System.out.println("[Log] Method execution time: " + (endTime - startTime) + "ms");
        System.out.println("[Log] Method execution completed");
        
        return result;
    }
}

Step 4: Create Proxy Object and Use It

import java.lang.reflect.Proxy;

public class DynamicProxyDemo {
    public static void main(String[] args) {
        // Create target object
        UserService target = new UserServiceImpl();
        
        // Create InvocationHandler instance
        InvocationHandler handler = new LoggingHandler(target);
        
        // Create proxy object
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),  // Class loader
            target.getClass().getInterfaces(),   // Interface array
            handler                              // Invocation handler
        );
        
        // Call methods through proxy object
        proxy.addUser("Zhang San");
        proxy.deleteUser("Li Si");
    }
}

IV. Implementation of CGLIB Dynamic Proxy

Step 1: Add CGLIB Dependency

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

Step 2: Implement the MethodInterceptor Interface

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

public class CglibLoggingInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("[CGLIB Log] Start executing method: " + method.getName());
        
        long startTime = System.currentTimeMillis();
        // Call the parent class (target class) method
        Object result = proxy.invokeSuper(obj, args);
        
        long endTime = System.currentTimeMillis();
        System.out.println("[CGLIB Log] Method execution time: " + (endTime - startTime) + "ms");
        
        return result;
    }
}

Step 3: Create Proxy Object

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        // Set parent class (target class)
        enhancer.setSuperclass(UserServiceImpl.class);
        // Set callback handler
        enhancer.setCallback(new CglibLoggingInterceptor());
        
        // Create proxy object
        UserService proxy = (UserService) enhancer.create();
        
        proxy.addUser("Wang Wu");
    }
}

V. Comparative Analysis of the Two Dynamic Proxies

Feature JDK Dynamic Proxy CGLIB Dynamic Proxy
Implementation Basis Interface-based Inheritance-based
Performance Calls reflective methods, slower Directly calls parent class methods, faster
Limitations Must implement an interface Cannot proxy final classes and methods
Dependencies Native JDK support Requires additional JAR

VI. Practical Application Scenarios of Dynamic Proxy

  1. Spring AOP: The Spring framework uses dynamic proxies to implement aspect-oriented programming.
  2. MyBatis: Proxy implementation of Mapper interfaces.
  3. Hibernate: Implementation of lazy loading functionality.
  4. RPC Framework: Client-side proxy for remote method calls.
  5. Transaction Management: Foundation for declarative transaction implementation.

VII. In-depth Understanding of Core Principles

The core of dynamic proxy lies in:

  • Proxy Class Generation: Dynamically generates the bytecode of the proxy class at runtime through bytecode technology.
  • Method Forwarding: All method calls are redirected to the invoke method of the InvocationHandler.
  • Chain of Responsibility Pattern: Implements the combination of multiple enhancement functions through a proxy chain.

Through dynamic proxies, we can achieve a complete separation of business logic and cross-cutting concerns, greatly improving code maintainability and reusability.