常见设计模式四:代理模式

437 阅读3分钟

前言

代理模式又称为委托模式,主要分为

  1. 静态代理
  2. 动态代理

代理模式的设计里面是限制对对象的直接访问,要想访问具体对象需要通过该对象的代理类去访问。

代理模式在很多地方是比较常见的,比如在 ARouter 内部,ARouter 的主要功能是通过其_ARouter 实现的,这里的 ARouter 就是_ARouter 的代理类。

/**
 * Init, it must be call before used router.
 */
public static void init(Application application) {
    if (!hasInit) {
        logger = _ARouter.logger;
        _ARouter.logger.info(Consts.TAG, "ARouter init start.");
        hasInit = _ARouter.init(application);

        if (hasInit) {
            _ARouter.afterInit();
        }

        _ARouter.logger.info(Consts.TAG, "ARouter init over.");
    }
}

静态代理

静态代理主要分为下面三个部分:

  1. 抽象接口 ISubject
  2. 具体对象 RealSubject
  3. 代理对象 ProxySubject

举个生活中我们使用代购替我们购买东西的例子。

创建购物抽象类:IShop

public interface IShop {
    void shop();
}

创建具体实际的购物抽象类:(朋友替我们购物)

public class Friend implements IShop {
    @Override
    public void shop() {
        System.out.println("朋友代替我们购物");
    }
}

创建代理类:

public class ProxyShop implements IShop {

    /**
     * 实际购物对象
     */
    private IShop realShop;

    public ProxyShop(IShop realShop) {
        this.realShop = realShop;
    }

    @Override
    public void shop() {
        System.out.println("委托朋友代购");
        realShop.shop();
        System.out.println("朋友把代购的物品交给我们");
    }
}

创建测试类:

public class Main {
    public static void main(String[] args) {
        Friend friend = new Friend();
        ProxyShop proxyShop = new ProxyShop(friend);
        proxyShop.shop();
    }
}

结果:

可以看到,朋友 friend 帮我们完成了购物动作,并且在代理类过程中,可以增加一些特殊的操作,在朋友帮我们代理购物的前后可以增加自定义操作。

动态代理

上面讲了静态代理,我们需要预先把需要的情况都要列出来,然后编写代理代码,需要自己生成编写代理类。

而动态代理就可以在运行时通过 Proxy.newProxyInstance生成代理类,比如我们有了新的需求,让朋友帮我们交话费,这个时候,就需要创建交话费的抽象,然后创建充话费的代理,这是很麻烦的,下面通过动态代理模式实现。

下面仍然以购物为例

抽象购物:

public interface IShop {
    void shop();
}

实际代理对象:Friend

public class Friend implements IShop {
    @Override
    public void shop() {
        System.out.println("朋友代替我们购物");
    }
}

动态代理:

public class DynamicProxyHandler implements InvocationHandler {

    Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("委托朋友代购");
        method.invoke(target,args);
        System.out.println("朋友把代购的物品交给我们");
        return null;
    }
}

测试类:

public class Main {
    public static void main(String[] args) {

        Friend friend = new Friend();
        DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(friend);
        IShop shop = (IShop) Proxy.newProxyInstance(Friend.class.getClassLoader(),Friend.class.getInterfaces(),dynamicProxyHandler);
        shop.shop();
    }
}

运行结果:

这就是动态代理的大致流程。

需要注意的是,上面 DynamicProxyHandler 里面的 invoke 里面的代码,是在执行代理接口的里面的方法的时候才会被执行到,而不是创建代理对象的时候执行,所以这里我们就可以额外的加入一些操作,比如在朋友把代购物品交给我们以后,我们还要向朋友付钱,那么就可以:

public class Main {
    public static void main(String[] args) {
        
        Friend friend = new Friend();
        DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(friend);
        IShop shop = (IShop) Proxy.newProxyInstance(Friend.class.getClassLoader(),Friend.class.getInterfaces(),dynamicProxyHandler);
        shop.shop();
        System.out.println("我们向朋友付了商品的价钱");
    }
}

结果:

可以看到,我们可以在方法具体执行的前后加入一些自定义逻辑,更加灵活,而且在没修改原来方法的情况下,可以增强代码功能。

通过上面的实例可以发现:

静态代理的代理类需要自己编写,而动态代理的代理类则是由运行时使用 newProxyInstance 动态生成,

同时不管是静态代理还是动态代理,都需要实现接口,本质上是面对接口编程的,能够增加现有代码的功能。 一些参考地址 轻松学,Java 中的代理模式及动态代理 从一道面试题开始说起 枚举、动态代理的原理