RMI和RPC之间最主要的区别在于方法是如何别调用的。在RMI中,远程接口使每个远程方法都具有方法签名。如果一个方法在服务器上执行,但是没有相匹配的签名被添加到这个远程接口上,那么这个新方法就不能被RMI客户方所调用。在RPC中,当一个请求到达RPC服务器时,这个请求就包含了一个参数集和一个文本值,通常形成“classname.methodname”的形式。这就向RPC服务器表明,被请求的方法在为“classname”的类中,名叫“methodname”。然后RPC服务器就去搜索与之相匹配的类和方法,并把它作为那种方法参数类型的输入。这里的参数类型是与RPC请求中的类型是匹配的。一旦匹配成功,这个方法就被调用了,其结果被编码后返回客户方。
附英语描述(Difference between RMI and RPC):
RMI or Remote Method Invokation is very similar to RPC or Remote Proceedure call in that the client both send proxy objects (or stubs) to the server however the subtle difference is that client side RPC invokes FUNCTIONS through the proxy function and RMI invokes METHODS through the proxy function. RMI is considered slightly superior as it is an object-oriented version of RPC.
RMI 即远程方法调用(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分布式Java应用程序的方便途径。
创建RMI
应用程序分四步:
(1)
定义和实现远端接口中的参数
(2)
定义和实现远端接口
(3)
编写服务端代码
(4)
编写客户端代码
(5)
启动rmiregistry ,
并将服务注册到rmiregistry.
自JDK1.5
之后不再需要创建存根与基干,可以看到JAVA每天都在进步。
(1)定义和实现远端接口中的参数,这里定义一个学生实体,该类实现Serializable接口,因为参数可能需要网络传输.
package rmi;
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = 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;
}
}
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = 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)
定义和实现远端接口
远端接口的定义如下,接口须从java.rmi.Remote继承;远端接口中的方法如果要throws异常,这个异常必须是java.rmi.RemoteException(或java.rmi.RemoteException的子类),否则,这个异常就不能被返回到客户端。
package rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface InfoConsult extends Remote{
public int getAge(String name) throws RemoteException;
public Student getStudent(String name) throws RemoteException;
}
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface InfoConsult extends Remote{
public int getAge(String name) throws RemoteException;
public Student getStudent(String name) throws RemoteException;
}
实现
远端接口
package rmi;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.List;
public class InfoConsultImpl extends UnicastRemoteObject implements InfoConsult{
private List<Student> students;
protected InfoConsultImpl(List<Student> students) throws RemoteException {
super();
this.students = students;
}
public int getAge(String name) throws RemoteException{
for(Student stu:students){
if(stu.getName().equals(name)){
return stu.getAge();
}
}
return -1;
}
public Student getStudent(String name) throws RemoteException{
for(Student stu:students){
if(stu.getName().equals(name)){
return stu;
}
}
return new Student( "null",-1);
}
}
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.List;
public class InfoConsultImpl extends UnicastRemoteObject implements InfoConsult{
private List<Student> students;
protected InfoConsultImpl(List<Student> students) throws RemoteException {
super();
this.students = students;
}
public int getAge(String name) throws RemoteException{
for(Student stu:students){
if(stu.getName().equals(name)){
return stu.getAge();
}
}
return -1;
}
public Student getStudent(String name) throws RemoteException{
for(Student stu:students){
if(stu.getName().equals(name)){
return stu;
}
}
return new Student( "null",-1);
}
}
(3)
服务端代码,服务器端主要负责实现了一个远程的对象以提供服务。绑定名称给提供给远程的对象,并让运行中的系统可能开始了一个新的服务套接字或是共享一个服务套接字,监控远程方法调用的呼叫.
package rmi;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class InfoServer {
public static void main(String args[]) throws RemoteException,
MalformedURLException, NamingException {
//使用程序创建RIM注册服务
//LocateRegistry.createRegistry(8888);
InfoServer server = new InfoServer();
InfoConsult consult = new InfoConsultImpl(server.getMockData());
//consult= (InfoConsult)UnicastRemoteObject.exportObject(consult, 0);
Naming.rebind( "rmi://localhost:8888/InfoConsult", consult);
}
public List<Student> getMockData() {
List<Student> lst = new ArrayList<Student>();
lst.add(new Student("guo", 26));
lst.add(new Student("zhang", 24));
lst.add(new Student("baby", 1));
return lst;
}
}
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class InfoServer {
public static void main(String args[]) throws RemoteException,
MalformedURLException, NamingException {
//使用程序创建RIM注册服务
//LocateRegistry.createRegistry(8888);
InfoServer server = new InfoServer();
InfoConsult consult = new InfoConsultImpl(server.getMockData());
//consult= (InfoConsult)UnicastRemoteObject.exportObject(consult, 0);
Naming.rebind( "rmi://localhost:8888/InfoConsult", consult);
}
public List<Student> getMockData() {
List<Student> lst = new ArrayList<Student>();
lst.add(new Student("guo", 26));
lst.add(new Student("zhang", 24));
lst.add(new Student("baby", 1));
return lst;
}
}
(4)客户端代码
package rmi;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class School {
public static void main(String args[]) {
try {
InfoConsult consult = (InfoConsult) Naming
.lookup( "rmi://localhost:8888/InfoConsult");
System.out.println(consult.getStudent("baby").getAge());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}
}
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class School {
public static void main(String args[]) {
try {
InfoConsult consult = (InfoConsult) Naming
.lookup( "rmi://localhost:8888/InfoConsult");
System.out.println(consult.getStudent("baby").getAge());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}
}
lookup获得远程对像,其实是代理. 调用远程方法.
(5)要启动服务端,务必先启RMI运行时. 用法很简单 RmiRegistry 8888.如果省去8888,默认1089.Java RMI 注册表只是一个允许客户端获得远程对象桩引用的简单名字服务.
有时执行这一步之后,启动服务端报以下错误:
1.
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
2
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
3...
..
没有找到存根类.InfoConsultImpl与其存根在同一目录下,为什么没有加载成功呢. 这是
因为存根类是由RmiRegistry 加载.而启动RmiRegistry 与启动服务端的classpath并不相同.所以就出现该异常.解决方法是先设定classpath环境变量这服务端工作目录,再启动服务端即可。
补充: 这一步也可以在程序里完成, JAVA提供了LocateRegistry.createRegistry(8888)方法创建服务.
(6)最后一步 启动服务端,启动客户端.显示结果1.