什么是代理模式
本篇内容介绍了"什么是代理模式"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
代理模式
整个过程用代码展示如下:
/** * 鞋厂接口 */public interface IShoesFactory { /** * 参观工厂 */ void visitFactory(); /** * 下订单 * @param price 金额 */ void placeOrder(double price); /** * 发货 */ void ship();}
/** * 鞋厂类 */public class ShoesFactory implements IShoesFactory{ private String name; public ShoesFactory(String name) { this.name = name; } /** * 参观工厂 */ @Override public void visitFactory() { System.out.println("带客户参观" + name); } /** * 下订单 * * @param price 金额 */ @Override public void placeOrder(double price) { System.out.println("接到" + price + "元的订单"); } /** * 发货 */ @Override public void ship() { System.out.println("开始发货"); }}
/** * 代理鞋厂类 */public class ShoesFactoryProxy implements IShoesFactory{ private IShoesFactory shoesFactory; public ShoesFactoryProxy(IShoesFactory shoesFactory) { this.shoesFactory = shoesFactory; } /** * 参观工厂 */ @Override public void visitFactory() { shoesFactory.visitFactory(); } /** * 下订单 * * @param price 金额 */ @Override public void placeOrder(double price) { shoesFactory.placeOrder(price); } /** * 发货 */ @Override public void ship() { shoesFactory.ship(); }}
客户类:
public class Client { public static void main(String[] args) { IShoesFactory factory = new ShoesFactory("刘望星的工厂"); IShoesFactory factoryProxy = new ShoesFactoryProxy(factory); // 参观工厂 factoryProxy.visitFactory(); // 下订单 factoryProxy.placeOrder(10000); // 发货 factoryProxy.ship(); }}
输出结果:
带客户参观刘望星的工厂接到10000.0元的订单开始发货
客户要参观工厂,小帅就带客户去参观刘望星的工厂,和客户说这就是自己的工厂。客户向小帅下单,小帅就把订单下给刘望星生产。
客户类调用代理的方法,代理类再调用业务类的方法完成工作。
代理模式定义
代理模式(Proxy Pattern) :给某一个对象提供一个代理,并由代理对象控制对原对象的引用。 代理模式的英语叫Proxy,它是一种结构型模式。
代理背后一般至少有一个实际对象,代理的外部功能和实际对象一般是一样的,用户与代理打交道,不直接接触实际对象,甚至不知道实际对象的存在。
代理模式到底有什么用呢?难道仅仅是通过代理类做个转发吗?
当然不是的,虽然外部功能和实际对象一样,但代理有它存在的价值,代理也分为多种类型比如:
远程(Remote)代理:适用于调用远程服务器对象的情况,代理通过网络传递客户端请求, 负责处理所有与网络相关的复杂细节,简化了客户端的调用。
虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个 开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
防火墙(Firewall)代理:保护目标不让恶意用户接近。
同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。
我们来看看几个例子:
保护代理
保护(Protect or Access)代理的作用是,控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
如果我们不想客户访问某个功能,就可以在代理类里做手脚,毕竟业务类都是通过代理类调用的嘛。
比如,小帅怕露馅,不想让客户参观工厂,就可以改写参观工厂的方法:
输出:
工厂禁止参观接到10000.0元的订单开始发货
智能引用代理
智能引用(Smart Reference)代理的作用是,当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。
比如,小帅作为代理商,肯定是要赚钱的,客户下了一个10000元的订单,小帅就和刘望星谈好提成10%,就可以在代理类中把金额扣掉。
还有,小帅的工厂有自己的品牌,发货之前还要贴上自己的牌子。
输出:
工厂禁止参观接到9000.0元的订单贴上小帅牌标签。开始发货
代理模式的几种应用
图片代理:一个很常见的代理模式的应用实例就是对大图浏览的控制。用户通过浏览器访问网页时先不加载真实的大图,而是通过代理对象的方法来进行处理。
在代理对象的方法中,先使用一个线程向客户端浏览器加载一个小图片,然后在后台使用另一个线程来调用大图片的加载方法将大图片加载到客户端。当需要浏览大图片时,再将大图片在新网页中显示。
如果用户在浏览大图时加载工作还没有完成,可以再启动一个线程来显示相应的提示信息。通过代理技术结合多线程编程将真实图片的加载放到后台来操作,不影响前台图片的浏览。远程代理:远程代理可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的远程业务对象是局域的而不是远程的,而远程代理对象承担了大部分的网络通信工作。
虚拟代理:当一个对象的加载十分耗费资源的时候,虚拟代理的优势就非常明显地体现出来了。虚拟代理模式是一种内存节省技术,那些占用大量内存或处理复杂的对象将推迟到使用它的时候才创建。
动态代理
上面说的这些方法是静态代理模式,真实的业务类必须是事先已经创建好的的,并把它传给代理对象,作为一个内部成员。
静态代理是这样子的:
如果一个真实业务类必须对应一个代理类,这将导致系统中的代理类的个数急剧增加,比如有10个不同的业务类,那么必须要有10个对应的代理类,因此需要想办法减少系统中类的个数。
动态代理可以在事先不知道真实业务类的情况下使用代理类,在程序运行期间由JVM根据反射等机制动态的生成,动态代理的典型应用就是Spring AOP。
下面我们看一下Java SDK动态代理的例子:
/** * 鞋厂接口 */public interface IShoesFactory { /** * 参观工厂 */ void visitFactory(); /** * 下订单 * @param price 金额 */ void placeOrder(double price); /** * 发货 */ void ship();}
/** * 鞋厂类 */public class ShoesFactory implements IShoesFactory { private String name; public ShoesFactory(String name) { this.name = name; } /** * 参观工厂 */ @Override public void visitFactory() { System.out.println("带客户参观" + name); } /** * 下订单 * * @param price 金额 */ @Override public void placeOrder(double price) { System.out.println("接到" + price + "元的订单"); } /** * 发货 */ @Override public void ship() { System.out.println("开始发货"); }}
InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,代理类每调用一次方法就会进入这里的invoke方法。
public class ShoesFactoryHandler implements InvocationHandler { /** * 被代理的对象,实际的方法执行者 */ private Object proxiedObject; public ShoesFactoryHandler(Object proxiedObject) { this.proxiedObject = proxiedObject; } /** * 代理类每调用一次方法就会进入这里 * @param proxy 表示代理对象本身,需要注意,它不是被代理的对象 * @param method 表示正在被调用的方法 * @param args 表示方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*if(method.getName().equals("visitFactory")) { System.out.println("工厂禁止参观"); }*/ if(method.getName().equals("placeOrder")) { // 如果是下订单方法,给代工厂9折的价格 method.invoke(proxiedObject, Double.parseDouble(String.valueOf(args[0])) * 0.9); } else if(method.getName().equals("ship")) { // 如果是发货方法,发货之前先贴上标签 System.out.println("贴上小帅牌标签"); method.invoke(proxiedObject, args); } else { // 其他方法,直接调用 method.invoke(proxiedObject, args); } return null; }}
Proxy类调用newProxyInstance方法来创建一个代理对象,上面的InvocationHandler对象作为参数之一。
public class ShoesFactoryProxy { /** * 生成动态代理 * @param proxiedObject 被代理的对象,实际的方法执行者 * @return */ public Object createProxy(Object proxiedObject) { // 获取对应的ClassLoader ClassLoader classLoader = proxiedObject.getClass().getClassLoader(); // 获取所有的接口 Class[] interfaces = proxiedObject.getClass().getInterfaces(); // 创建一个传给代理类的调用请求处理器 ShoesFactoryHandler handler = new ShoesFactoryHandler(proxiedObject); return Proxy.newProxyInstance(classLoader, interfaces, handler); }}
客户端类:
public class Client { public static void main(String[] args) { IShoesFactory factory = new ShoesFactory("刘望星的工厂"); ShoesFactoryProxy shoesFactoryProxy = new ShoesFactoryProxy(); IShoesFactory factoryProxy = (IShoesFactory)shoesFactoryProxy.createProxy(factory); // 参观工厂 factoryProxy.visitFactory(); // 下订单 factoryProxy.placeOrder(10000); // 发货 factoryProxy.ship(); }}
输出结果:
带客户参观刘望星的工厂接到9000.0元的订单贴上小帅牌标签开始发货
通用性更强些的动态代理类一般是这样的:
public class NormalHandler implements InvocationHandler { /** * 被代理的对象,实际的方法执行者 */ private Object proxiedObject; public NormalHandler(Object proxiedObject) { this.proxiedObject = proxiedObject; } /** * 代理类每调用一次方法就会进入这里 * @param proxy 表示代理对象本身,需要注意,它不是被代理的对象 * @param method 表示正在被调用的方法 * @param args 表示方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 调用实际方法之前的操作 preRequest(); // 调用实际方法 Object result = method.invoke(proxiedObject, args); // 调用实际方法之后的操作 afterRequest(); } return result; }}
这样,我们就能在preRequest()和afterRequest()方法中做很多事情了,比如记录日志,记录调用时间等等。
总结
代理模式看上去和装饰者模式很像,不过它们的意图是不一样的,装饰者模式是为对象加上新的行为,而代理模式是作为真实对象的替身,控制对象的访问。
比如,保护代理,进行权限控制,不让客户访问某些功能;比如,虚拟代理,在真实的对象创建之前,先返回预定设定好的信息。
下面我们来看看代理模式的优点和缺点:
优点
代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
你可以在客户端毫无察觉的情况下控制服务对象。
即使服务对象还未准备好或不存在, 代理也可以正常工作。
符合开闭原则, 你可以在不对服务或客户端做出修改的情况下创建新代理。
缺点
由于在客户端和真实业务类之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
实现代理模式需要额外的工作,增加了程序的复杂度。
"什么是代理模式"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!