首先,要提供一个服务的接口:
public interface MyRemote extends Remote {
public Duck getDuck() throws RemoteException;
}
其中Duck源码如下:
public class Duck implements Serializable {
private static final long serialVersionUID = 6695054579460725212L;
private String type;
private int age;
public Duck(String type, int age) {
this.type = type;
this.age = age;
}
// 省略getter和setter
public void quack() {
System.out.println("Quack! ");
}
}
然后,编写一个类来实现服务的接口:
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
private static final long serialVersionUID = 1387969923515492997L;
public MyRemoteImpl() throws RemoteException {}
@Override
public Duck getDuck() {
return new Duck("Ballard", 3);
}
}
接下来,编写服务器的启动器:
public class ServerLauncher {
public static void main(String[] args) {
try {
MyRemote remote = new MyRemoteImpl();
// 创建并导出接受指定port请求的本地主机上的Registry实例。
LocateRegistry.createRegistry(8887);
/**
* Naming 类提供在对象注册表中存储和获得远程对远程对象引用的方法
* Naming 类的每个方法都可将某个名称作为其一个参数,
* 该名称是使用以下形式的URL格式(没有scheme组件)的 java.lang.String:
* //host:port/name
* host:注册表所在的主机(远程或本地),省略则默认为本地主机
* port:是注册表接受调用的端口号,省略则默认为1099,RMI注册表registry使用的著名端口
* name:是未经注册表解释的简单字符串
*/
// Naming.bind("//host:port/name", h);
Naming.bind("rmi://localhost:8887/MyRemote", remote);
System.out.println("Ready");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
然后需要编写客户端的启动器:
public class ClientLauncher {
public static void main(String[] args) {
try {
MyRemote remote = (MyRemote) Naming.lookup("rmi://localhost:8887/MyRemote");
Duck d = remote.getDuck();
d.quack();
d.roam();
} catch (MalformedURLException e) {
System.out.println("url格式异常");
} catch (RemoteException e) {
System.out.println("创建对象异常");
e.printStackTrace();
} catch (NotBoundException e) {
System.out.println("对象未绑定");
}
}
}
客户端的Duck类比服务器端的Duck类多一个 roam()
方法,但是上面的程序可以正常运行,说明通过RMI传输的对象在服务器和客户端的定义不需要完全一致。
RMI注意事项:
- 客户端和服务器必须有相同的接口集
- 必须先启动服务器,然后再运行客户端
- 通过RMI传输的对象,类定义必须实现
Serializable
接口 - 通过RMI传输的对象,在客户端和服务器的类定义不需要完全一致,但二者的
serialVersionUID
必须相同,否则会抛出异常