创建RMI程序有4个步骤:
1、定义一个远程接口的接口,该接口中的每一个方法必须声明它将产生一个RemoteException异常。
2、定义一个实现该接口的类
3、创建一个服务,用于发布2中定义的类。
4、创建一个客户端,进行RMI调用。
Student demo
程序demo
1、创建一个Student类。实现Serializable接口,用于信息的传输
public class Student implements Serializable {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}
2、定义一个接口
public interface IStudentService extends Remote {List<Student> getStudents() throws RemoteException;}
3、创建一个实现类
public class StudentServiceImpl extends UnicastRemoteObject implements IStudentService {protected StudentServiceImpl() throws RemoteException {}@Overridepublic List<Student> getStudents() throws RemoteException {List<Student> students = new ArrayList<>();for (int i = 1; i < 5; i++) {Student s = new Student();s.setName("Nick" + i);s.setAge(i + 20);students.add(s);}return students;}}
4、启动服务
public class RmiServer {public static void main(String[] args) {try {IStudentService studentService = new StudentServiceImpl();LocateRegistry.createRegistry(5000);Naming.rebind("rmi://127.0.0.1:5000/IStudentService", studentService);System.out.println("服务已经启动");}catch (Exception e){e.printStackTrace();}}}
5、客户端程序调用RMI方法
public class RmiClient {public static void main(String[] args) {try{IStudentService studentService = (IStudentService) Naming.lookup("rmi://127.0.0.1:5000/IStudentService");List<Student> students = studentService.getStudents();for (Student s: students){System.out.println("姓名:" + s.getName() + ",年龄:" + s.getAge());}}catch (Exception e){e.printStackTrace();}}}
显示结果:
姓名:Nick1,年龄:21姓名:Nick2,年龄:22姓名:Nick3,年龄:23姓名:Nick4,年龄:24
WordClock demo
目的:服务器会提供一个WorldClock服务,允许客户端获取指定时区的时间
1、要实现RMI,服务器和客户端必须共享同一个接口。定义一个WorldClock接口。
Java的RMI规定此接口必须派生自java.rmi.Remote,并在每个方法声明抛出RemoteException。
public interface WorldClock extends Remote {LocalDateTime getLocalDateTime(String zoneId) throws RemoteException;}
2、编写服务器的实现类,因为客户端请求的调用方法getLocalDateTime()最终会通过这个实现类返回结果。
public class WorldClockService implements WorldClock {@Overridepublic LocalDateTime getLocalDateTime(String zoneId) throws RemoteException {return LocalDateTime.now(ZoneId.of(zoneId)).withNano(0);}}
3、通过Java RMI提供的一系列底层支持接口,把上面编写的服务以RMI的形式暴露在网络上,客户端才能调用
public class RmiServer {public static void main(String[] args) throws RemoteException {System.out.println("create World clock remote service...");// 实例化一个WorldClock:WorldClock worldClock = new WorldClockService();// 将此服务转换为远程服务接口:WorldClock skeleton = (WorldClock) UnicastRemoteObject.exportObject(worldClock, 0);// 将RMI服务注册到1099端口:Registry registry = LocateRegistry.createRegistry(1099);// 注册此服务,服务名为 WorldClock:registry.rebind("WorldClock", skeleton);}}
上述代码主要目的是通过RMI提供的相关类,将我们自己的WorldClock实例注册到RMI服务上。RMI的默认端口是1099,最后一步注册服务时通过rebind()指定服务名称为”WorldClock”。
4、编写客户端代码。RMI要求服务器和客户端共享同一个接口,因此我们要把WorldClock.java这个接口文件复制到客户端,然后在客户端实现RMI调用:
public class RmiClient {public static void main(String[] args) throws RemoteException, NotBoundException {// 连接到服务器localhost,端口1099:Registry registry = LocateRegistry.getRegistry("localhost", 1099);// 查找名称为"WorldClock"的服务并强制转型为WorldClock接口:WorldClock worldClock = (WorldClock) registry.lookup("WorldClock");// 正常调用接口方法:LocalDateTime now = worldClock.getLocalDateTime("Asia/Shanghai");// 打印调用结果:System.out.println(now);}}
先运行服务器,再运行客户端。从运行结果可知,因为客户端只有接口,并没有实现类,因此,客户端获得的接口方法返回值实际上是通过网络从服务器端获取的。整个过程实际上非常简单,对客户端来说,客户端持有的WorldClock接口实际上对应了一个“实现类”,它是由Registry内部动态生成的,并负责把方法调用通过网络传递到服务器端。而服务器端接收网络调用的服务并不是我们自己编写的WorldClockService,而是Registry自动生成的代码。我们把客户端的“实现类”称为stub,而服务器端的网络服务类称为skeleton,它会真正调用服务器端的WorldClockService,获取结果,然后把结果通过网络传递给客户端。整个过程由RMI底层负责实现序列化和反序列化:
