二十三种设计模式之代理模式

本文介绍了代理模式在IT中的应用,如SpringBoot中的AOP,以及通过生活中的火车票代售点解释其概念。重点讲解了JDK动态代理的实现,包括MyInvocationHandler和如何获取被代理对象。
摘要由CSDN通过智能技术生成

代理模式的作用

代理模式可以说是 23 种设计模式中,非常有名的一种了。为什么这么说呢?当今使用 Java 作为后台语言的互联网大厂都离不开 SpringBoot,而 SpringBoot 中,代理模式可以说是满天飞,SpringBoot 最核心的功能面向切面编程(AOP)就是使用代理模式实现的。

代理模式怎么理解呢?

生活中,最接近代理模式的就是火车票的代售点了。

想想为什么会有火车票代售点?

火车站密度小,很多人住的地方离火车站很远;
如果所有人都去火车站买票,火车站将不堪重负,尤其是在春节期间。
所以,代理模式作用在这样一种情景:要访问的对象,直接访问起来开销很大,比如有些对象是通过远程过程调用的方式生成的,访问依次就会有网络开销。有些对象初始化的时候,是要从磁盘加载的,等等。

代理模式的功能和作用就是:给那些不建议直接访问的对象 A,提供一个代理对象 B。当需要访问 A 时,通过 B 去访问,B 会做一些优化工作,使得它访问 A 的开销要小得多。

下面看下火车票代售点的例子的实现。

车站和车票
车票接口如下:

/**

  • 车票
    */
public interface Tickets {
    //售卖车票
    void sellTickets();
}

车站需要去实现上面的 sellTickets 方法,进行卖票:

/**

  • 火车站
    */
public class RailwayStation implements Tickets{
    //火车站构造函数
    public RailwayStation() {
        initStation();
    }
    //火车站售卖车票
    @Override
    public void sellTickets() {
        System.out.println("火车站售卖车票");
    }
    //初始化火车站
    private void initStation() {
        System.out.println("初始化火车站,比如调配工作人员、设立售票窗口");
    }
}

车站在初始化的时候,需要执行一些动作,比如调配工作人员、设立售票窗口等,这样,通过车站去买票的开销就很大。

火车票代售点
代售点也可以买票,代售点的票也是从车站来的,和上面不同的是,代售点从车站拿票的时候,是不需要每次都让车站初始化的,如下:

/**

  • 火车票代售点
    */
public class ProxySellStation implements Tickets{
    //火车站
    private RailwayStation railwayStation;
    //代售点售卖车票
    @Override
    public void sellTickets() {
        //初始化火车站
        if (null == railwayStation) {
            railwayStation = new RailwayStation();
        }
        //售卖车票
        railwayStation.sellTickets();
    }
}

可以看到,代售点会去判断车站有没有初始化成功,有的话,就直接执行售卖车票的动作。

通过一个主类,看下程序的执行结果:

public class Main {
    //主函数
    public static void main(String[] args) {
        //初始化代售点
        Tickets tickets = new ProxySellStation();
        //代售点售卖车票
        tickets.sellTickets();
        //代售点售卖车票
        tickets.sellTickets();
    }
}

看下运行结果:

图 1 运行结果

代售点卖了两次车票,只有第一次需要初始化车站。符合预期。
JDK 动态代理
上面的代理模式中,我们需要为每一个被代理的类新建一个代理类,这样很低效,这种成为静态代理。

与之相反的就是动态代理,即:我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口。

JDK 动态代理是一种常见的动态代理机制,它是 Java 原生的,原理是在运行时动态生成类字节码,并加载到 JVM 中的。

如果你不了解也没关系,这是比较难的知识,建议结合 Spring 或者 SpringBoot 去学习。

一步步看下 JDK 动态代理如何使用。

定义一个动态代理类
动态代理类的作用类比于上面的火车票代售点:

/**

  • 定义一个 JDK 动态代理类
    */
public class MyInvocationHandler implements InvocationHandler {
    /**
     * 代理类中的真实对象
     */
    private final Object target;
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    /**
     * 执行被代理对象的方法
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("开始执行方法之前:");
        //执行被代理对象的方法
        Object result = method.invoke(target, args);
        //返回执行结果
        return result;
    }
}

和代售点一样,它也需要一个成员变量,存储被代理的对象。然后在 invoke 中,执行被代理对象的被代理方法。在这个方法执行前后,我们都可以加入自己的代码。

不同的是,这里的被代理对象 target 是通用的,可以使用任意类的对象。

获取被代理对象并使用
接下来通过一个工厂方法,获取被代理的对象:

//获取被代理对象的方法

public static Object getProxy(Object target) {
    //返回被代理的对象
    return Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 目标类的类加载
            target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
            new MyInvocationHandler(target)   // 代理对象对应的自定义 InvocationHandler
    );
}

这也是通用的,意味着我们不需要为不同的代理对象,编写不同的获取方法。

最后是调用被代理对象,执行方法:

//主函数

public static void main(String[] args) {
    //通过代理获取对象
    Tickets railwayProxy = (Tickets)getProxy(new RailwayStation());
    //执行方法
    railwayProxy.sellTickets();
}

看下执行结果:
在这里插入图片描述
运行结果

符合预期。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ac果

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值