代理模式:为一个对象提供一个替身,以控制这个对象的访问。被代理对象可以使远程对象、创建开销大的对象或需要安全控制的对象。
接下来介绍一个远程监控程序,通过代理模式如何实现监控功能。
这个例子是在本地监控多台远端的糖果机,要求获取他们的位置以及状态。通过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));
}
}
三、缓存代理
通俗来讲,比如手机第一次访问网络上的图片后,缓存起来,下次访问的时候先从本地读取。
四、防火墙代理
在局域网的机器通过访问防火墙的形式,间接访问互联网。
不怎么华丽的分割线
代理模式和装饰模式的区别
代理模式不增加新功能,是对已有功能的访问控制
装饰模式增加新功能,对已有对象的扩张。