设计模式之代理模式浅析
/** * 代理模式: * 为另一个对象提供一个占位符或者替身,以控制对这个对象的访问 * * 按照控制方式的不同: * 远程代理-->控制访问远程对象(不在同一个JVM的对象) * 虚拟代理-->控制访问大开销资源的对象(网络或者IO对象) * 保护代理-->基于权限控制对资源的访问(不同的权限,对资源的访问层次不同) * 缓存代理-->为开销大的运算结果提供暂时的存储 * 智能引用代理-->当主题被引用时,进行额外的操作 * 防火墙代理-->控制网络资源的访问 * 同步代理-->在多线程的情况下为主题提供安全的访问 * 复杂隐藏代理-->用来隐藏一个类的复杂度并进行访问控制 * 写入时复制代理-->用来控制对象的复制 * * 模式对比: * 装饰者模式-->包装一个对象,对其提供额外的功能 * 适配器模式-->包装一个对象,并提供不同的接口 * 外观模式-->包装许多对象以提供访问的简单接口 * 代理模式 -->包装一个对象,以控制对它的访问 * * * 示例: * 远程代理使用RMI作为示例 ,远程访问的还有EJB和WEBSERVICES * 虚拟代理使用网络资源的加载作为示例,利用JFRAME作为示例 * 保护代理使用资源的访问作为示例,像SPRING的AOP就用了保护代理 * * * * @author Administrator * */
1、先看RMI示例的远程代理
RMI参考之前的文章 http://blog.csdn.net/undergrowth/article/details/24913031
远程服务接口
package com.undergrowth.proxy; import java.rmi.Remote; import java.rmi.RemoteException; /** * 代理模式: * 为另一个对象提供一个占位符或者替身,以控制对这个对象的访问 * * 按照控制方式的不同: * 远程代理-->控制访问远程对象(不在同一个JVM的对象) * 虚拟代理-->控制访问大开销资源的对象(网络或者IO对象) * 保护代理-->基于权限控制对资源的访问(不同的权限,对资源的访问层次不同) * 缓存代理-->为开销大的运算结果提供暂时的存储 * 智能引用代理-->当主题被引用时,进行额外的操作 * 防火墙代理-->控制网络资源的访问 * 同步代理-->在多线程的情况下为主题提供安全的访问 * 复杂隐藏代理-->用来隐藏一个类的复杂度并进行访问控制 * 写入时复制代理-->用来控制对象的复制 * * 模式对比: * 装饰者模式-->包装一个对象,对其提供额外的功能 * 适配器模式-->包装一个对象,并提供不同的接口 * 外观模式-->包装许多对象以提供访问的简单接口 * 代理模式 -->包装一个对象,以控制对它的访问 * * * 示例: * 远程代理使用RMI作为示例 ,远程访问的还有EJB和WEBSERVICES * 虚拟代理使用网络资源的加载作为示例,利用JFRAME作为示例 * 保护代理使用资源的访问作为示例,像SPRING的AOP就用了保护代理 * * * * @author Administrator * */ public interface GumballMachineRemote extends Remote{ /** * 每个方法都必须抛出throws RemoteException异常 * @return * @throws RemoteException */ public String getLocation() throws RemoteException; public int getCount() throws RemoteException; public String getDescription() throws RemoteException; public State getCurrState() throws RemoteException; }
package com.undergrowth.proxy; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; /** * 远程服务对象 * @author Administrator * */ public class GumballMachine extends UnicastRemoteObject implements GumballMachineRemote { String location; String description; int count; State currentState; /** * */ private static final long serialVersionUID = 1L; /** * 因为UnicastRemoteObject构造器需要处理异常 因为RMI在网络上传输 会发生未知的异常 * * @throws RemoteException */ protected GumballMachine() throws RemoteException { super(); // TODO Auto-generated constructor stub } public GumballMachine(String location, String description, int count) throws RemoteException { super(); this.location = location; this.description = description; this.count = count; if(count>0){ this.currentState = new NoMoneyState(); } } @Override public String getLocation() { // TODO Auto-generated method stub return location; } @Override public int getCount() { // TODO Auto-generated method stub return count; } @Override public String getDescription() { // TODO Auto-generated method stub return description; } @Override public State getCurrState() { // TODO Auto-generated method stub return currentState; } }
相关状态
package com.undergrowth.proxy; import java.io.Serializable; /** * 使用RMI传输State * @author Administrator * */ public interface State extends Serializable{ public void insertMoney(); }
无币状态
package com.undergrowth.proxy; /** * 模拟初始状态 * @author Administrator * */ public class NoMoneyState implements State { @Override public void insertMoney() { // TODO Auto-generated method stub System.out.println("投币"); } }
RMI注册服务和查找
package com.undergrowth.proxy; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; /** * RMI注册服务和查找 * @author Administrator * */ public class RegisterRmiServer { public static void registerRmi(int port,String name,GumballMachineRemote remoteServer){ try { //创建register端口监听 LocateRegistry.createRegistry(port); //绑定服务 Naming.rebind(name, remoteServer); //注册远程服务成功 System.out.println("注册远程服务成功"); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static GumballMachineRemote getRmiServer(String name){ try { return (GumballMachineRemote) Naming.lookup(name); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NotBoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }
测试类
package com.undergrowth.proxy.test; import static org.junit.Assert.*; import java.rmi.RemoteException; import javax.imageio.spi.RegisterableService; import org.junit.Test; import com.undergrowth.proxy.GumballMachine; import com.undergrowth.proxy.GumballMachineRemote; import com.undergrowth.proxy.RegisterRmiServer; public class GumballMachineRemoteTest { @Test public void test() { try { //创建远程服务 GumballMachineRemote remoteServer=new GumballMachine("广州萝岗", "科学大道高德汇", 100); //绑定 int port=4567; RegisterRmiServer.registerRmi(port,"rmi://192.168.1.101:"+port+"/remoteServer", remoteServer); //等待客户端的查找RMI服务 while(true){ } } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Test public void testClient(){ int port=4567; GumballMachineRemote remoteServer=RegisterRmiServer.getRmiServer("rmi://192.168.1.101:"+port+"/remoteServer"); try { System.out.println(remoteServer.getCount()); System.out.println(remoteServer.getLocation()); System.out.println(remoteServer.getDescription()); System.out.println(remoteServer.getCurrState()); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
先test(),然后testClient()
查看服务端口
控制台
100 广州萝岗 科学大道高德汇 com.undergrowth.proxy.NoMoneyState@193a6a5
2、虚拟代理,使用JFRAME加载图片组件,加载网络资源,使用状态模式管理图片的状态
面板
package com.undergrowth.proxy; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.swing.Icon; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; /** * 虚拟代理-->用于代理大开销的资源 * 使用代理 在未加载资源时,显示提示信息 * @author Administrator * */ public class LoadImageUrlFrame extends JFrame { /** * */ private static final long serialVersionUID = 1L; JMenu menu; JMenuBar menuBar; Map<String, String> imageResources = new HashMap<String, String>(); ImageComponent imageComponent; public LoadImageUrlFrame() { initResources(); menu = new JMenu("加载图片"); //添加菜单项 addMenuItem(menu); menuBar = new JMenuBar(); menuBar.add(menu); setJMenuBar(menuBar); //初始化图片 Icon icon=null; try { icon = new ImageProxy(new URL(imageResources.get("熊黛林"))); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } //添加图片组件 imageComponent=new ImageComponent(icon); getContentPane().add(imageComponent); // 初始化操作 setSize(800, 500); setLocation((getToolkit().getScreenSize().width-getWidth())/2, (getToolkit().getScreenSize().height-getHeight())/2); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); } /** * 添加菜单项 * @param menu2 */ private void addMenuItem(JMenu menu2) { // TODO Auto-generated method stub for (Iterator iterator = imageResources.keySet().iterator(); iterator.hasNext();) { String name = (String) iterator.next(); //添加菜单项 JMenuItem menuItem=new JMenuItem(name); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub //当点击菜单项的时候 //替换图片组件中的图片 并将frame重绘 try { //替换图像组件中的图片 imageComponent.setIcon(new ImageProxy(new URL(imageResources.get(e.getActionCommand())))); //重绘面板 repaint(); } catch (MalformedURLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } }); menu2.add(menuItem); } } /** * 初始化图片资源 */ private void initResources() { // TODO Auto-generated method stub imageResources .put("古力娜扎", "http://cdn.duitang.com/uploads/item/201207/27/20120727130449_hSxQk.jpeg"); imageResources.put("熊黛林", "http://ent.shangdu.com/stardata/P_5962597_1__1869907784.jpg"); } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub LoadImageUrlFrame frame = new LoadImageUrlFrame(); } }
图片组件
package com.undergrowth.proxy; import java.awt.Graphics; import javax.swing.Icon; import javax.swing.JComponent; public class ImageComponent extends JComponent { /** * */ private static final long serialVersionUID = 1L; Icon icon; public ImageComponent(Icon icon){ this.icon=icon; } /** * 绘制图片组件 */ @Override protected void paintComponent(Graphics g) { // TODO Auto-generated method stub super.paintComponent(g); int x=icon.getIconWidth(); int y=icon.getIconHeight(); x=(800-x)/2; y=(500-y)/2; icon.paintIcon(this, g, x, y); } public void setIcon(Icon icon) { this.icon = icon; } }
图片代理
package com.undergrowth.proxy; import java.awt.Component; import java.awt.Graphics; import java.net.URL; import javax.swing.Icon; import javax.swing.ImageIcon; public class ImageProxy implements Icon { ImageIcon icon; //图像的状态 使用状态模式 在加载与未加载之间切换 ImageState imageLoadState; ImageState imageNonLoadState; ImageState imageCurr; //代理图片的位置 URL imageUrl; public ImageProxy(URL imageUrl){ this.imageUrl=imageUrl; imageNonLoadState=new ImageNonLoadState(this); imageLoadState=new ImageLoadState(this); imageCurr=imageNonLoadState; } /** * 委托给当前状态进行处理 */ @Override public void paintIcon(Component c, Graphics g, int x, int y) { // TODO Auto-generated method stub imageCurr.paintIcon(c, g, x, y); } @Override public int getIconWidth() { // TODO Auto-generated method stub return imageCurr.getIconWidth(); } @Override public int getIconHeight() { // TODO Auto-generated method stub return imageCurr.getIconHeight(); } public ImageState getImageCurr() { return imageCurr; } public void setImageCurr(ImageState imageCurr) { this.imageCurr = imageCurr; } public ImageState getImageLoadState() { return imageLoadState; } public ImageState getImageNonLoadState() { return imageNonLoadState; } public ImageIcon getIcon() { return icon; } public void setIcon(ImageIcon icon) { this.icon = icon; } public URL getImageUrl() { return imageUrl; } }
图片状态接口
package com.undergrowth.proxy; import javax.swing.Icon; /** * 图像状态接口 * @author Administrator * */ public interface ImageState extends Icon{ }
图片已加载状态
package com.undergrowth.proxy; import java.awt.Component; import java.awt.Graphics; public class ImageLoadState implements ImageState { ImageProxy imageProxy; public ImageLoadState(ImageProxy imageProxy) { super(); this.imageProxy = imageProxy; } @Override public void paintIcon(Component c, Graphics g, int x, int y) { // TODO Auto-generated method stub imageProxy.getIcon().paintIcon(c, g, x, y); } @Override public int getIconWidth() { // TODO Auto-generated method stub return imageProxy.getIcon().getIconWidth(); } @Override public int getIconHeight() { // TODO Auto-generated method stub return imageProxy.getIcon().getIconHeight(); } }
图片未加载状态
package com.undergrowth.proxy; import java.awt.Component; import java.awt.Graphics; import javax.swing.ImageIcon; public class ImageNonLoadState implements ImageState { ImageProxy imageProxy; public ImageNonLoadState(ImageProxy imageProxy){ this.imageProxy=imageProxy; } @Override public void paintIcon(final Component c, Graphics g, int x, int y) { // TODO Auto-generated method stub g.drawString("正在加载图片中。。。", x, y); //使用线程 加载网络资源 加载完成后 重绘面板 new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub //修改图片状态 imageProxy.setIcon(new ImageIcon(imageProxy.getImageUrl())); imageProxy.setImageCurr(imageProxy.getImageLoadState()); //重绘 c.repaint(); } }).start(); } @Override public int getIconWidth() { // TODO Auto-generated method stub return 400; } @Override public int getIconHeight() { // TODO Auto-generated method stub return 400; } }
测试 运行面板
3、保护代理,用于控制权限访问
代理者与被代理者的共同接口
package com.undergrowth.proxy; /** * Person接口 用于代理和被代理的共同接口 * 用于实例保护代理 * @author Administrator * */ public interface Person { public String getName() ; public void setName(String name) ; public String getHobby(); public void setHobby(String hobby) ; public String getDescription() ; public void setDescription(String description); public float getScore() ; public void setScore(float score) ; }被代理者
package com.undergrowth.proxy; /** * 每个人都可以维护自己的信息 但是评分不能自己维护 需要别人维护 * @author Administrator * */ public class PersonBeanImpl implements Person{ String name; String hobby; String description; float score; public PersonBeanImpl(String name, String hobby, String description) { super(); this.name = name; this.hobby = hobby; this.description = description; this.score = 0; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public float getScore() { return score; } public void setScore(float score) { this.score = score; } @Override public String toString() { return "PersonBean [name=" + name + ", hobby=" + hobby + ", description=" + description + ", score=" + score + "]"; } }
代理工厂
package com.undergrowth.proxy; import java.lang.reflect.Proxy; /** * 简单工厂 * 利用JDK内置的代理 创建代理对象 * @author Administrator * */ public class ProxyFactory { public static Person getOwnerPersonBean(Person personBean){ return (Person) Proxy.newProxyInstance(personBean.getClass().getClassLoader(), personBean.getClass().getInterfaces(), new PersonBeanOwnerHandler(personBean)); } public static Person getNonOwnerPersonBean(Person personBean){ return (Person) Proxy.newProxyInstance(personBean.getClass().getClassLoader(), personBean.getClass().getInterfaces(), new PersonBeanNonOwnerHandler(personBean)); } }
拥有者,事件处理者
package com.undergrowth.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 自己处理器(拥有者) 自己可以维护自己的信息 除了评分以外 * @author Administrator * */ public class PersonBeanOwnerHandler implements InvocationHandler { Person personBean; public PersonBeanOwnerHandler(Person personBean) { super(); this.personBean = personBean; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub if(method.getName().startsWith("get")){//获取自己的其他信息 return method.invoke(personBean, args); }else if(method.getName().equals("setScore")){ throw new IllegalAccessException("你不能自己给自己评分"); }else if(method.getName().startsWith("set")){ //设置自己的其他信息 return method.invoke(personBean, args); } return null; } }
非拥有者
package com.undergrowth.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 不是自己处理器(非拥有者) 可以获取信息 可以设置其他人的评分 但是不能设置其他人的其他信息 * @author Administrator * */ public class PersonBeanNonOwnerHandler implements InvocationHandler { Person personBean; public PersonBeanNonOwnerHandler(Person personBean) { super(); this.personBean = personBean; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub if(method.getName().startsWith("get")){ return method.invoke(personBean, args); }else if (method.getName().equals("setScore")) { return method.invoke(personBean, args); }else if (method.getName().startsWith("set")) { throw new IllegalAccessException("你不能设置其他人的其他信息"); } return null; } }
测试
package com.undergrowth.proxy.test; import static org.junit.Assert.*; import java.lang.reflect.InvocationHandler; import org.junit.Test; import com.undergrowth.proxy.Person; import com.undergrowth.proxy.PersonBeanImpl; import com.undergrowth.proxy.PersonBeanOwnerHandler; import com.undergrowth.proxy.ProxyFactory; public class PersonBeanTest { @Test public void test() { PersonBeanImpl personBean = new PersonBeanImpl("科比", "篮球", "职业素养最好的运动员"); System.out.println(personBean); //创建拥有者 Person personBeanOwner = ProxyFactory.getOwnerPersonBean(personBean); System.out.println(personBeanOwner.getName()); personBeanOwner.setDescription(personBeanOwner.getDescription() + ",老将中的战斗机"); System.out.println(personBeanOwner.getDescription()); System.out.println(personBean); try { // 自己给自己评分 会发生异常 personBeanOwner.setScore(100); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); System.out.println(personBean); } //创建非拥有者 Person personNonOwner=ProxyFactory.getNonOwnerPersonBean(personBean); System.out.println(personNonOwner.getDescription()); personNonOwner.setScore(100); System.out.println(personBean); try { //设置别人的信息 会发生异常 personNonOwner.setDescription("足球踢得非常好"); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); System.out.println(personBean); } } }
控制台
PersonBean [name=科比, hobby=篮球, description=职业素养最好的运动员, score=0.0] java.lang.reflect.UndeclaredThrowableException at $Proxy4.setScore(Unknown Source) at com.undergrowth.proxy.test.PersonBeanTest.test(PersonBeanTest.java:29) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) 科比 职业素养最好的运动员,老将中的战斗机 PersonBean [name=科比, hobby=篮球, description=职业素养最好的运动员,老将中的战斗机, score=0.0] at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) Caused by: java.lang.IllegalAccessException: 你不能自己给自己评分 at com.undergrowth.proxy.PersonBeanOwnerHandler.invoke(PersonBeanOwnerHandler.java:29) ... 25 more PersonBean [name=科比, hobby=篮球, description=职业素养最好的运动员,老将中的战斗机, score=0.0] 职业素养最好的运动员,老将中的战斗机 PersonBean [name=科比, hobby=篮球, description=职业素养最好的运动员,老将中的战斗机, score=100.0] java.lang.reflect.UndeclaredThrowableException at $Proxy4.setDescription(Unknown Source) at com.undergrowth.proxy.test.PersonBeanTest.test(PersonBeanTest.java:42) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) Caused by: java.lang.IllegalAccessException: 你不能设置其他人的其他信息 at com.undergrowth.proxy.PersonBeanNonOwnerHandler.invoke(PersonBeanNonOwnerHandler.java:31) ... 25 more PersonBean [name=科比, hobby=篮球, description=职业素养最好的运动员,老将中的战斗机, score=100.0]
以上即是远程代理、虚拟代理、保护代理的示例代码,代理模式的应用点太多了,后续遇到或者学习到,持续更新中,记录学习的脚步
posted on 2014-12-21 16:01 liangxinzhi 阅读(136) 评论(0) 编辑 收藏 举报