我们都站在巨人的肩膀上
全称:
- Remote Method Invocation
组成:
- 远程服务的接口定义
- 远程服务接口的具体实现
- 桩(Stud)和框架(Skeleton)文件
- 一个运行远程服务的服务器
- 一个RMI命名服务,它允许客户端去发现这个远程服务
- 类文件的提供者(一个http或者ftp服务器)
- 一个需要这个远程服务的客户端
示意图:
步骤:
1. 生成一个远程接口
2. 实现远程对象(服务器端程序)
3. 生成占位程序和骨干网(服务器端程序)
4. 编写服务器程序
5. 编写客户程序
6. 注册远程对象
7. 启动远程对象
RMI的优点:
- 面向对象:RMI可将完整的对象作为参数和返回值进行传递,而不仅仅是预定义的数据类型。
- 可移动属性:RMI可将属性(类实现程序)从客户机移动到服务器,或者从服务器移到客户机。
- 设计方式:对象传递功能使您可以在分布式计算中充分利用面向对象技术的强大功能,如二层和三层结构系统。如果您能够传递属性,那么您就可以在您的解决方案中使用面向对象的设计方式。所有面向对象的设计方式无不依靠不同的属性来发挥功能,如果不能传递完整的对象–包括实现和类型–就会失去设计方式上所提供的优点。
- 安 全:RMI使用Java内置的安全机制保证下载执行程序时用户系统的安全。RMI使用专门为保护系统免遭恶意小应用程序侵害而设计的安全管理程序,可保护您的系统和网络免遭潜在的恶意下载程序的破坏。在情况严重时,服务器可拒绝下载任何执行程序。
- 便于编写和使用:RMI使得Java远程服务程序和访问这些服务程序的Java客户程序的编写工作变得轻松、简单。
- 可连接现有/原有的系统:RMI可通过Java的本机方法接口JNI与现有系统进行进行交互。利用RMI和JNI,您就能用Java语言编写客户端程序,还能使用现有的服务器端程序。在使用RMI/JNI与现有服务器连接时,您可以有选择地用Java重新编写服务程序的任何部分,并使新的程序充分发挥Java的功能。类似地,RMI可利用JDBC、在不修改使用数据库的现有非Java源代码的前提下与现有关系数据库进行交互。
- 编写一次,到处运行:RMI是Java“编写一次,到处运行 ”方法的一部分。
- 分布式垃圾收集:RMI采用其分布式垃圾收集功能收集不再被网络中任何客户程序所引用的远程服务对象。与Java 虚拟机内部的垃圾收集类似,分布式垃圾收集功能允许用户根据自己的需要定义服务器对象,并且明确这些对象在不再被客户机引用时会被删除。
- 并行计算:RMI采用多线程处理方法,可使您的服务器利用这些Java线程更好地并行处理客户端的请求。
demo 示例:
Person 类:
public class Person implements Serializable {
private static final long serialVersionUID = 6457034708207555367L;
private String id;
private String name;
private String age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public Person(String id, String name, String age) {
this.id = id;
this.name = name;
this.age = age;
}
public Person() {
}
@Override
public String toString() {
return "[id:" + id + "\tname:" + name + "\tage" + age + "]";
}
}
PersonServer 类:要继承Remote抽象类
public interface PersonServer extends Remote {
//创建接口,必须要抛出RemoteException异常,否则会报错
public List<Person> getList() throws RemoteException;
}
PersonServerImpl 类:实现PersonServer,需要继承UnicastRemoteObject 抽象类
public class PersonServerImpl extends UnicastRemoteObject implements
PersonServer {
private static final long serialVersionUID = 8119235256125775333L;
//必须实现该方法,抛出RemoteException异常
protected PersonServerImpl() throws RemoteException {
super();
}
public List<Person> getList() {
List<Person> list = new ArrayList<Person>(3);
//三个实例
Person person1 = new Person("001", "编号1", "编号1");
Person person2 = new Person("002", "编号2", "编号2");
Person person3 = new Person("003", "编号3", "编号3");
list.add(person1);
list.add(person2);
list.add(person3);
return list;
}
}
ServiceMain 类:启动服务(生产者)
public class ServiceMain {
public static void main(String[] args) {
//实例化PersonServer
PersonServer server = new PersonServerImpl();
//注册端口
LocateRegistry.createRegistry(6600);
//rebind()方法负责把指定名称重新绑定到一个新的远程对象。如果那个名称已经绑定过了,先前的绑定会被替换掉
Naming.rebind("rmi://127.0.0.1:6600/PersonServer", server);
System.out.println("Server started");
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
ClientMain 类:启动客户端(消费者)
public class ClientMain {
public static void main(String[] args) {
try {
//接收远程对象
PersonServer server = (PersonServer) Naming
.lookup("rmi://127.0.0.1:6600/PersonServer");
List<Person> list = server.getList();
for (Person person : list) {
System.out.println(person.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
启动:
1.启动 ServiceMain ,输出:
Server started
2.启动 ClientMain ,输出:
[id:001 name:编号1 age编号1]
[id:002 name:编号2 age编号2]
[id:003 name:编号3 age编号3]