在我们日常生活中,有些事情我们大部分不会直接去做,而是会通过一个类似中介的角色去完成,例如买车时,一般会到4S店购买,不会直接到汽车厂商处购买;租房时会让中介帮忙寻找,自己等待中介提供房源,而不是自己花大时间成本去寻找;黑客进行网络攻击时,一般不会用自己的真实主机去操作,而是通过层层的代理机,防止自己的地址被追踪到……这样的例子在生活中很多,艺术来源于生活,设计模式同样也来源于生活,这样场景在软件设计模式上称为代理模式,这是一种非常重要的模式,在Java很多的架构框架中有大量地应用这样的设计模式。
什么是代理模式?
代理模式(Proxy) :为另一个对象提供一个替身或占位符以控制这个对象的访问;由于某些原因不能直接访问某个对象,需要通过代理对象间接访问目标对象,以代理对象间接访问目标对象的模式称为代理模式。
代理模式结构组成
代理模式的结构比较简单,主要是通过定义一个继承抽象业务的代理来增强真正的业务,从而实现对实际的业务访问,下面来分析其基本结构。
代理模式的主要组成包含以下部分:

业务抽象类(Subject):通过接口或抽象类声明真实业务场景的业务方法,服务端需要实现该方法。
业务实现类(Real Subject):实现了业务抽象中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
代理类(Proxy):提供了与业务实现相同的接口,其内部含有对业务实现类的引用,它可以访问、控制或扩展业务实现类功能。
在实际的代码应用中,一般使用代理模式会被理解为代码增强,实际上就是在原代码逻辑前后增加一些代码逻辑,但对调用者是无感知的。
代理模式实现
在Java语言中,根据代理对象的创建时期,我们将代理模式分为静态代理和动态代理。
静态代理:由开发者创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
静态代理
假设有一个场景,消费者要买某品牌的手机,选择在当地的代理商购买,而手机品牌厂商除了提供手机产品给代理商外,其他均未提供,产品的营销和服务均授权给代理商。在这样的一种场景下,消费者是不宜直接跟手机厂商直接购买,而是通过代理商来购买比较合适。用代码如何实现这样的一种关系呢?
首先,我们先定义一个手机厂商的接口及其实现类,手机厂商只有一个进行手机生产售卖的方法;


有了这两个类,我们再来定义一个手机代理商角色,其主要功能也是售卖手机,只是在售卖手机前加上了自己市场营销活动,对手机进行促销等,而卖出手机后,还对手机的售后进行服务。

最后消费者进行购买时区别会在哪里?

运行消费者进行购买手机的代码,对比分别在手机厂商和手机代理商处购买的结果,我们会发现,如果消费者直接从手机厂商处购买,是没有获得购买前的促销优惠以及购买手机后的售后服务的,很显然,这样的情况下消费者是不适合直接向手机厂商直接购买,而是从手机的代理商处购买会更加符合利益,这也是符合前面我们提到的代理模式能解决的问题所在,由于某些原因不适合直接访问目标对象,需要通代理对象进行访问。
动态代理
上述的静态代理使用中,大家会发现,这个代理增强是需要我们去实现,如果代理类有很多岂不是要我们写很多实现?而且有些特殊的场景,需要运行时才能确定其代理类,那这就不能满足我们的需求了,因此,有了动态代理。
动态代理的作用和静态代理的作用都是一样,前面提到它们的差别就在于实现方式,静态代理是需要开发者去实现代理对象,而动态代理的代理对象主要在运行时生成的。
我们依旧使用上方场景,看动态代理是如何实现的?
这里我们选择使用Java的JDK的实现方式,选择实现java.lang.reflect.InvocationHandler接口中的invoke方法;

IMobileMakerDynamicProxy类里面除了包含实现invoke方法外,还加上了前面静态代理中的售卖前的营销方法和售卖后的服务方法,他们分别在invoke方法的method.invoke前后分别进行调用,另外,此类还提供了此类对象的工厂创建方法;
再来看下我们使用动态代理的调用过程和结果

得到是结果是和上述中的静态代理是一样的。
在动态代理中,其实还有一种实现方式叫CGLib代理,它是区别于JDK需实现接口的动态代理的另一种实现方式,CGLib代理其主要是对代理类进行继承生成子类扩展的方式实现的。
使用CGLib动态代理实现如下(需引入包,这里使用了spring-core 3.2.2):

与JDK不同的是,CGLib动态代理实现方式需要实现MethodInterceptor接口,然后需要实现其intercept方法,其他的方法跟JDK动态代理的方法是一样的,该在代理前后进行增强就增强;使用Cglie最后运行如下:

达到的效果是跟JDK的动态代理一样的,不过在IMobileMakerImpl不需要实现接口(上方运行代码时已去除实现IMobileMaker接口);
JDK动态代理和CGLib代理的区别
- JDK需要实现接口,而CGLib是针对类
- JDK生成的是接口的实现类增强,CGLib是继承生成目标的类的子类来增强
- CGLib的所要代理的类不能使用Final关键字修饰,这样会无法继承生成子类
- 有研究表明,CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,大概要高10倍;但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,大概有8倍的差距;
应用场景
代理模式应用场景很多,因为静态代理模式局限性比较小,所以应用最多的还是动态代理,
应用场景比如权限校验、日志记录,缓存等,还有开发框架如Spring的AOP,一些RPC如dubbo框架均有使用代理模式。
代理模式优缺点
缺点:
1、代理模式会造成系统设计中类的数量增加。
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
3、增加了系统的复杂度。
优点:
1、代理模式能将代理对象与真实被调用的目标对象分离。
2、一定程度上降低了系统的耦合度,扩展性好。
3、可以起到保护目标对象的作用。
4、可以对目标对象的功能增强。
总结
通过上述的例子,手机厂商只提供手机,代理商提供额外的营销和服务,消费者如果直接从手机厂商能购买到的只有手机,但是从代理商处购买还额外获取营销的优惠以及售后的服务的,这些都是代理商的“增强”行为,代理商卖的手机本质上还是从手机厂商处获取的。
代理模式理解起来并不困难,稍微结合一下生活中的例子就能理解了;但是理解并不等于学会,就像书本上的每个知识点都懂,但不是每个人都能上清北。在实际业务中,需要极强的业务理解和抽象能力才能将此模式运用得好,不过万变不离其中,找准了目标对象和代理对象,剩下一切的都迎刃而解了。