1.分布式应用程序需要?
(1)查找远程对象。
(2)通过远程对象通信
(3)调入被当作参数或返回值的对象的类的字节码
2.java分布式对象模型中的术语?
(1)remote object:指这个对象的方法可以被另一个JRE调用,潜在的含义是JRE不在同一主机上。
(2)remote method invocation(RMI):在remote object中调用remote interface中规定方法的动作。
3.用户定义remote interface的要求?
(1)必须直接或间接继承java.rmi.Remote接口。
例外情况:可以继承non-remote interface。
public interface Alpha{
public void test() throws java.rmi.RemoteException;
}
public interface beta extends Alpha, Remote{
}
(2)声明在remote或者父接口中的方法发须满足以下要求:
<1>声明的方法必须抛出java.rmi.RemoteException,或该异常的父类,例如:java.io.IOException,java.lang.Exception。也可以抛出用户自定义异常,但是该异常必须继承java.rmi.RemoteException。
<2>声明方法中被声明为参数或返回值必须是一个接口类型,而不是接口的实现类。
4.实现remote interface?
(1)该类通常继承java.rmi.server.UnicastRemoteObject,因此也会继承java.rmi.server.RemoteObject,java.rmi.server.RemoteServer。
(2)该类可以实现多个remote interface
(3)该类可以继承另一个remote interface实现类
(4)该类可以定义remote interface中未提供的方法,但是这些方法是只能本地来用而不能用来做远程调用的方法。
5.RMI中传递的参数?
参数或返回值可以是实现serializable的任意对象。例如:简单类型,远程对象,实现java.io.Serializable接口的对象。
(1)传递non-remote对象
调用的jre,是传递过来的对象副本,创建一个新的对象。
(2)传递remote对象
传递的是remote对象的存根
(3)引用完整性
当一个对象的两个应用同时传递到另一个JRE,RMI系统维护引用完整性。
(4)类注释
RMI系统为类描述在调用流中用类的信息(例如:URL)来做注释,以便类可以在接收放被调用。
(5)参数传递
在远程方法调用中的参数是被写入到一个流,这个流是ObjectOutputStream的子类,用来序列化远程方法调用中定义的参数。ObjectOutputStream的子类重载replaceObject()方法,用输出的远程对象的通讯桩实例来替换远程对象。对象参数是被写入到流通过ObjectOutputStream的writeObject()方法。ObjectOutputStream为个对象先调用replaceObject,再经由writeObject方法将其写入到流。
6.ObjectOutputStream的RMI子类中的replaceObject返回?
(1)如果对象是java.rmi.Remote对象实例且是被输出在RMI运行时,返回远程对象的桩。如果对象是java.rmi.Remote对象实例且不是被RMI运行时输出,返回远程本身。远程对象桩,可以通过java.rmi.server.RemoteObject.toStub()方法。
(2)如果不是java.rmi.Remote对象,则被简单的返回。
7.Stubs(桩) and Skeletons(骨架)?
RMI在远程对象间使用标准通信机制(RPC也采用)stubs和skeletons。
(1)远程对象的stub?
可以看作是远程对象的本地代表或代理。调用者调用local stub的方法,stub负责完成远程对象的调用。
(2)stub的方法被调用后?
<1>初始化于包含远程对象的虚拟机的连接
<2>整理(写或传递)远程对象的参数
<3>等待方法返回的结果
<4>整理(读)返回值或返回的表达式
<5>向调用者返回值
(3)skeleton?
负责分发调用到实际的远程对象实现上。在Java2 platform,skeleton可以没有。
(4)skeleton接收到方法调用后?
<1>整理(读)远程方法的参数
<2>调用远程对象实现的方法
<3>整理(写或传递)返回结果给调用者
注:stub和skeleton是由rmic编译生成。
8.java.rmi.Naming?
<1>每个方法都有一个公共的参数,该参数是一个String类型的URL,格式为://host(default host:localhost):port(default port:1099)/name
9.java.rmi.server.UnicastRemoteObject?
(1)如果远程对象是用UnicastRemoteObject.exportObject(Remote)方法输出,被调用的stub class(由rmic工具生成)实例是由以下顺序生成。
<1>确定根类
1)远程对象类直接实现了扩展remote接口的接口
2)如果远程对象的父类,实现了扩展remote接口的接口的类,则是根类;否则,依次向上查找
<2>被载入的stub的名字是,根类加上"_Stub"后缀。
<3>stub class根据名字被载入使用根类的class loader。stub class必须继承RemoteStub,必须有一个公共的构造函数,该函数接收一个RemoteRef类型的参数。
<4>stub实例用RemoteRef来构造。
(2)输出remote object另外的方式?
<1>如果远程对象的stub载入失败或java.rmi.server.ignoreStubClasses是被设置为true。代理是被以下发生构造。
1)代理是被remote object类的class loader定义。
2)代理实现remote object类实际的所有的接口。
3)代理调用处理器是一个RemoteRef构建的RemoteObjectInvocationHandler实例。
4)如果代理不能创建,则抛出StubNotFoundException异常。
(3)UnicastRemoteObject实现了包含以下特征的远程服务对象
<1>远程对象通过TCP transport通讯。
<2>客户端和服务器端之间通过stream protocol通信,调用、方法、返回值。
例子:
远程对象接口文件
RmiHelloRemoteInterface.java
import java.rmi.*;
public interface RmiHelloRemoteInterface extends Remote{
public String helloRemoteObj(String client) throws RemoteException;
}
远程对象文件
RmiHelloRemoteObject.java
import java.rmi.server.*;
import java.rmi.*;
public class RmiHelloRemoteObject extends UnicastRemoteObject implements RmiHelloRemoteInterface{
public RmiHelloRemoteObject() throws RemoteException{
super();
}
public String helloRemoteObj(String client) throws RemoteException{
return "Hello world, " + client;
}
}
服务端程序
import java.io.*;
import java.rmi.*;
import java.rmi.server.*;
import sun.applet.*;
import java.rmi.registry.LocateRegistry;
public class RmiHelloServer{
public RmiHelloServer(){
}
public static void main(String[] args){
//创建并安装安全管理
if(System.getSecurityManager() == null){
System.setSecurityManager(new RMISecurityManager());
}
try{
//创建远程对象
RmiHelloRemoteObject obj = new RmiHelloRemoteObject();
//启动注册表
LocateRegistry.createRegistry(3000);
//将名称绑定到对象
Naming.bind("//localhost:3000/helloObj", obj);
System.out.println("RMI服务器正在运行... ...");
}catch(Exception e){
e.printStackTrace();
}
}
}//客户端程序
import java.rmi.*;
import java.rmi.server.*;
public class RmiHelloClient{
public RmiHelloClient(){
}
public static void main(String[] args){
//创建并安装安全管理器
if(System.getSecurityManager() == null){
System.setSecurityManager(new RMISecurityManager());
}
try{
RmiHelloRemoteInterface client = (RmiHelloRemoteInterface)Naming.lookup("rmi://localhost:3000/helloObj");
System.out.println(client.helloRemoteObj("everyone!"));
}catch(Exception e){
e.printStackTrace();
}
System.exit(0);
}
}
//安全管理文件
//将以下信息创建一个文件,文件名任取,运行服务端和客户时,要用到,用于授权。
//grant codeBase "file:/D:/test_ws/RMI/" {permission java.net.SocketPermission "*:1000-9999", "accept, connect, listen, resolve";};
执行程序
(1)编译以上文件
(2)通过rmic创建stub rmic RmiHelloRemoteObject
(3)运行java -Djava.security.policy=test.policy RmiHelloServer
(4)运行java -Djava.security.policy=test.policy RmiHelloClient