标签:
代理模式:为一个对象提供一个替身,以控制这个对象的访问。被代理对象可以使远程对象、创建开销大的对象或需要安全控制的对象。
接下来介绍一个远程监控程序,通过代理模式如何实现监控功能。
这个例子是在本地监控多台远端的糖果机,要求获取他们的位置以及状态。通过RMI,建立本地与远程机器的连接作为代理。
通过这个代理,我们能够查看他们的状态,实现监控。
对RMI不熟悉的朋友,可以出门左转,我有一篇关于RMI简单介绍的博文。
远程接口:
import java.rmi.Remote; import java.rmi.RemoteException; import com.java.jikexueyuan.agentmode.candymachine.State; public interface CandyMachineRemote extends Remote{ public String getLocation() throws RemoteException; public int getCount() throws RemoteException; public State getstate() throws RemoteException; }
在目标机器上运行的远程接口实现:
import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import com.java.jikexueyuan.agentmode.candymachinermi.CandyMachineRemote; public class CandyMachine extends UnicastRemoteObject implements CandyMachineRemote{ State mSoldOutState; State mOnReadyState; State mHasCoin; State mSoldState; State mWinnerState; private String location=""; private State state; private int count = 0; public CandyMachine(String location,int count) throws RemoteException{ this.location=location; this.count = count; mSoldOutState = new SoldOutState(this); mOnReadyState = new OnReadyState(this); mHasCoin = new HasCoin(this); mSoldState = new SoldState(this); mWinnerState = new WinnerState(this); if (count > 0) { state = mOnReadyState; } else { state = mSoldOutState; } } public String getLocation() { return location; } public void setState(State state) { this.state = state; } public void insertCoin() { state.insertCoin(); } public void returnCoin() { state.returnCoin(); } public void turnCrank() { state.turnCrank(); state.dispense(); } void releaseCandy() { // TODO Auto-generated method stub if (count > 0) { count = count - 1; System.out.println("a candy rolling out!"); } } public int getCount() { return count; } public void printstate() { state.printstate(); } public State getstate() { return state; } }
在远端机器上注册RMI服务,并且启动:
import java.rmi.Naming; import java.rmi.registry.LocateRegistry; import com.java.jikexueyuan.agentmode.candymachine.CandyMachine; import com.java.jikexueyuan.agentmode.rmi.MyRemote; import com.java.jikexueyuan.agentmode.rmi.MyRemoteImpl; public class RemoteMainTest { public static void main(String[] args) { try { CandyMachine service = new CandyMachine("test1", 7); // LocateRegistry.createRegistry(6602); Naming.rebind("rmi://127.0.0.1:6602/test1", service); service.insertCoin(); service = new CandyMachine("test2", 5); Naming.rebind("rmi://127.0.0.1:6602/test2", service); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println(e.toString()); } } }
监控类:我们可以看到,监控类通过数组维持了对所有目标机器的引用,通过循环获取所有机器的状态
import com.java.jikexueyuan.agentmode.candymachine.CandyMachine; public class Monitor { private ArrayList<CandyMachineRemote> candyMachinelst; public Monitor() { candyMachinelst = new ArrayList<CandyMachineRemote>(); } public void addMachine(CandyMachineRemote mCandyMachine) { candyMachinelst.add(mCandyMachine); } public void report() { CandyMachineRemote mCandyMachine; for (int i = 0, len = candyMachinelst.size(); i < len; i++) { mCandyMachine = candyMachinelst.get(i); try { System.out .println("Machine Loc:" + mCandyMachine.getLocation()); System.out.println("Machine Candy count:" + mCandyMachine.getCount()); System.out.println("Machine State:" + mCandyMachine.getstate().getstatename()); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
测试类:建立和所有机器的连接,传给监控类,调用report函数获取监控数据
import java.rmi.Naming; import com.java.jikexueyuan.agentmode.candymachine.CandyMachine; import com.java.jikexueyuan.agentmode.rmi.MyRemote; public class MainTest { public static void main(String[] args) { Monitor mMonitor = new Monitor(); try { CandyMachineRemote mCandyMachine = (CandyMachineRemote) Naming .lookup("rmi://127.0.0.1:6602/test1"); mMonitor.addMachine(mCandyMachine); mCandyMachine = (CandyMachineRemote) Naming .lookup("rmi://127.0.0.1:6602/test2"); mMonitor.addMachine(mCandyMachine); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } mMonitor.report(); } }
华丽的分割线
上面的介绍太过简单,怎么对得起正在看本文的客官呢,下面介绍更多干货。
一、虚拟代理
为创建开销大的对象提供代理服务。举个栗子,用过hibernate的童鞋应该都知道,session.load方法提供的懒加载功能就是这种。当真正使用的时候,该方法返回的对象只不过是一个代理而没有数据。
还有,Android异步加载网络图片的时候的image,其实也是虚拟代理。
二、动态代理
关于动态代理,其实我们并不陌生,Spring的重要概念AOP就是通过Java的动态代理来实现的。
这里有两个重要的组件必须介绍,分别是Proxy类和InnovacationHandler接口。
举个栗子,加入我们要开发一个类似facebook一样给别人评分的功能。规则是自己能够设置性别、名字、爱好等信息,但是不能给自己打分;而其他人能够给自己打分,但是不能篡改自己性别、姓名等信息。
个人信息类如下:
public interface PersonBean { String getName(); String getGender(); String getInterests(); int getHotOrNotRating(); void setName(String name); void setGender(String gender); void setInterests(String interests); void setHotOrNotRating(int rating); } public class PersonBeanImpl implements PersonBean{ String name; String gender; String interests; int rating; int ratingcount=0; @Override public String getName() { // TODO Auto-generated method stub return name; } @Override public String getGender() { // TODO Auto-generated method stub return gender; } @Override public String getInterests() { // TODO Auto-generated method stub return interests; } @Override public int getHotOrNotRating() { // TODO Auto-generated method stub if(ratingcount==0) return 0; return (rating/ratingcount); } @Override public void setName(String name) { // TODO Auto-generated method stub this.name=name; } @Override public void setGender(String gender) { // TODO Auto-generated method stub this.gender=gender; } @Override public void setInterests(String interests) { // TODO Auto-generated method stub this.interests=interests; } @Override public void setHotOrNotRating(int rating) { // TODO Auto-generated method stub this.rating=rating; ratingcount++; } }
对于自己,我们通过实现Invocationhandler类来实现规则:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class OwnerInvocationHandler implements InvocationHandler{ PersonBean person; public OwnerInvocationHandler(PersonBean person) { this.person=person; } @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(person,args); }else if(method.getName().equals("setHotOrNotRating")) { return new IllegalAccessException(); }else if(method.getName().startsWith("set")) { return method.invoke(person,args); } return null; } }
对于其他人
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class NonOwnerInvocationHandler implements InvocationHandler{ PersonBean person; public NonOwnerInvocationHandler(PersonBean person) { this.person=person; } @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(person,args); }else if(method.getName().equals("setHotOrNotRating")) { return method.invoke(person,args); }else if(method.getName().startsWith("set")) { return new IllegalAccessException(); } return null; } }
我们仔细分析下面代码,通过getOwnerProxy方法和getNonOwnerProxy两个方法,返回不同的代理。
前者能够给自己设置性别、名字、兴趣,但是不能修改评分。
而后者相反。通过代理,使不同身份的用户访问得到控制,达到信息安全的目的。是不是很神奇~~~
InvocationHandler用来定义哪些功能能访问,哪些不能访问。而Proxy跟InvocationHandler合作,返回代理。
import java.lang.reflect.Proxy; public class MatchService { public MatchService() { PersonBean joe = getPersonInfo("joe", "male", "running"); PersonBean OwnerProxy = getOwnerProxy(joe); System.out.println("Name is " + OwnerProxy.getName()); System.out.println("Interests is " + OwnerProxy.getInterests()); OwnerProxy.setInterests("Bowling"); System.out.println("Interests are " + OwnerProxy.getInterests()); OwnerProxy.setHotOrNotRating(50); System.out.println("Rating is " + OwnerProxy.getHotOrNotRating()); OwnerProxy.setHotOrNotRating(40); System.out.println("Rating is " + OwnerProxy.getHotOrNotRating()); System.out.println("**************"); PersonBean nonOwnerProxy = getNonOwnerProxy(joe); System.out.println("Name is " + nonOwnerProxy.getName()); System.out.println("Interests are " + nonOwnerProxy.getInterests()); nonOwnerProxy.setInterests("haha"); System.out.println("Interests are " + nonOwnerProxy.getInterests()); nonOwnerProxy.setHotOrNotRating(60); System.out.println("Rating is " + nonOwnerProxy.getHotOrNotRating()); } PersonBean getPersonInfo(String name, String gender, String interests) { PersonBean person = new PersonBeanImpl(); person.setName(name); person.setGender(gender); person.setInterests(interests); return person; } PersonBean getOwnerProxy(PersonBean person) { return (PersonBean) Proxy.newProxyInstance(person.getClass() .getClassLoader(), person.getClass().getInterfaces(), new OwnerInvocationHandler(person)); } PersonBean getNonOwnerProxy(PersonBean person) { return (PersonBean) Proxy.newProxyInstance(person.getClass() .getClassLoader(), person.getClass().getInterfaces(), new NonOwnerInvocationHandler(person)); } }
三、缓存代理
通俗来讲,比如手机第一次访问网络上的图片后,缓存起来,下次访问的时候先从本地读取。
四、防火墙代理
在局域网的机器通过访问防火墙的形式,间接访问互联网。
不怎么华丽的分割线
代理模式和装饰模式的区别
代理模式不增加新功能,是对已有功能的访问控制
装饰模式增加新功能,对已有对象的扩张。
标签:
原文地址:http://my.oschina.net/gaohongtian/blog/491927