浅谈RMI
目录
1. RMI简介
RMI
用于不同虚拟机之间的通信,这些虚拟机可以在不同的主机上、也可以在同一个主机上,这里的通信可以理解为一个虚拟机上的对象调用另一个虚拟机上对象的方法。RMI要解决的是:是让客户端对远程方法的调用可以相当于对本地方法的调用而屏蔽其中关于远程通信的内容,即使在远程上,也和在本地上是一样的。
1.1 实现原理
客户端只与代表远程主机中对象的Stub
对象进行通信,丝毫不知道Server
的存在。客户端只是调用Stub
对象中的本地方法,Stub
对象是一个本地对象,它实现了远程对象向外暴露的接口。客户端认为它是调用远程对象的方法,实际上是调用Stub
对象中的方法。可以理解为Stub
对象是远程对象在本地的一个代理,当客户端调用方法的时候,Stub
对象会将调用通过网络传递给远程对象。
2. 实现流程
以一个简单案例来进一步理解RMI的使用。
1. 声明远程接口
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* 远程服务对象接口必须继承Remote接口;同时方法必须抛出RemoteExceptino异常
*/
public interface Hello extends Remote {
public String sayHello(User user) throws RemoteException;
}
其中有一个引用对象作为参数:
import java.io.Serializable;
/**
* 引用对象应该是可序列化对象,这样才能在远程调用的时候:1. 序列化对象 2. 拷贝 3. 在网络中传输
* 4. 服务端反序列化 5. 获取参数进行方法调用; 这种方式其实是将远程对象引用传递的方式转化为值传递的方式
*/
public class User implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2258201344880196063L;
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. 实现远程服务对象
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
/**
* 远程服务对象实现类写在服务端;必须继承UnicastRemoteObject或其子类
**/
public class HelloImpl extends UnicastRemoteObject implements Hello {
/**
*
*/
private static final long serialVersionUID = 3638546195897885959L;
protected HelloImpl() throws RemoteException {
super();
// TODO Auto-generated constructor stub
}
@Override
public String sayHello(User user) throws RemoteException {
System.out.println("this is server, hello:" + user.getName());
return "hello";
}
}
3. 服务端程序
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
/**
* 服务端程序
**/
public class Server {
public static void main(String[] args) {
try {
Hello hello = new HelloImpl(); // 创建一个远程对象,同时也会创建stub对象、skeleton对象
LocateRegistry.createRegistry(8080); //启动注册服务
try {
Naming.bind("//127.0.0.1:8080/wbh", hello); //将stub引用绑定到服务地址上
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("service bind already!!");
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4. 客户端程序
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
/**
* 客户端程序
* @author wbh
*
*/
public class Client {
public static void main(String[] args) {
try {
Hello hello = (Hello) Naming.lookup("//127.0.0.1:8080/wbh");//获取远程对象
User user = new User();
user.setName("james");
System.out.println(hello.sayHello(user));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NotBoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
5. 启动服务端程序
6. 客户端调用
服务端:
客户端
3. 程序结构
实际应用中客户端程序与服务端程序是分开的,那么客户端要使用远程对象,就必须声明相同的远程接口与一些可序列化的参数对象。但是我们又不想重复写,怎么办?可以通过引用jar包的方式。