RMI(Remote Method Invocation),即远程方法调用,是Java编程语言里一种用于实现远程过程调用的应用程序编程接口,使得客户机上运行的程序可以调用远程服务器上的对象。
Java RMI极大地依赖于接口。在需要创建一个远程对象的时候,开发者通过传递一个接口来隐藏底层的实现细节。好了,到现在读者对RMI可以做什么已经有一个初步的了解了吧。下面让我们马上动手,书写RMI的第一个小程序之HelloWorld。
首先来看下程序目录结构:
RMIServer和RMIClient用以模拟服务器端程序和客户端程序,运行环境为本机.
注意:
如读者所见,服务端RMIServer的 IHello.java与客户端RMIClient的 IHello.java相同. 即服务端与客户端共用同一个接口文件 IHello.java
服务端和客户端接口文件必须使用相同的包(Package)或不使用包
编码规范: 接口文件日常练习中以 I 开头,表示 Interface.
1. 构造服务器端程序(RMIServer)
1>创建远程接口( IHello.java)
package com.thera.rmi; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.Map; /** * @description RMI(Remote Method Invocation)远程方法调用接口 * @author Thera Santorini(E-mail: [email protected]) * 定义一个远程接口,继承Remote,抛出RemoteException异常 */ public interface IHello extends Remote { /** * @Description: 简单的返回"HelloWorld!"字符串 * @return String * @throws RemoteException */ public String helloWorld() throws RemoteException; /** * @Description: 传入参数为Map的复合类型, 返回字符串与调用者交互 * @return String * @throws RemoteException */ public String getPersonalInfo(Map<String, Object> infoMap) throws RemoteException; }
创建远程接口需要注意以下:
远程接口必须声明为public.
远程对象扩展java.rmi.Remote接口.
每个方法必须抛出java.rmi.RemoteException例外.
任何作为参数或返回值传送的远程对象的数据类型必须为远程接口类型(即本程序中的 IHello.java),而不是实现类 .
2> 创建远程接口实现类( HelloImpl.java)
package com.thera.rmi; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.Map; /** * @description 远程接口IHello的实现类 * @author Thera Santorini(E-mail: [email protected]) */ public class HelloImpl extends UnicastRemoteObject implements IHello { public HelloImpl() throws RemoteException { } @Override public String helloWorld() throws RemoteException { return "Hello World!"; } @Override public String getPersonalInfo(Map<String, Object> infoMap) throws RemoteException { return "姓名: " + infoMap.get("name") + "\n年龄: " + infoMap.get("age") + "\n我的博客: " + infoMap.get("blog"); } }
创建远程接口实现类需要注意以下:
必须继承 UnicastRemoteObject类(J2SE官方解释为: 用于导出带 JRMP 的远程对象和获得与该远程对象通信的 stub)
必须实现构造方法且抛出RemoteException例外
3> 编写和实现服务器类(HelloServer.java)
package com.thera.rmi; import java.net.MalformedURLException; import java.rmi.AlreadyBoundException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; /** * @description RMI服务器类, 指定端口上创建RMI注册表并启动RMI服务,将对象注册到RMI注册表 * @author Thera Santorini(E-mail: [email protected]) */ public class HelloServer { public static void main(String[] args) { try { // 创建一个远程对象 IHello hello = new HelloImpl(); // 启动注册表在端口8888上的监听 LocateRegistry.createRegistry(8888); // 将名称绑定到对象 Naming.bind("rmi://localhost:8888/Hello", hello); System.out.println(">>>>>Info:远程IHello对象绑定成功"); } catch (RemoteException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (AlreadyBoundException e) { e.printStackTrace(); } } }
2. 构造客户端程序(RMIClient)
package com.thera.rmi; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.util.HashMap; import java.util.Map; /** * @description 客户端调用远程接口的指定方法 * @author Thera Santorini(E-mail: [email protected]) */ public class HelloClient { public static void main(String[] args) { try { // 调用远程方法(localhost表示本机,开发中为服务端的IP地址) IHello hello = (IHello)Naming.lookup("rmi://localhost:8888/Hello"); // 调用远程方法之 helloWorld System.out.println(hello.helloWorld()); Map<String, Object> map = new HashMap<String, Object>(); map.put("name", "Santorini"); map.put("age", 22); map.put("blog", "http://my.oschina.net/u/2265030"); // 调用远程方法之 getPersonalInfo System.out.println(hello.getPersonalInfo(map)); } catch (RemoteException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (NotBoundException e) { e.printStackTrace(); } } }
创建RMI客户端程序需注意以下:
copy一份服务端的接口源码(即 IHello)到客户端,且保证包名与服务端相同
Naming.lookup(String name); // name参数与服务端中Naming.bind(String name, Remote obj)中的name相同
到此为止,编码完成。让我们赶紧看看运行结果。
首先,启动服务端程序(RMIServer.java)
然后,启动客户端程序(RMIClient.java)