动态代理的原理及其应用

本文介绍了Java中的动态代理概念,包括JDK动态代理和CGLIB动态代理的原理与使用。JDK动态代理基于接口,适用于已实现接口的对象,而CGLIB通过生成目标类的子类实现代理,适用于普通类。文章还详细讲解了Proxy类和InvocationHandler接口的使用,并提供了示例代码帮助理解。
摘要由CSDN通过智能技术生成

目录

一.什么是动态代理?

二.CGLIB动态代理

三.jdk代理 

四.Proxy类

五.InvocationHandler接口 

六.案例 


一.什么是动态代理?

动态代理是一种在运行时生成代理对象的技术,用于替代原始对象执行操作,并且可以在方法调用前后进行额外的处理。通过使用反射机制,动态代理可以在不修改原始对象代码的情况下,为其添加额外的功能或逻辑,并提供更灵活的代理方式。

 

在Java中,有两种主要的动态代理方式:

  1. JDK动态代理(JDK Proxy):JDK动态代理是通过Java标准库中的Proxy类和InvocationHandler接口实现的。它只能代理接口类型的对象,通过在运行时动态生成代理类来实现代理功能。JDK动态代理是基于接口的代理,它利用反射机制在运行时生成代理类,并在代理类中实现对接口方法的调用转发。

  2. CGLIB动态代理:CGLIB(Code Generation Library)是一个强大的第三方库,它通过生成目标类的子类来实现代理。CGLIB动态代理可以代理普通类(非接口类型),并通过继承实现对目标类的代理。它会在运行时生成目标类的子类,并通过方法拦截器来控制方法的调用。

需要注意的是,使用JDK动态代理要求代理的目标对象实现接口,而使用CGLIB动态代理则不需要。一般而言,如果目标对象已实现接口,推荐使用JDK动态代理;如果目标对象未实现接口,或者需要对普通类进行代理,可以选择CGLIB动态代理。

选择合适的动态代理方式取决于具体的需求和场景。JDK动态代理简单易用,适用于对接口的代理;CGLIB动态代理更为强大,适用于对普通类的代理。根据实际情况,选择适合的代理方式可以帮助我们实现灵活和高效的代理功能。

二.CGLIB动态代理

CGLIB动态代理是一种基于继承的动态代理方式,使用CGLIB(Code Generation Library)库实现。与JDK动态代理不同,CGLIB动态代理可以代理普通类(非接口类型),并通过生成目标类的子类来实现代理。

使用CGLIB动态代理,我们通常需要以下步骤:

1. 引入CGLIB库:首先需要将CGLIB库添加到项目的依赖中。CGLIB库通常需要与对应的ASM库一起使用。

2. 创建Enhancer对象:Enhancer是CGLIB库中的主要类,用于生成代理类。我们可以通过创建Enhancer对象来配置代理的相关属性。

3. 设置父类和回调函数:通过调用Enhancer对象的setSuperclass()方法,可以设置需要代理的目标类作为父类。然后,通过调用setCallback()方法,可以设置一个MethodInterceptor接口的实现类作为回调函数,用于在方法调用时执行额外的逻辑。

4. 创建代理对象:通过调用Enhancer对象的create()方法,根据设置的父类和回调函数,生成代理类的实例对象。

下面是一个简单的示例代码:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class); // 设置父类
enhancer.setCallback(new MyMethodInterceptor()); // 设置回调函数

TargetClass proxy = (TargetClass) enhancer.create(); // 创建代理对象

在上述示例中,TargetClass是需要代理的目标类,MyMethodInterceptor是实现了MethodInterceptor接口的自定义类,用于在方法调用时执行额外的逻辑。

CGLIB动态代理基于继承,直接操作字节码生成代理类,因此性能较JDK动态代理更高。它也是一种强大的代理方式,适用于代理普通类和没有实现接口的类。然而,由于生成的代理类是目标类的子类,在某些特定情况下可能会有限制或不适用(例如,目标类被声明为final类)。在选择动态代理方式时,需要根据实际需求进行选择。

三.jdk代理 

JDK动态代理是Java标准库提供的一种动态代理机制,通过反射和InvocationHandler接口实现。它可以代理接口类型的对象,并在运行时动态生成代理类来实现代理功能。

使用JDK动态代理,我们需要以下步骤:

  1. 创建InvocationHandler对象:首先,我们需要实现InvocationHandler接口,该接口只有一个方法invoke(Object proxy, Method method, Object[] args),用于处理方法调用。在invoke()方法中,我们可以自定义对方法调用的处理逻辑,例如在方法调用前后添加额外的操作、修改参数等。

  2. 创建代理对象:通过调用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)方法,可以创建一个实现了指定接口数组的代理类的实例。参数包括类加载器、接口数组和InvocationHandler对象。

下面是一个简单的示例代码:

public class MyInvocationHandler implements InvocationHandler {
    private Object target;

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

    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) {
        RealObject realObject = new RealObject();
        InvocationHandler handler = new MyInvocationHandler(realObject);
        SomeInterface proxy = (SomeInterface) Proxy.newProxyInstance(
                SomeInterface.class.getClassLoader(),
                new Class[]{SomeInterface.class},
                handler
        );

        proxy.someMethod();
    }
}

在上述示例中,我们定义了一个接口SomeInterface和一个实现类RealObject。我们通过实现InvocationHandler接口来自定义对方法调用的处理逻辑并创建一个InvocationHandler对象handler。然后,使用Proxy.newProxyInstance()方法创建了一个代理对象proxy,该对象实现了SomeInterface接口,并在方法调用时会委托给handler处理。

JDK动态代理是一种轻量级的代理方式,由于是基于接口生成代理对象,因此对接口的代理非常方便。然而,它无法直接代理普通类,对目标对象的调用会涉及到反射和Method.invoke()的调用,因此性能相对较低。在选择是否使用JDK动态代理时,需要根据具体需求和性能要求进行权衡。

四.Proxy类

Proxy类是Java标准库中用于创建动态代理对象的工具类。它提供了静态方法来生成动态代理类和代理对象。

Proxy类的主要方法包括:

  1. newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler):该方法用于创建一个实现了指定接口数组的代理类的实例。参数包括类加载器、接口数组和InvocationHandler对象,其中InvocationHandler对象负责执行方法调用的处理。此方法返回的对象就是动态代理对象。

  2. isProxyClass(Class<?> clazz):该方法用于判断给定的类是否为由Proxy类生成的代理类。

  3. getProxyClass(ClassLoader loader, Class<?>... interfaces):该方法用于获取一个指定类加载器和接口数组所对应的动态代理类的Class对象。

Proxy类的使用方法通常是通过调用newProxyInstance()方法创建代理对象,然后将其转型为特定的接口类型,从而实现代理对象的使用。生成的代理对象会实现指定接口,并且在调用具体方法时会委托给InvocationHandler对象来处理。

需要注意的是,使用Proxy类创建的动态代理对象在运行时是通过反射以及动态生成的代理类来实现的。这些代理类在内存中只存在一次,并且可以多次使用。

Proxy类提供了一种简单而强大的方式来实现动态代理,可以在运行时基于接口生成代理对象,并在代理中添加自定义的处理逻辑。利用Proxy类,我们可以非常灵活地创建代理对象,实现横切关注点的集中处理、远程调用、日志记录等功能。

五.InvocationHandler接口 

InvocationHandler接口是Java反射机制中的一个核心接口,它用于实现动态代理的处理逻辑。

InvocationHandler接口定义了一个名为invoke()的方法,该方法在代理对象上执行方法调用时被调用。

InvocationHandler接口有以下方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

  • proxy:代理对象,即调用该方法的代理对象。
  • method:被调用的方法对象,即要执行的方法。
  • args:方法的参数数组。

在invoke()方法中,我们可以根据需要编写额外的逻辑,例如在方法执行前或执行后添加额外的处理,根据参数进行判断或转发等操作。在方法执行时,通过调用method.invoke()方法,可以将方法调用转发给实际的对象。

InvocationHandler接口通常与Proxy类一起使用,用于创建动态代理对象。通过实现InvocationHandler接口,并在其中编写代理逻辑,我们可以定制代理对象的行为,以实现对原始对象的访问控制和增加额外功能等需求。

使用InvocationHandler接口,我们可以非常灵活地控制代理对象的行为,并在不修改原始对象的情况下为其添加额外的功能。这种灵活性使得动态代理在许多场景中都有广泛的应用,例如AOP(面向切面编程)和远程调用等。

 

六.案例 

 

假设你要买一本书,但是你没有时间亲自去书店购买。那么你可以请一个代购人员帮你去书店购买,并将书送到你手中。

在这个比喻中,你是客户,原始的购买行为是亲自去书店购买,而代购人员则是代理。代购人员充当了你和书店之间的中间人角色,代替你执行购买书籍的操作。

这种情况下,代购人员可以具备一些额外的功能,如根据你的要求选择合适的书籍、比较价格、谈判折扣等。代购人员的存在可以减轻你的负担,节省你的时间和精力,使你能够更方便地购买到所需的物品。

这个比喻中的代购人员就是动态代理,他在运行时根据你的需求来执行购买行为,并可以提供额外的服务。你作为客户只需与代购人员进行交互,而无需直接参与购买过程,从而简化了你的操作流程。

以下是一个简单的Java代码示例,用于实现上述代购人员的代理购买行为:

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

interface BookStore {
    void buyBook(String bookName);
}

class RealBookStore implements BookStore {
    @Override
    public void buyBook(String bookName) {
        System.out.println("购买书籍: " + bookName);
    }
}

class ProxyBuyer implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeBuy(); // 购买前的额外服务
        Object result = method.invoke(target, args);
        afterBuy(); // 购买后的额外服务
        return result;
    }

    private void beforeBuy() {
        System.out.println("代理人员:根据要求选择合适的书籍");
        System.out.println("代理人员:比较价格和谈判折扣");
    }

    private void afterBuy() {
        System.out.println("代理人员:将书籍送到客户手中");
    }
}

public class ProxyBuyerExample {
    public static void main(String[] args) {
        BookStore realBookStore = new RealBookStore();

        // 创建动态代理对象
        BookStore proxyBuyer = (BookStore) Proxy.newProxyInstance(
            ProxyBuyerExample.class.getClassLoader(),
            new Class[]{BookStore.class},
            new ProxyBuyer(realBookStore)
        );

        // 调用代理对象的 buyBook() 方法
        proxyBuyer.buyBook("Java编程指南");
    }
}

在这个示例中,BookStore 接口定义了购买书籍的方法。RealBookStore 类是实际的书店,实现了 BookStore 接口,并实现了购买书籍的具体操作。

ProxyBuyer 类是代理购买人员,实现了 InvocationHandler 接口,并重写了 invoke() 方法,在购买前后添加了额外的服务。在 beforeBuy() 方法中,代理人员可以根据要求选择合适的书籍,并进行价格比较和折扣谈判。在 afterBuy() 方法中,代理人员将书籍送到客户手中。

在 ProxyBuyerExample 类中,我们创建了一个实际的书店对象 realBookStore,然后使用 Proxy.newProxyInstance() 方法,动态地生成代理购买人员对象 proxyBuyer。最后,通过调用代理对象的 buyBook() 方法,实际上会触发代理对象中的方法,并在方法执行前后执行额外的服务。

当执行该代码时,输出结果将展示代理人员根据要求选择书籍、比较价格和谈判折扣,然后调用实际的书店购买书籍,最后将书籍送到客户手中。这样,代理购买人员就完成了代替客户进行购买的任务。

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值