还是那个糖果机,CEO想要获得对糖果机的远程监控,获取机器状态,库存等。(果断想到了RMI)
我们会把GumballMachine变成远程服务,提供一些可以被调用的方法,然后创建一个能和GumballMachine沟通的代理,这需要用到RMI。最后再结合监视系统,CEO就可以监视任何数量的远程糖果机了。
步骤:0、制作远程接口1、制作远程的实现2、利用rmic产生的stub和skeleton3、启动RMIregistry4、开始远程服务
服务器端的代码:
远程接口:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface GumballMachineRemote extends Remote{
public int getCount() throws RemoteException;
public String getLocation() throws RemoteException;
public State getState() throws RemoteException;
}
State不是可序列的 所以要修改下:
import java.io.Serializable;
public interface State extends Serializable{
public void insertQuarter();
public void ejectQuarter();
public void turnCrank();
public void dispense();
}
这样子类中的State就可以在网上传播了。
但是我们不希望整个糖果机都被序列化,需要在GumballMachine实例变量前面加上transient关键字。
GumballMachine类实现GumballMachineRemote接口:
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class GumballMachine extends UnicastRemoteObject implements GumballMachineRemote{
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State winnerState;
String location;
State state=soldOutState;//跟踪当前状态,初始为 售罄
int count=0;//追踪机器内的糖果数量
public GumballMachine(String location,int count)throws RemoteException{
soldOutState=new SoldOutState(this);
noQuarterState=new NoQuarterState(this);
hasQuarterState=new HasQuarterState(this);
soldState=new SoldState(this);
winnerState=new WinnerState(this);
this.location=location;
this.count=count;
if(count>0)
state=noQuarterState;//如果库存不为零,机器进入 没有25分钱状态
}
public String getLocation(){
return location;
}
public void insertQuarter(){
state.insertQuarter();
}
public void ejectQuarter(){
state.ejectQuarter();
}
public void turnCrank(){
state.turnCrank();
state.dispense();
}
void setState(State state){
this.state=state;
}
public State getState(){
return this.state;
}
void releaseBall(){
System.out.println("A gumball comes rolling out the slot...");
if(count!=0)
count=count-1;
}
public State getHasQuarterState(){
return hasQuarterState;
}
public int getCount(){
return count;
}
public State getNoQuarterState(){
return noQuarterState;
}
public State getSoldState(){
return soldState;
}
public State getSoldOutState(){
return soldOutState;
}
public State getWinnerState(){
return winnerState;
}
}
注册启动服务:
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.util.Scanner;
public class GumballMachineTestDrive {
public static void main(String[]args){
GumballMachineRemote gumballMachine=null;
int count;
Scanner in=new Scanner(System.in);
int oo=in.nextInt();
try{
count=oo;
gumballMachine=new GumballMachine("santafe.mightygumball.com", count);
LocateRegistry.createRegistry(1099);
Naming.rebind("gumballmachine", gumballMachine);
}catch(Exception e){
e.printStackTrace();
}
}
}
然后是GumballMonitor客户端:
import java.rmi.RemoteException;
public class GumballMonitor {
GumballMachineRemote machine;
public GumballMonitor(GumballMachineRemote machine){
this.machine=machine;
}
public void report(){
try{
System.out.println("Gumball Machine: "+machine.getLocation());
System.out.println("Current inventory: "+machine.getCount()+" gumballs");
System.out.println("Current State: "+machine.getState());
}catch(RemoteException e){
e.printStackTrace();
}
}
}
监视器测试程序:
import java.rmi.Naming;
public class GumballMonitorTestDrive {
public static void main(String[]args){
String location="rmi://127.0.0.1/gumballmachine";
GumballMonitor monitor=null;
try{
GumballMachineRemote machine=(GumballMachineRemote)Naming.lookup(location);
monitor=new GumballMonitor(machine);
//System.out.println(monitor);
}catch(Exception e){
e.printStackTrace();
}
monitor.report();
}
}
输入数量是555的话
Gumball Machine: santafe.mightygumball.com
Current inventory: 555 gumballs
Current State: NoQuarterState
定义代理模式:为另一个对象提供一个替身或占位符以控制对这个对象的访问。
上面是远程代理,接下来是另一种代理:虚拟代理。
我们打算建立一个应用程序,用来展现你最喜欢的CD封面。你可以建立一个CD标题菜单,然后从网站的在线服务中取得CD封面的图。如果使用swing,可以创建一个Icon接口从网络上加载图像。唯一的问题是,下载可能需要一些时间,所以在等待图像加载的时候,应该显示sth...
我们设计一个ImageProxy,首先创建一个ImageIcon,然后开始从网络URL上加载图像,在加载的过程中,ImageProxy显示“CD封面加载中,请稍后。。。”当图像加载完成,ImageProxy把所有方法调用委托给真正的ImageIcon,这些方法包括paintIcon()、getWidth()、getHeight()。如果用户请求新的图像,我们就创建新的代理,重复这样的过程。
编写ImageProxy:
public class ImageProxy implements Icon{
ImageIcon imageIcon;//真正的图像
URL imageUrl;
Thread retrievalThread;
boolean retrieving=false;
public ImageProxy(URL url){
imageUrl=url;
}
//在图像加载完毕前,返回默认的宽和高,图像加载完毕后,转给imageIcon处理
public int getIconWidth(){
if(imageIcon!=null){
return imageIcon.getIconWidth();
}else {
return 800;
}
}
public int getIconHeight(){
if(imageIcon!=null){
return imageIcon.getIconHeight();
}else {
return 600;
}
}
public void paintIcon(final Component c,Graphics g,int x,int y){
if(imageIcon!=null){
imageIcon.paintIcon(c, g, x, y);//如果有Icon,就画出自己
}else {//否则显示加载中的消息
g.drawString("Loading CD cover. please wait...", x+300, y+190);
if(!retrieving){//如果还没有试着取出图像
retrieving=true;//那么就开始取出图像
retrievalThread=new Thread(new Runnable() {
//我们不希望挂起整个用户界面,所以用另一个线程取出图像
@Override
public void run() {
// TODO Auto-generated method stub
try{
imageIcon=new ImageIcon(imageUrl,"CD cover");
//在线程中,我们实例化此icon对象,其构造器会在图像加载完成后才返回
c.repaint();
}catch(Exception e){
e.printStackTrace();
}
}
});
retrievalThread.start();
}
}
}
//可以用状态模式清理这些if语句
}
测试CD封面浏览器:
public class ImageProxyTestDrive {
ImageComponent imageComponent;
JFrame frame=new JFrame("CD cover viewer");
JMenuBar menuBar;
JMenu menu;
Hashtable cds= new Hashtable<>();
public static void main(String[]args)throws Exception{
ImageProxyTestDrive testDrive=new ImageProxyTestDrive();
}
public ImageProxyTestDrive()throws Exception{
cds.put("meinv1", "http://game.online.cq.cn/uploadfile/2012/0113/20120113104548732.jpg");
cds.put("meinv2", "http://pic10.nipic.com/20101023/1304280_092014003671_2.jpg");
cds.put("meinv3", "http://pic4.nipic.com/20091014/1295091_150512051562_2.jpg");
cds.put("meinv4", "http://pic6.nipic.com/20100417/1304280_085257009323_2.jpg");
cds.put("meinv5", "http://pic6.nipic.com/20100417/4713361_100503544816_2.jpg");
cds.put("meinv6", "http://pic.4j4j.cn/upload/pic/20140331/e6b50c91eb.jpg");
cds.put("meinv7", "http://iphone.images.paojiao.cn/iphone/paper/20116/30/74970434/paojiao_3a066a72.jpg");
URL initialUrl=new URL((String)cds.get("meinv5"));
menuBar=new JMenuBar();
menu=new JMenu("Favorite CDs");
menuBar.add(menu);
frame.setJMenuBar(menuBar);
for(Enumeration e=cds.keys();e.hasMoreElements();){
String name=(String)e.nextElement();
JMenuItem menuItem=new JMenuItem(name);
menu.add(menuItem);
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
imageComponent.setIcon(new ImageProxy(getCDUrl(e.getActionCommand())));
frame.repaint();
}
});
}
Icon icon=new ImageProxy(initialUrl);
imageComponent=new ImageComponent(icon);
frame.getContentPane().add(imageComponent);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
URL getCDUrl(String name){
try{
return new URL((String)cds.get(name));
}catch(MalformedURLException e){
e.printStackTrace();
return null;
}
}
}
ImageProxy有点像是装饰者,但是目的是不一样的,装饰者为对象增加行为,而代理是控制对象的访问。
上面我们是创建新的ImageProxy来取得图像,即使图像已经被取回来过。所以我们可以把加载过的图像放在缓存中,就是缓存代理。它会维护之前创建的对象,当收到请求时,在可能等情况下返回缓存对象。
下面使用JAVA API的代理,创建一个保护代理,其实就是动态代理。
约会服务系统中可以加入“hot”或者“not”的评鉴,涉及到一个PersonBean,允许设置或取得一个人的信息。
public interface PersonBean {
void initialInfo(String name,String gender,String interests,int rating,int ratingCount);
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;
public void initialInfo(String name,String gender,String interests,int rating,int ratingCount){
this.name=name;
this.gender=gender;
this.interests=interests;
this.rating=rating;
this.ratingCount=ratingCount;
}
public String getName() {
// TODO Auto-generated method stub
return name;
}
public String getGender() {
// TODO Auto-generated method stub
return gender;
}
public String getInterests() {
// TODO Auto-generated method stub
return interests;
}
public int getHotOrNotRating() {
// TODO Auto-generated method stub
if(rating==0)
return 0;
return (rating/ratingCount);
}
public void setName(String name) {
// TODO Auto-generated method stub
this.name=name;
}
public void setGender(String gender) {
// TODO Auto-generated method stub
this.gender=gender;
}
public void setInterests(String interests) {
// TODO Auto-generated method stub
this.interests=interests;
}
public void setHotOrNotRating(int rating) {
// TODO Auto-generated method stub
this.rating+=rating;
ratingCount++;
}
}
为PersonBean创建动态代理:
首先创建两个InvocationHandler,创建动态代理,利用适当的代理包装任何PersonBean对象。
如果不是顾客自己(拥有者),就是另一个顾客正在检查的服务使用者(非拥有者)
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;
}
public Object invoke(Object proxy,Method method,Object[]args)throws IllegalAccessException{
try{
if(method.getName().startsWith("get"))
return method.invoke(person, args);
else if (method.getName().startsWith("in")) {
return method.invoke(person, args);
}
else if (method.getName().equals("setHotOrNotRating")) {
throw new IllegalAccessException();
}else if (method.getName().startsWith("set")) {
return method.invoke(person, args);
}
}catch (InvocationTargetException e) {
// TODO: handle exception
e.printStackTrace();
}
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;
}
public Object invoke(Object proxy,Method method,Object[]args)throws IllegalAccessException{
try{
if(method.getName().startsWith("get"))
return method.invoke(person, args);
else if (method.getName().startsWith("in")) {
throw new IllegalAccessException();
}
else if (method.getName().equals("setHotOrNotRating")) {
return method.invoke(person, args);
}else if (method.getName().startsWith("set")) {
throw new IllegalAccessException();
}
}catch (InvocationTargetException e) {
// TODO: handle exception
e.printStackTrace();
}
return null;
}
}
测试配对服务:
import java.lang.reflect.Proxy;
public class MatchMakingTestDrive {
public static void main(String[]args){
MatchMakingTestDrive test=new MatchMakingTestDrive();
test.drive();
}
public void drive(){
PersonBean joe=new PersonBeanImpl();
PersonBean ownerProxy=(PersonBean)Proxy.newProxyInstance(joe.getClass().getClassLoader(),
joe.getClass().getInterfaces(),new OwnerInvocationHandler(joe));
ownerProxy.initialInfo("joe", "male", "Reading book", 8, 1);
System.out.println("Name is "+ownerProxy.getName());
ownerProxy.setInterests("bowling,go");
System.out.println("Interests set From owner proxy");
try{
ownerProxy.setHotOrNotRating(10);
}catch(Exception e){
System.out.println("can't set rating from owner proxy");
}
System.out.println("Rating is "+ownerProxy.getHotOrNotRating());
PersonBean nonPersonProxy=(PersonBean)Proxy.newProxyInstance(joe.getClass().getClassLoader(),
joe.getClass().getInterfaces(),new NonOwnerInvocationHandler(joe));
System.out.println("Name is "+nonPersonProxy.getName());
try{
nonPersonProxy.setInterests("bowling ,go");
}catch(Exception e){
System.out.println("can't see interests from non owner proxy");
}
nonPersonProxy.setHotOrNotRating(3);
System.out.println("Rating set from non owner proxy");
System.out.println("Rating is "+nonPersonProxy.getHotOrNotRating());
}
}
执行结果:
Name is joe
Interests set From owner proxy
can't set rating from owner proxy
Rating is 8
Name is joe
can't see interests from non owner proxy
Rating set from non owner proxy
Rating is 5
===================
还有其他代理:
防火墙代理
智能引用代理
缓存代理
同步代理
复杂隐藏代理
写入时复制代理
。。。