1.目前任务:
上一章的万能糖果机的CEO,希望我们能发给它一份库存以及机器状态的报告。(简直就是小cese,CEO一定会被我的编码能力折服的hiahiahia~)
(1)在上一章的代码中加入location变量(2)然后添加糖果机监视器这个类。有一个问题:我点击运行后控制台什么反应都没有,我也不能输入信息。这是什么情况?
1 public class GumballMachine { 2 String location; 3 int count = 0; 4 public GumballMachine(String location, int count) { 5 super(); 6 //其他代码 7 this.location = location; 8 } 9 10 public String getLocation() { 11 return location; 12 } 13 14 //其他方法 15 }
1 import unit10.state.GumballMachine; 2 3 public class GumballMonitor { 4 GumballMachine machine; 5 6 public GumballMonitor(GumballMachine machine) { 7 super(); 8 this.machine = machine; 9 } 10 11 public void report(){ 12 System.out.println("gumball machine: "+machine.getLocation()); 13 System.out.println("current inventory: "+machine.getCount()); 14 System.out.println("current state: "+machine.getState()); 15 } 16 }
1 import unit10.state.GumballMachine; 2 3 public class GumballMachineTestDrive { 4 public void main(String[] args){ 5 int count = 0; 6 //利用命令行来传入位置和数目 7 if(args.length<2){ 8 System.out.println("GumballMachine <name><inventory>"); 9 System.exit(1); 10 } 11 12 count = Integer.parseInt(args[1]); 13 GumballMachine machine = new GumballMachine(args[0],count); 14 GumballMonitor monitor = new GumballMonitor(machine); 15 16 monitor.report(); 17 //其他测试代码 18 } 19 }
但是问题来了:CEO想要的是远程监控糖果机,而现在监视器和糖果机在同一个JVM上运行!
解决办法:远程代理。远程代理就好比是“远程对象的本地代表”。远程对象:活在不同java虚拟机(jvm)堆中。本地代表:可以由本地方法调用的对象,其行为会转发到远程对象中。这里的“本地堆”就是ceo的桌面,它其实是和代理在聊天,它以为他在和真正的糖果机聊天。"远程堆"就是糖果机,它才是真正做事的。
(1)了解RMI
(2)把gumballMachine变成远程服务,提供一些可被远程调用的方法。
(3)创建一个能和远程gumballMachine沟通的代理proxy(这里需要用到RMI),然后和监视系统GumballMonitor结合。
2.RMI
JAVA已经内置远程调用的功能了,我们只需要修改下代码,使其符合RMI的要求即可。
客户对象---客户辅助对象(stub)---服务辅助对象(skeleton)--服务对象
(1)将一个普通对象变成可以被远程客户调用的服务:
a.制作远程接口:客户辅助对象(stub)和服务对象实现此接口。
b.制作远程的实现:真正的行为实现。
c.利用rmic产生的stub和skeleton:这就是客户和服务的辅助类,当我们运行rmic工具时,这都会自动处理。
d.启动RMI registry:就像一个电话簿,所有注册的服务都在这里。
e.开启远程服务:注册服务,开始运行。
1 import java.rmi.Remote; 2 import java.rmi.RemoteException; 3 4 public interface MyRemote extends Remote { 5 //因为远程方法调用会用到网络和I/O,所以必须考虑成有风险的 6 //方法的返回类型必须是primitive类型或者serializable类型,因为需要通过网络运送 7 public String sayHello() throws RemoteException; 8 }
1 import java.rmi.Naming; 2 import java.rmi.RemoteException; 3 import java.rmi.server.UnicastRemoteObject; 4 5 public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote { 6 7 protected MyRemoteImpl() throws RemoteException { 8 super(); 9 } 10 11 public String sayHello(){ 12 return "server says: hey"; 13 } 14 15 public static void main(String[] args){ 16 try { 17 //新建一个远程对象 18 MyRemote service = new MyRemoteImpl(); 19 //将远程对象绑定到RMI regstry(类似电话簿的东西)中,注册名为RemoteHello, 20 //用户client就通过这个名字去找对应的代理:“我要找名字叫做RemoteHello”的stub 21 Naming.rebind("RemoteHello", service); 22 } catch (Exception e) { 23 e.printStackTrace(); 24 } 25 } 26 }
我的环境变量是对的,所以还是不知道是什么原因。
已解决:书上的命令是%rmic,但是我要打rmic才是对的,%不需要,那只是它的输入提示符,不是命令部分的东西。
(2)获取sub对象:当服务为绑定到RMI registry中时,其实真正被绑定的是stub,所以可以通过lookup找出来。
1 import java.rmi.*; 2 3 public class MyRemoteClient { 4 public static void main(String[] args){ 5 new MyRemoteClient().go(); 6 } 7 8 public void go() { 9 try { 10 MyRemote service = (MyRemote) Naming.lookup("rmi://10.15.199.179/RemoteHello"); 11 String s = service.sayHello(); 12 System.out.println(s); 13 } catch (Exception e) { 14 e.printStackTrace(); 15 } 16 } 17 }
3.让gumballMachine成为远程服务:
(1)为gumballMachine创建远程接口。
(2)确认接口方法的所有返回类型都是可序列化的:在这里state接口不是可序列化的,所以要extends Serializable。但是没完呢!上一章程序中,我们
(3)在gumballMachine中实现此接口。
1 import java.rmi.*; 2 import unit10.state.State; 3 4 public interface GumballMachineRemote extends Remote { 5 public int getCount() throws RemoteException; 6 public String getLocation() throws RemoteException; 7 public State getState() throws RemoteException; 8 }
1 import java.io.Serializable; 2 3 public interface State extends Serializable{ 4 ... 5 }