什么是动态代理?

什么是动态代理?

动态代理是一种设计模式,它允许在运行时创建一个代理对象,该代理对象能够代替另一个对象进行方法调用。它是通过使用反射机制来实现的。

在动态代理中,有两个主要角色:代理对象和目标对象。代理对象作为目标对象的代表,对外提供与目标对象相同的接口,同时对目标对象的方法调用进行了封装和控制。

当调用代理对象的方法时,实际执行的是代理对象的处理逻辑。代理对象可以在方法执行前后执行额外的逻辑,比如记录日志、权限验证、缓存等。然后,代理对象将方法调用转发给目标对象进行实际的处理。

动态代理的好处是可以在不修改目标对象的情况下,提供额外的功能。它可以实现横切关注点的解耦,提高代码的灵活性和可维护性。

在Java中,动态代理可以使用java.lang.reflect.Proxy类来创建。该类提供了一个静态方法newProxyInstance(),可以根据指定的类加载器、目标接口和调用处理器动态生成代理对象。调用处理器负责拦截代理对象的方法调用,执行额外的逻辑,并将方法调用转发给目标对象。**

两种常用的动态代理方式

选择使用哪种动态代理方式取决于实际情况。如果目标对象已经实现了接口,且希望进行接口层面的代理,那么可以选择JDK动态代理。如果目标对象没有实现接口,或者需要代理的是类而不是接口,那么可以选择CGLib动态代理。需要注意的是,CGLib动态代理对目标类的访问性有一些限制,比如无法代理被声明为final的类。

1. JDK动态代理:

JDK动态代理是基于接口的代理机制,使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来创建代理对象。它要求目标对象必须实现一个或多个接口。JDK动态代理通过生成一个实现目标接口的代理类来实现代理功能。它适用于对接口进行代理,并且能够方便地进行使用。

JDK动态代理是一种基于接口的代理机制,它使用了java.lang.reflect包中的Proxy类和InvocationHandler接口来创建代理对象。

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

// 定义一个接口
interface MyInterface {
    void doSomething();
}

// 实现接口的目标对象
class MyTarget implements MyInterface {
    @Override
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

// 自定义的调用处理器
class MyInvocationHandler implements InvocationHandler {
    private Object target; // 目标对象

    public MyInvocationHandler(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 class Main {
    public static void main(String[] args) {
        MyTarget target = new MyTarget(); // 实例化目标对象

        MyInvocationHandler invocationHandler = new MyInvocationHandler(target); // 创建调用处理器

        // 创建代理对象
        MyInterface proxyObject = (MyInterface) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 类加载器
                new Class[]{MyInterface.class}, // 目标对象实现的接口数组
                invocationHandler
        );

        proxyObject.doSomething(); // 使用代理对象调用方法
    }
}

2. CGLib动态代理:

CGLib动态代理是基于类的代理机制,使用CGLib库来生成代理对象。相较于JDK动态代理,它不存在目标对象必须实现接口的限制,可以直接对类进行代理。CGLib动态代理通过生成目标类的子类来实现代理功能。它适用于对类进行代理,并且能够实现更高性能的代理。

首先,我们需要在项目中添加CGLib库的依赖。可以使用Maven或Gradle等构建工具来添加依赖。

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 目标对象
class MyTarget {
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

// 自定义的方法拦截器
class MyMethodInterceptor implements MethodInterceptor {
    @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 class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MyTarget.class); // 设置目标对象的父类
        enhancer.setCallback(new MyMethodInterceptor()); // 设置方法拦截器

        MyTarget proxyObject = (MyTarget) enhancer.create(); // 创建代理对象

        proxyObject.doSomething(); // 使用代理对象调用方法
    }
}

需要注意的是,CGLib动态代理是通过继承目标对象的方式实现的,因此如果目标对象被声明为final,则无法生成代理对象。另外,CGLib动态代理相对于JDK动态代理而言,生成代理对象的过程较为耗时,所以在大量代理对象的情况下,性能可能会受到一定影响。

(1)CGLib 的实现步骤:

CGLib是一个强大的代码生成库,用于实现动态代理。

  1. 创建Enhancer对象:Enhancer是CGLib库用于生成代理类的核心类。通过创建Enhancer对象,我们可以设置代理类的父类、回调方法、过滤器等属性。
Enhancer enhancer = new Enhancer();
  1. 设置父类:通过setSuperclass()方法设置代理类的父类,即需要被代理的类。
enhancer.setSuperclass(MyTarget.class);
  1. 设置回调方法:CGLib使用回调方法(Callback)来拦截对代理类方法的调用。可以实现MethodInterceptor接口作为回调方法。
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    // 在方法调用前可以执行预处理逻辑
    System.out.println("Before method invocation...");

    Object result = proxy.invokeSuper(obj, args); // 调用目标对象的方法

    // 在方法调用后可以执行后处理逻辑
    System.out.println("After method invocation...");

    return result;
});
  1. 创建代理对象:通过create()方法创建代理对象。
MyTarget proxyObject = (MyTarget) enhancer.create();
  1. 使用代理对象调用方法:通过代理对象调用目标方法。
proxyObject.someMethod();

在这个过程中,CGLib使用ASM字节码操作库来分析和修改字节码,生成新的类作为代理类。代理类继承自父类,并重写了需要代理的方法,在重写的方法中添加了拦截器的处理逻辑。

需要注意的是,为了使用CGLib实现动态代理,目标类不能被声明为final,否则无法生成代理类。另外,由于CGLib是通过继承来创建代理类的,因此代理类和目标类必须处于同一个包中或者目标类的方法为protected,以保证继承的可见性。

(2)CGLib 的底层原理:

GLib(Code Generation Library)是一个强大的,高性能的代码生成库,它基于ASM(一个字节码操作库)来操作字节码并生成新的类。CGLib底层原理主要依靠ASM来分析和改写字节码来生成代理类。

CGLib动态代理的核心是通过继承目标类,并重写其中的方法来实现代理。

  1. 通过Enhancer来创建代理对象。Enhancer是CGLib库中的一个关键类,用于生成代理对象。通过设置父类、回调、过滤器等参数,创建代理对象的模板。
  2. 使用CallBack拦截方法调用。CallBack是CGLib中的一个接口,定义了方法拦截器的行为。在创建代理对象时,我们需要指定一个CallBack来拦截目标方法的调用。常用的CallBack有MethodInterceptor、FixedValue等,分别用于拦截方法调用和固定返回值。
  3. CGLib通过生成新的类来实现代理。在生成代理类时,CGLib会在运行时通过ASM库操作字节码,根据父类的信息以及拦截方法的逻辑生成一个新的类。该类继承自父类,并重写了被代理方法。
  4. 通过创建代理对象调用目标方法。生成代理类以后,我们可以通过其实例化一个代理对象。当我们通过代理对象调用被代理类的方法时,CGLib会拦截这个方法的调用,并在内部调用拦截器的逻辑。

总结来说,CGLib动态代理通过继承和重写的方式,在运行时动态生成新的类,并将目标类的方法调用委托给拦截器进行处理。相对于JDK动态代理,CGLib动态代理在生成代理类时不需要强制目标对象实现接口,而是直接继承目标类,因此更加灵活。不过,由于CGLib是通过操作字节码实现代理,所以在性能上相对于JDK动态代理会略有开销。

3.JDK,CGLib两者的区别:

DK动态代理和CGLib动态代理是两种常见的实现动态代理的方式,它们在实现原理、使用方式和适用场景上有一些区别。

JDK动态代理:

  • 实现原理:JDK动态代理是基于接口的代理机制,使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来创建代理对象。它要求目标对象必须实现一个或多个接口。JDK动态代理通过生成一个实现目标接口的代理类来实现代理功能。
  • 使用方式:JDK动态代理通过Proxy.newProxyInstance()方法创建代理对象,并将目标对象和一个实现InvocationHandler接口的对象传入。InvocationHandler对象实现了对代理方法的拦截和处理逻辑。
  • 适用场景:适用于对接口进行代理,能够方便地进行使用。在原始类实现了接口的情况下,可以使用JDK动态代理来实现代理功能。

CGLib动态代理:

  • 实现原理:CGLib动态代理是基于类的代理机制,使用CGLib库来生成代理对象。它通过继承目标类并重写方法来实现代理功能,无需目标对象实现接口。
  • 使用方式:CGLib动态代理通过创建Enhancer对象,并设置父类、回调方法等属性来创建代理对象。回调方法作为拦截器来处理方法调用。
  • 适用场景:适用于对类进行代理,可以代理没有实现接口的类。对于无法修改源代码的类,CGLib动态代理提供了一种可行的代理方案。

区别总结如下:

  1. 实现原理:JDK动态代理基于接口,创建实现目标接口的代理类;CGLib动态代理基于类,创建继承目标类的代理子类。
  2. 接口要求:JDK动态代理要求目标对象实现接口;CGLib动态代理不要求目标对象实现接口。
  3. 性能表现:JDK动态代理生成的代理类是在运行时动态生成的,性能较好;CGLib动态代理生成的代理类是通过字节码生成,生成过程相对较慢,但在方法调用时性能较高。
  4. 使用方式:JDK动态代理使用Proxy.newProxyInstance()方法创建代理对象;CGLib动态代理使用Enhancer类创建代理对象。
  5. 适用场景:JDK动态代理适用于对接口进行代理;CGLib动态代理适用于对类进行代理,尤其是对没有实现接口的类的代理。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值