设计模式(十四):代理模式
一、概述
代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。其实就是代理就是做到类似转发的功能,针对不同代理,转发的具体实现不同。
二、解决问题
从原理知道他就是控制客户对一个对象的访问,它跟现实中的中介代理类似,只是作为代表做一些受理工作,真正执行的并不是它自己。比如买火车票,外面有很多火车票代理点,我们直接去代理点买票就好而不用跑到火车票买了(暂时不考虑网购哈)。
三、结构类图
四、应用实例
在这个例子中,主要讲解远程代理,它可以作为另一个JVM上的本地代表。客户端调用代理的方法,代理会利用网络把请求转发到远程执行,并把执行结果通过网络返回到代理,最终返回到客户端。
下面使用引用java RMI(Remote Method Invoke远程方法调用)的例子讲解远程代理模式。它的的工作原理图如下,用户向服务器A发起话费充值请求,服务器A通过网络调用服务器B的方法,服务器B把充值结果返回到服务器A,最后返回到用户。
代码实现如下,首先创建一个远程接口
package com.jet.pattern.proxy; import java.net.MalformedURLException; import java.rmi.NotBoundException; import java.rmi.Remote; import java.rmi.RemoteException; /** * description: * 远程接口,其继承java的Remote接口,真正干活对象和代理都要实现这个接口 * Created by Administrator on 2017/1/17. */ public interface MyRemote extends Remote{ // 远程调用有风险告诉客户端 public String request(int money) throws RemoteException, MalformedURLException, NotBoundException; }
创建远程对象
package com.jet.pattern.proxy.impl; import com.jet.pattern.proxy.MyRemote; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.server.UnicastRemoteObject; /** * description: * 真正干活的对象,接受代理对象的访问 * Created by Administrator on 2017/1/17. */ // 继承UnicastRemoteObject,让jvm帮我们完成远程调用 public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{ // 父类构造器抛出了异常,所以子类构造器也需要抛出异常 public MyRemoteImpl() throws RemoteException { } @Override public String request(int money) throws RemoteException { System.out.println("充值" + money + "元话费成功"); return "充值成功"; } public static void main(String[] args) { try { // 产生远程对象 MyRemote service = new MyRemoteImpl(); // 注册远程访问接口 LocateRegistry.createRegistry(8888); // 注册远程对象,注册名为RemoteHello,代理访问时指定这个名称就可以找到本类, Naming.rebind("rmi://localhost:8888/RemoteHello",service); } catch (Exception e) { e.printStackTrace(); } } }
创建代理对象
package com.jet.pattern.proxy.impl; import com.jet.pattern.proxy.MyRemote; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; /** * description: * 代理对象,控制客户端的访问,把客户端访问请求转发到真正干活的对象 * Created by chenzanjin on 2017/2/4. */ public class Proxy implements MyRemote { @Override public String request(int money) throws RemoteException, MalformedURLException, NotBoundException { MyRemote remote = (MyRemote)Naming.lookup("rmi://localhost:8888/RemoteHello"); return remote.request(money); } }
创建访问客户端
package com.jet.pattern.proxy.test; import com.jet.pattern.proxy.MyRemote; import com.jet.pattern.proxy.impl.Proxy; import java.net.MalformedURLException; import java.rmi.NotBoundException; import java.rmi.RemoteException; /** * description: * 代理客户端,测试对代理的访问 * Created by chenzanjin on 2017/2/4. */ public class ProxyClient { public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException { MyRemote proxy = new Proxy(); System.out.println(proxy.request(100)); } }
测试步骤:
1、启动MyRemoteImpl 类的main方法,绑定远程对象到jvm。
2、启动ProxyClient 类的main方法,对代理对象发起访问。
代码输出如下:
MyRemoteImpl 类会输出:充值100元话费成功
ProxyClient 类输出:充值成功
五、其他代理
1、虚拟代理:可以作为创建开销大的对象的代表,虚拟代理让我们真正需要使用一个对象时才会去创建这个对象。在对象创建前和创建中,虚拟代理扮演真实对象的替身,当对象创建完毕后,虚拟代理会把请求直接委托给真实对象。虚拟代理在手机页面加载中经常可以见到,比如打开页面显示"**正在玩命加载中..."。
2、缓存代理:可以理解成把网络上的静态资源或者不常改变的数据保存在本地,再次访问的这次数据的时候直接从本地读取。应用例子有页面的缓存机制、cdn、app缓存机制等。
3、保护代理:根据角色或者其他机制控制用户对一些资源的访问权限。这常见于一些网站需要登录才能获取更多的内容,struts2和spring的拦截器等。
4、静态代理:可以理解为对象由代理创建,在创建前后做一些事情,比如统计创建了多少对象,但静态代理只能创建一个或固定的几个对象。详见:http://blog.csdn.net/lidatgb/article/details/8941711。
5、动态代理:其用途跟静态代理类型,但可以利用反射机制动态地创建对象,可以减少代理代码的重复,有更好的扩展性。详见:http://blog.csdn.net/lidatgb/article/details/8993131。
6、正向代理:用户可以指定代理服务器转发请求到目标服务器完成,用户知道代理服务器地址和目标服务器地址。常见于FQ代理服务器。
7、反向代理: 用户访问的目标服务器其实是代理服务器,代理服务器做负载均衡后把请求转发到真正的目标服务器执行。常见于负载均衡服务器。
六、使用场景
由代理的概述中知道,但需要控制对资源或者对象的访问时使用代理模式。从代理的种类中可以进一步看清代理模式的使用场景,无非就是转发远程请求、作为创建开销大对象的替身、在本地保存网络资源、控制一些受保护资源的权限访问、作负载均衡。