自学内容网 自学内容网

java的动态代理

Java 中的动态代理是一种设计模式,允许程序在运行时动态地生成代理类,而不是在编译时确定。它的主要作用是对接口的实现进行增强,使得我们可以在调用方法前后添加一些操作,比如日志、事务控制等。动态代理有两种常见的实现方式:

  1. JDK 动态代理:基于接口实现的代理,使用 java.lang.reflect.ProxyInvocationHandler
  2. CGLIB 动态代理:基于子类继承的代理,使用字节码生成,适用于没有接口的类。

以下是对这两种实现方式的原理和示例的详细介绍。


1. JDK 动态代理

JDK 动态代理基于 Java 的 Proxy 类和 InvocationHandler 接口来实现,只能代理实现了接口的类。其原理是通过生成代理类在调用方法时拦截,并由 InvocationHandler 中的 invoke() 方法处理。

实现原理
  • 代理类生成:在运行时,Proxy 类使用 Proxy.newProxyInstance() 方法生成代理对象。
  • 方法拦截:代理对象调用方法时会被 InvocationHandler 接口的 invoke() 方法拦截,invoke() 方法可以在调用前后插入逻辑。
  • 字节码生成:JDK 动态代理利用字节码生成技术动态生成代理类,在 JVM 中加载执行。
实现示例

假设我们有一个 Service 接口和它的实现类 ServiceImpl

public interface Service {
    void performTask();
}

public class ServiceImpl implements Service {
    public void performTask() {
        System.out.println("Executing task...");
    }
}

创建动态代理类

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

public class DynamicProxyHandler implements InvocationHandler {
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method invocation");
        Object result = method.invoke(target, args);
        System.out.println("After method invocation");
        return result;
    }

    public static void main(String[] args) {
        // 创建目标对象
        Service target = new ServiceImpl();

        // 创建代理对象
        Service proxy = (Service) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new DynamicProxyHandler(target));

        // 调用代理方法
        proxy.performTask();
    }
}

执行结果

Before method invocation
Executing task...
After method invocation
适用场景
  • 适用于代理接口类,不能代理普通类。
  • 常用于拦截器、权限控制、日志记录、事务管理等。

2. CGLIB 动态代理

CGLIB(Code Generation Library)是一种基于字节码生成的动态代理实现方式,能够代理普通类,适用于没有实现接口的类。CGLIB 动态代理通过生成目标类的子类,并重写其中的方法来实现。

实现原理
  • 子类继承:CGLIB 通过生成目标类的子类实现代理,目标类的方法被拦截。
  • 方法拦截:代理类调用方法时会被 MethodInterceptor 拦截,MethodInterceptor 可以在方法调用前后加入自定义逻辑。
  • 字节码增强:CGLIB 通过 ASM 字节码生成框架动态生成字节码,并在 JVM 中加载。
实现示例
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 CGLibProxy implements MethodInterceptor {
    private final Object target;

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

    public Object createProxy() {
        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 {
        System.out.println("Before method invocation");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("After method invocation");
        return result;
    }

    public static void main(String[] args) {
        // 目标对象
        ServiceImpl target = new ServiceImpl();

        // 创建代理对象
        ServiceImpl proxy = (ServiceImpl) new CGLibProxy(target).createProxy();

        // 调用代理方法
        proxy.performTask();
    }
}

执行结果

Before method invocation
Executing task...
After method invocation
适用场景
  • 适用于没有接口的普通类。
  • 由于 CGLIB 是通过继承实现的,所以无法代理 final 方法和 final 类。

JDK 动态代理和 CGLIB 的对比

特性JDK 动态代理CGLIB 动态代理
代理方式基于接口基于继承
性能较高,适合频繁调用的小方法较低,但适合复杂的调用
代理目标接口普通类(但不能代理 final 类和方法)
实现库Java 自带第三方库(cglibspring-core
常用场景AOP、拦截器等代理没有接口的类,AOP

总结

动态代理是一种重要的设计模式,允许在运行时动态增强对象的功能。JDK 动态代理适合代理接口,CGLIB 则适合没有接口的类。通过了解两者的原理和实现,可以灵活运用动态代理技术来增强 Java 应用的功能,如日志、事务管理、权限控制等。


原文地址:https://blog.csdn.net/qq_35861084/article/details/143477216

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!