Bootstrap

深入理解动态代理:JDK动态代理与CGLIB动态代理

引言

在软件开发中,动态代理是一种强大的技术,它允许在运行时创建代理对象,从而为原对象添加额外的功能。Java中主要有两种动态代理机制:JDK动态代理和CGLIB动态代理。本篇文章将详细介绍这两种动态代理的概念、实现方式、应用场景,并进行对比分析。

1. 什么是动态代理?

动态代理是一种在运行时创建代理对象的技术,通过代理对象来控制对原对象的访问,能够在不修改原对象的情况下添加额外的功能。动态代理主要有以下两种实现方式:

  • JDK动态代理:基于Java反射机制,只能代理实现了接口的类。
  • CGLIB动态代理:基于字节码生成技术,可以代理没有实现接口的类。

2. JDK动态代理

原理

JDK动态代理利用反射机制在运行时生成代理类,并通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现代理功能。它只能代理实现了接口的类。

示例代码

接口定义
// 抽象主题接口
public interface BusinessService {
    void performTask();
}
实现类
// 真实主题类
public class RealBusinessService implements BusinessService {
    @Override
    public void performTask() {
        System.out.println("Performing the main task...");
    }
}
动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 动态代理类
public class BusinessServiceProxy implements InvocationHandler {
    private Object target;

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

    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        logBefore();
        Object returnValue = method.invoke(target, args);
        logAfter();
        return returnValue;
    }

    private void logBefore() {
        System.out.println("Log before method execution");
    }

    private void logAfter() {
        System.out.println("Log after method execution");
    }
}
客户端代码
public class JDKDynamicProxyDemo {
    public static void main(String[] args) {
        RealBusinessService realBusinessService = new RealBusinessService();
        BusinessServiceProxy proxy = new BusinessServiceProxy(realBusinessService);
        BusinessService businessService = (BusinessService) proxy.getProxyInstance();

        // 调用方法前后会执行增强内容
        businessService.performTask();
    }
}

输出

Log before method execution
Performing the main task...
Log after method execution

3. CGLIB动态代理

原理

CGLIB(Code Generation Library)动态代理利用ASM字节码操作框架在运行时生成代理类,它通过继承的方式代理目标对象,因此可以代理没有实现接口的类。

示例代码

实现类
// 真实主题类
public class RealBusinessService {
    public void performTask() {
        System.out.println("Performing the main task...");
    }
}
动态代理类
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 动态代理类
public class BusinessServiceProxy implements MethodInterceptor {
    private Object target;

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

    public Object getProxyInstance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        logBefore();
        Object returnValue = proxy.invokeSuper(obj, args);
        logAfter();
        return returnValue;
    }

    private void logBefore() {
        System.out.println("Log before method execution");
    }

    private void logAfter() {
        System.out.println("Log after method execution");
    }
}
客户端代码
public class CGLIBDynamicProxyDemo {
    public static void main(String[] args) {
        RealBusinessService realBusinessService = new RealBusinessService();
        BusinessServiceProxy proxy = new BusinessServiceProxy(realBusinessService);
        RealBusinessService businessService = (RealBusinessService) proxy.getProxyInstance();

        // 调用方法前后会执行增强内容
        businessService.performTask();
    }
}

输出

Log before method execution
Performing the main task...
Log after method execution

4. JDK动态代理与CGLIB动态代理的比较

相同点

  • 动态生成代理类:两者都在运行时生成代理类,能够在不修改原对象的情况下添加额外功能。
  • 增强功能:两者都可以在方法调用前后添加增强功能,如日志记录、权限控制等。

不同点

特性JDK动态代理CGLIB动态代理
代理对象只能代理实现了接口的类可以代理没有实现接口的类
实现方式基于反射机制基于字节码生成
性能调用速度稍慢,因为使用了反射调用速度较快,但创建代理对象的开销较大
依赖无需额外依赖,JDK自带需要引入CGLIB库(如Spring框架自带)
继承方式实现接口的代理通过继承的方式代理目标类

选择建议

  • JDK动态代理:如果目标类已经实现了接口,并且希望保持简单的依赖关系,使用JDK动态代理是一个好选择。
  • CGLIB动态代理:如果目标类没有实现接口或者需要更高的性能,可以考虑使用CGLIB动态代理。

5. 总结

动态代理在Java中提供了一种灵活而强大的机制,能够在运行时为目标对象添加额外的功能。JDK动态代理和CGLIB动态代理各有优势和适用场景,开发者可以根据具体需求选择合适的代理机制。

希望这篇文章对你理解动态代理有所帮助。如果觉得本文内容有价值,请点赞、收藏和关注我们,获取更多设计模式的精彩内容!

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;