乐趣区

Java别再问我什么是静态代理

从前从前,有个面试官问我动态代理和静态代理的区别,我当时支支吾吾没说清楚,只提到了动态代理需要实现 InvocationHandler 接口,然后使用 Proxy 类反射创建实例云云。至于静态代理……这玩意不就是一种设计思想?

面试官笑了笑,从此天涯路人不相逢。

我痛定思痛,一定要把代理这一块搞懂,于是乎有了这篇以及动态代理两篇文章。以后再也不怕面试官问我关于静态代理和动态代理的问题了!

1 什么是代理

说到静态代理,就不得不提设计模式中的 代理模式

百度百科对于代理模式的定义是这样的:

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

在这段定义中有这样两个字:中介。

我想下面这个例子可以很好的解释代理模式。

相信在一个陌生的城市打拼的程序员们在初期都会遇到这样一个问题:租房。我们通常有三种方式,第一可以自己在闲鱼、豆瓣、自如等信息网站去找房源,第二直接去心仪的小区公告栏看看有没有招租信息(当然可能被中介的广告霸占),第三就是联系房产中介,中介会帮你挑选你想要租的房子,只不过需要付一笔服务费。

假设我选择 委托 中介来租房,在这个过程中就可以把房子抽象为一个类,把帮我租房的中介抽象为一个类,这两个类都持有同样的方法。而”我“作为客户,委托 中介帮我找到房子。

委托 中介帮我找房子的这个过程,就是代理。

2 静态代理

所谓的静态代理,就是在程序启动之前代理类的 .class 文件就已经存在。而代理类可能是程序员直接创建的 .java 文件,或者是借助某些工具生成的 .java 文件,但无一例外都必须再由编译器编译成 .class 文件之后再启动程序。

3 定义与类图

在根据上面说的租房的例子来编写实际的代码作为静态代理的示例之前,首先得了解一下代理模式中的几个角色,代理模式中有三个主要角色,即抽象主题角色、真实角色(被代理角色)以及代理类角色。

3.1 主题角色 (Subject)

主题角色可以是接口,也可以是抽象类。它定义了真实角色和代理类角色共有的方法,主题类让这两者具有一致性。

同时,也正是基于主题角色,才能实现代理的功能。

3.2 真实角色 (RealSubject)

真实角色就是被代理类,在上面的例子中就是房子,同时也是具体业务逻辑的执行者。

3.3 代理类角色 (Proxy)

代理类角色的内部含有对真实角色 RealSubject 的引用,它负责对被代理角色的调用,并在被代理角色处理前后做预处理和后处理。

在上面租房的例子中就是房产中介。

3.4 Client

有人可能会问了,那“我”呢?简单点说,其实“我”就是测试方法中的 main 方法,负责调用代理角色的方法。

3.5 类图

图片来源于 Proxy 模式——静态代理

4 代码示例

基于上面租房的例子使用代码实现。

首先创建主题角色,它是一个接口,拥有一个方法叫做 intention,这个方法是要被其他两个角色重写的。

public interface Subject {

    /**
     * 各个角色的公用方法
     */
    void intention();}

然后是真实角色,也就是被代理角色、真正的业务逻辑执行者。

public class House implements Subject {

    @Override
    public void intention() {System.out.println("我是客户想要的房子");
    }
}

然后代理类,它能够增强被代理角色的方法。代理类就是帮助我”找到好房子“的房产中介。

当一个房产中介拥有一个客户(RealSubject)时,才会发挥他的作用,在我的”意图“被实现前后,它分别可以对我的”意图“进行增强。

public class Proxy implements Subject {

    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {this.realSubject = realSubject;}

    @Override
    public void intention() {System.out.println("我是中介,我会帮助客户找到心仪的房子");
        house.intention();
        System.out.println("我是中介,找到了");
    }
}

最后,“我”出场了,“我”委托中介寻找房子。

public class Client {public static void main(String[] args) {
        // 我可能心仪的房子
        House house = new House();
        // 代理类——房产中介
        Proxy proxy = new Proxy(house);
        // "我" 委托中介去寻找房子
        proxy.intention();}   
}

最终“我”执行的是代理类(中介)的 intention 方法,由于代理类持有一个真实角色(房子),程序又会执行真实角色的 intention 方法,这样就实现了“我”委托中介找到房子的静态代理过程。

5 静态代理的优缺点

5.1 优点

  1. 业务类只需要关注业务逻辑本身,保证了业务类的重用性。
  2. 客户端只需要知道代理,无需关注具体实现。

中介只需要关注自己能找房子的效率和质量就可以了,无论谁想来委托中介,都能找到房子。而“我”不需要知道中介是如何找房子的,只要他帮我找到房子,就可以了。

5.2 缺点

  1. 由于代理类和被代理类都实现了主题接口,它们都有相同的方法,导致大量代码重复。同时如果主题接口新增了一个方法,那么代理类与被代理类也都需要实现这个方法,增加了维护代码的复杂度。
  2. 如果代理类要为其他真实角色提供委托服务的话,就需要实现其他的接口,当规模变大时也会增加代码复杂度。

如果中介不仅提供租房服务,还提供打游戏、卖房子、卖电影票、卖彩票、陪聊天、陪玩游戏等等一系列服务,那么他将变得无比庞杂,没有人敢动他(这里的他指代码)。

6 小结

本文讲述了代理模式、静态代理、代理模式中的角色、静态代理的代码实现以及静态代理的优缺点。

希望可以帮助到大家。

以上。

退出移动版