💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、豆包、星火、月之暗面及文生图、文生视频 广告
# 常见设计模式四:代理模式 [TOC] 代理模式又称为委托模式,主要分为 1. 静态代理 2. 动态代理 代理模式的设计里面是限制对对象的直接访问,要想访问具体对象需要通过该对象的代理类去访问。 代理模式在很多地方是比较常见的,比如在 ARouter 内部,ARouter 的主要功能是通过其_ARouter 实现的,这里的 ARouter 就是_ARouter 的代理类。 ```java /** * 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 ```java public interface IShop { void shop(); } ``` 创建具体实际的购物抽象类:(朋友替我们购物) ```java public class Friend implements IShop { @Override public void shop() { System.out.println("朋友代替我们购物"); } } ``` 创建代理类: ```java 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("朋友把代购的物品交给我们"); } } ``` 创建测试类: ```java public class Main { public static void main(String[] args) { Friend friend = new Friend(); ProxyShop proxyShop = new ProxyShop(friend); proxyShop.shop(); } } ``` 结果: ![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200308174155.png) 可以看到,朋友 friend 帮我们完成了购物动作,并且在代理类过程中,可以增加一些特殊的操作,在朋友帮我们代理购物的前后可以增加自定义操作。 ## 动态代理 上面讲了静态代理,我们需要预先把需要的情况都要列出来,然后编写代理代码,需要自己生成编写代理类。 而动态代理就可以在运行时通过 Proxy.newProxyInstance生成代理类,比如我们有了新的需求,让朋友帮我们交话费,这个时候,就需要创建交话费的抽象,然后创建充话费的代理,这是很麻烦的,下面通过动态代理模式实现。 下面仍然以购物为例 抽象购物: ```java public interface IShop { void shop(); } ``` 实际代理对象:Friend ```java public class Friend implements IShop { @Override public void shop() { System.out.println("朋友代替我们购物"); } } ``` 动态代理: ```java 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; } } ``` 测试类: ```java 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(); } } ``` 运行结果: ![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200308181042.png) 这就是动态代理的大致流程。 需要注意的是,上面 DynamicProxyHandler 里面的 invoke 里面的代码,是在执行代理接口的里面的方法的时候才会被执行到,而不是创建代理对象的时候执行,所以这里我们就可以额外的加入一些操作,比如在朋友把代购物品交给我们以后,我们还要向朋友付钱,那么就可以: ```java 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("我们向朋友付了商品的价钱"); } } ``` 结果: ![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200308193510.png) 可以看到,我们可以在方法具体执行的前后加入一些自定义逻辑,更加灵活,而且在没修改原来方法的情况下,可以增强代码功能。 通过上面的实例可以发现: 静态代理的代理类需要自己编写,而动态代理的代理类则是由运行时使用 newProxyInstance 动态生成, 同时不管是静态代理还是动态代理,都需要实现接口,本质上是面对接口编程的,能够增加现有代码的功能。 区别 : **静态代理** 静态代理业务类只需要关注业务逻辑本身,保证了业务类的重用性。代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,需要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法,增加了代码维护的复杂度。 **动态代理** 动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler invoke)。这样,在接口方法数量比较多的时候,可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使类职责更加单一,复用性更强。 一些参考地址 [轻松学,Java 中的代理模式及动态代理](https://blog.csdn.net/briblue/article/details/73928350#comments) [从一道面试题开始说起 枚举、动态代理的原理](https://blog.csdn.net/lmj623565791/article/details/79278864)