【Spring篇】JDK动态代理

目录

什么是代理?

代理模式

动态代理

Java中常用的代理模式

问题来了,如何动态生成代理类?

动态代理底层实现


什么是代理?

顾名思义,代替某个对象去处理一些问题,谓之代理,那么何为动态?即让JVM虚拟机去完成而非程序员去完成(与静态对比),连起来就是让虚拟机去动态的创建一个对象去代替另一个对象完成某些业务需求

呢么其中就涉及到了两个对象,代理类目标类;代理类又被前辈归纳成代理模式,下面看代理模式;

代理模式

代理模式是GoF23种设计模式之一。属于结构型设计模式。

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不应该看到的内容和服务或者添加客户需要的额外服务。 通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式,通过引入代理对象来间接访问一个对象,这就是代理模式的模式动机。

代理模式中的角色:

  • 代理类(代理主题)
  • 目标类(真实主题)
  • 代理类和目标类的公共接口(抽象主题):客户端在使用代理类时就像在使用目标类,不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。

为什么要有个公共接口呢?

呢你代理类要代替目标类完成某些操作,你是不是就需要拥有目标类该有的功能,如何拥有这个类拥有的功能呢?一、代理类继承目标类(但是继承太死板,耦合度太高,而且当需求量上去后,你是不是要写n个子类,不明智!!!)二、通过实现共同接口,实现必要方法,nice!!!,完美解决;

 知道基本概念后,我们完善一下,用更加官方的话来表示。。。

动态代理

在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。

Java中常用的代理模式

  • jdk动态代理;
  • cglib静态代理;
  • Javassist动态代理技术;

 我们主要分析JDK代理技术

JDK动态代理(理解):使用java反射包中的类和接口实现动态代理的功能,反射包java.lang.reflect,里面有三个类:InvocationHandler,Method,Proxy


 |                                        下面我们通过一个实际业务需求来分析动态代理                                   |


假设某项目已上线,并且运行正常,只是客户反馈系统有一些地方运行较慢,要求项目组对系统进行优化。于是项目负责人就下达了这个需求。首先需要搞清楚是哪些业务方法耗时较长,于是让我们统计每个业务方法所耗费的时长。你坑定不能直接在已经上线并且运行很好的项目的源代码上操作啊,于是乎我们采用静态代理方法,代码如下:

// 目标接口
public interface OrderService {
    void add();

    void update();

    void delete();
}
// 目标类
public class OrderServiceImpl implements OrderService {
    @Override
    public void add() {
        System.out.println("添加用户");
    }

    @Override
    public void update() {
        System.out.println("修改用户");
    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }
}
// 代理类
public class OrderServiceProxy implements OrderService {
    
    @Override
    public void add() {
        long begin = System.currentTimeMillis();
        OrderService.add();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");

    }

    @Override
    public void update() {
        long begin = System.currentTimeMillis();
        OrderService.update();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");

    }

    @Override
    public void delete() {
        long begin = System.currentTimeMillis();
        OrderService.delete();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");

    }
}

在上面的静态代理方法中,代理类和目标类都实现同一个接口,在代理类中维护目标类对象,并完成对目标类对象方法的增强,这种方式虽然遵循开闭原则,但是代理类和目标类至少是“一对一”的绑定关系,如果需要被代理的目标类个数越多,代理类就会越多,会产生大量重复的代码,也不利于后期的维护。

于是乎我们使用动态代理技术:目前代码是这样的,需要我们动态生成一个代理类:

// 目标接口
public interfaceOrderService {
    void add();

    void update();

    void delete();
}
// 目标类
public class OrderServiceImpl implements OrderService {
    @Override
    public void add() {
        System.out.println("添加用户");
    }

    @Override
    public void update() {
        System.out.println("修改用户");
    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }
}

问题来了,如何动态生成代理类?

回归到文章中心题目,JDK动态代理技术

在JDK中,有一个Proxy类,Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态的生成实现类。这个类有一个静态方法:newProxyInstance()方法。这个方法的目的就是给我们的目标对象返回一个代理对象。

我们可以看到其中newProxyInstance()方法有三个参数,下面分析三个参数:

  • 第一个参数:类加载器。在内存中生成了字节码,要想执行这个字节码,也是需要先把这个字节码加载到内存当中的。所以要指定使用哪个类加载器加载。
  • 第二个参数:接口类型。代理类和目标类实现相同的接口,所以要通过这个参数告诉JDK动态代理生成的类要实现哪些接口。
  • 第三个参数:调用处理器。这是一个JDK动态代理规定的接口,接口全名:java.lang.reflect.InvocationHandler。显然这是一个回调接口,也就是说调用这个接口中方法的程序已经写好了,就差这个接口的实现类了。

所以接下来我们要写一下java.lang.reflect.InvocationHandler接口的实现类,并且实现接口中的方法,代码如下:

public class TimerInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }
}

InvocationHandler接口中有一个方法invoke,这个invoke方法上有三个参数:

  • 第一个参数:Object proxy。代理对象。设计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用。
  • 第二个参数:Method method。目标方法。
  • 第三个参数:Object[] args。目标方法调用时要传的参数。

我们将来肯定是要调用“目标方法”的,但要调用目标方法的话,需要“目标对象”的存在,“目标对象”从哪儿来呢?我们可以给TimerInvocationHandler提供一个构造方法,可以通过这个构造方法传过来“目标对象”,代码如下:

public class TimerInvocationHandler implements InvocationHandler {
    // 目标对象
    private Object target;

    // 通过构造方法来传目标对象
    public TimerInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 目标执行之前增强。
        long begin = System.currentTimeMillis();
        // 调用目标对象的目标方法
        Object retValue = method.invoke(target, args);
        // 目标执行之后增强。
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");
        // 一定要记得返回哦。
        return retValue;
    }
}

接下来我们开始调用:

 // 创建目标对象
        OrderService target = new OrderServiceImpl();
        // 创建代理对象
        OrderService orderServiceProxy = (OrderService) ProxyUtil.newProxyInstance(target);
        // 调用代理对象的代理方法
        orderServiceProxy.add();
        orderServiceProxy.update();
        orderServiceProxy.delete();

 成功;

动态代理底层实现

现在我们思考一个问题:在使用动态代理之前,我们有一个目标类、一个公共接口;我们如何通过二者和proxy类返回一个代理类对象呢?

我们知道创建一个对象就先要有这个类才能new对象,那现在代理类都还不存在,该怎么去构造代理对象呢?

JDK动态代理的目的就是通过接口来生成代理类以及代理类的对象,我们知道接口是不能直接通过new关键字创建对象的。那么JDK动态代理是怎么创建出代理类以及代理类对象的呢?
 

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>