Java RMI 框架_远程方法调用(2016-08-16)

时间:2021-11-07 09:02:02
概念:
  Java RMI 指的是远程方法调用 (Remote Method Invocation)。它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法。可以用此方法调用的任何对象必须实现该远程接口。
  Java RMI不是什么新技术(在Java1.1的时代都有了),但却是是非常重要的底层技术。
  大名鼎鼎的EJB都是建立在rmi基础之上的,现在还有一些开源的远程调用组件,其底层技术也是rmi。
  在大力鼓吹Web Service、SOA的时代,是不是每个应用都应该选用笨拙的Web Service组件来实现,通过对比测试后,RMI是最简单的,在一些小的应用中是最合适的。
Java RMI 框架_远程方法调用(2016-08-16)Java RMI 框架_远程方法调用(2016-08-16)

RMI远程调用步骤:

  1,客户对象调用客户端辅助对象上的方法

  2,客户端辅助对象打包调用信息(变量,方法名),通过网络发送给服务端辅助对象

  3,服务端辅助对象将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象

  4,调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象

  5,服务端辅助对象将结果打包,发送给客户端辅助对象

  6,客户端辅助对象将返回值解包,返回给客户对象

  7,客户对象获得返回值

  对于客户对象来说,步骤2-6是完全透明的

服务端类图:
 
Java RMI 框架_远程方法调用(2016-08-16)
  IService接口用于声明服务器端必须提供的服务(即service()方法)
  ServiceImpl类是具体的服务实现类
  Server类是最终负责注册服务器远程对象,以便在服务器端存在骨架代理对象来对客户端的请求提供处理和响应。
 
各个类的源代码如下:

IService接口:

import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IService extends Remote {
//声明服务器端必须提供的服务
String service(String content) throws RemoteException;
}

  创建远程方法接口,该接口必须继承自Remote接口

  Remote 接口是一个标识接口,用于标识所包含的方法可以从非本地虚拟机上调用的接口,Remote接口本身不包含任何方法

  由于远程方法调用的本质依然是网络通信,只不过隐藏了底层实现,网络通信是经常会出现异常的,所以接口的所有方法都必须抛出RemoteException以说明该方法是有风险的。

ServiceImpl实现类:

import java.rmi.RemoteException;
//UnicastRemoteObject用于导出的远程对象和获得与该远程对象通信的存根。
import java.rmi.server.UnicastRemoteObject; public class ServiceImpl extends UnicastRemoteObject implements IService { private String name; /**
* 必须定义构造方法,即使是默认构造方法,也必须把它明确地写出来,因为它必须抛出出RemoteException异常
*/
public ServiceImpl(String name) throws RemoteException {
this.name = name;
} @Override
public String service(String content) {
return "server >> " + content;
}
}

  创建远程方法接口实现类:

  UnicastRemoteObject类的构造函数抛出了RemoteException,故其继承类不能使用默认构造函数,继承类的构造函数必须也抛出RemoteException

  由于方法参数与返回值最终都将在网络上传输,故必须是可序列化的

 Server类:

/**
* Context接口表示一个命名上下文,它由一组名称到对象的绑定组成。
* 它包含检查和更新这些绑定的一些方法。
*/
import javax.naming.Context;
/*
* InitialContext类是执行命名操作的初始上下文。
* 该初始上下文实现 Context 接口并提供解析名称的起始点。
*/
import javax.naming.InitialContext; public class Server {
public static void main(String[] args) {
try {
// 实例化实现了IService接口的远程服务ServiceImpl对象
IService service02 = new ServiceImpl("service02");
// 初始化命名空间
Context namingContext = new InitialContext();
/**
* Naming 类提供在对象注册表中存储和获得远程对远程对象引用的方法 Naming 类的每个方法都可将某个名称作为其一个参数,
* 该名称是使用以下形式的 URL 格式(没有 scheme 组件)的 java.lang.String:
* //host:port/name host:注册表所在的主机(远程或本地),省略则默认为本地主机
* port:是注册表接受调用的端口号,省略则默认为1099,RMI注册表registry使用的著名端口
* name:是未经注册表解释的简单字符串
*/
// 将名称绑定到对象,即向命名空间注册已经实例化的远程服务对象
namingContext.rebind("rmi://localhost/service02", service02);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("服务器向命名表注册了1个远程服务对象!");
}
}

 Client类:

import javax.naming.Context;
import javax.naming.InitialContext; public class Client {
public static void main(String[] args) {
String url = "rmi://localhost/";
try {
Context namingContext = new InitialContext();
// 检索指定的对象。 即找到服务器端相对应的服务对象存根
IService service02 = (IService) namingContext.lookup(url
+ "service02");
Class stubClass = service02.getClass();
System.out.println(service02 + " 是 " + stubClass.getName()
+ " 的实例!");
// 获得本底存根已实现的接口类型
Class[] interfaces = stubClass.getInterfaces();
for (Class c : interfaces) {
System.out.println("存根类实现了 " + c.getName() + " 接口!");
}
System.out.println(service02.service("你好!"));
} catch (Exception e) {
e.printStackTrace();
}
}
}

执行步骤:

  将以上代码保存于某一目录下,
  先运行“start rmiregistry”来启动JDK自带的注册表程序,它用于保存Server类注册的远程对象并允许远程客户端的请求访问;
  然后运行服务器端的Server类,即“start java Server”,该程序向注册表中注册具体的远程对象;
  最后才是运行客户端程序来查找并获得服务器端的远程对象存根,此时才能使用存根对象与服务器进行通信,命令是“java Client”。
  注意:上面命令中的start的功能是重新打开一个DOS窗口。
 
运行结果如下:
Java RMI 框架_远程方法调用(2016-08-16)
Java RMI 框架_远程方法调用(2016-08-16)
Java RMI 框架_远程方法调用(2016-08-16) 
 
其实整个简单的RMI 应用中各个类的交互时序如下图:
Java RMI 框架_远程方法调用(2016-08-16)

参考复制来源:

http://haolloyin.blog.51cto.com/1177454/332426

http://blog.csdn.net/a19881029/article/details/9465663

http://damies.iteye.com/blog/51778

http://www.blogjava.net/zhenyu33154/articles/320245.html

  还没弄太明白,先攒起来以后看。