1.原理
- RMI:远程方法调用(Remote Method Invocation)。能够让在某个java虚拟机上的对象像调用本地对象一样调用另一个java 虚拟机中的对象上的方法。
2.搭建一个RMI服务的过程分为以下7步
2.1 创建远程方法接口,该接口必须继承自Remote接口
//Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口。任何远程对象都必须直接或间接实现此接口
public interface UserHandler extends Remote {
public User getUser() throws RemoteException;
int getUserCount() throws RemoteException;
}
由于远程方法调用的本质依然是网络通信,只不过隐藏了底层实现,网络通信是经常会出现异常的,所以接口的所有方法都必须抛出RemoteException以说明该方法是有风险的
2.2创建远程方法接口实现类
public class UserHandlerImpl extends UnicastRemoteObject implements UserHandler {
private static final long serialVersionUID = -271947229644133464L;
protected UserHandlerImpl() throws RemoteException {
super();
// TODO Auto-generated constructor stub
}
@Override
public User getUser() throws RemoteException {
User user =new User();
user.setUser("123", 12);
return user;
}
@Override
public int getUserCount() throws RemoteException{
return 1;
}
}
UnicastRemoteObject类的构造函数抛出了RemoteException,故其继承类不能使用默认构造函数,继承类的构造函数必须也抛出RemoteException
由于方法参数与返回值最终都将在网络上传输,故必须是可序列化的
2.3 服务端实体类必须可序列化
public class User implements Serializable{
private String name;
private int age;
public void setUser(String name,int age){
this.name=name;
this.age=age;
}
public String getUser(){
return "name:"+name+", age"+String.valueOf(age);
}
}
2.4 构建服务端代码
public class ServerTest {
public static void main(String[] args){
UserHandler userHandler = null;
Registry registry = null;
try {
//1.创建远程对象远程接口
registry = LocateRegistry.createRegistry(1099);
userHandler = new UserHandlerImpl();
registry.rebind("user", userHandler);
System.out.println(" rmi server is ready ...");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
- Registry 是简单远程对象注册表的一个远程接口,它提供存储和获取绑定了任意字符串名称的远程对象引用的方法。bind、unbind 和 rebind 方法用于改变注册表中的名称绑定,lookup 和 list 方法用于查询当前的名称绑定。
2.5 创建客户端接口
public interface UserHandler extends Remote {
public User getUser() throws RemoteException;
int getUserCount() throws RemoteException;
}
2.6 创建客户端实体类
public class User implements Serializable{
private String name;
private int age;
public void setUser(String name,int age){
this.name=name;
this.age=age;
}
public String getUser(){
return "name:"+name+", age"+String.valueOf(age);
}
}
2.7 创建客户端代码
public class ClientTest{
public static void main(String[] args){
try {
UserHandler handler = (UserHandler)Naming.lookup("rmi://192.168.1.61:1099/user");
System.out.println(handler.getUserCount());
System.out.println(handler.getUser().getUser());
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
- Naming 类提供在对象注册表中存储和获得远程对远程对象引用的方法。Naming 类的每个方法都可将某个名称作为其一个参数,该名称是使用以下形式的 URL 格式(没有 scheme 组件)的 java.lang.String
- registry.bind和Naming.bind的区别:Naming是对registry重新构造使用更简单
源码:
public static void bind(String name, Remote obj)
throws AlreadyBoundException,
java.net.MalformedURLException,
RemoteException
{
ParsedNamingURL parsed = parseURL(name);
Registry registry = getRegistry(parsed);
if (obj == null)
throw new NullPointerException("cannot bind to null");
registry.bind(parsed.name, obj);
}
private static Registry getRegistry(ParsedNamingURL parsed)
throws RemoteException
{
return LocateRegistry.getRegistry(parsed.host, parsed.port);
}
例子:
LocateRegistry.getRegistry("127.0.0.1", 8494).bind("R1",
UnicastRemoteObject.exportObject(new RemoteObject(), 0));
Naming.bind("rmi://127.0.0.1:8494/R1",
UnicastRemoteObject.exportObject(new RemoteObject (), 0));
3.java.rmi.Naming和java.rmi.registry.LocateRegistry的区别
- Naming类只是在“远程对象注册表”上进行存储和读取操作,该类并不能创建“远程对象注册表”;
- LocateRegistry类可以获取“远程对象注册表”引用,或者创建本地主机上的“远程对象注册表”;
- Naming类方法封装了Registry接口方法,只需要一个URL就能对“远程对象注册表”进行相关操作,比如:
IHello hello = new HelloImpl();
java.rmi.Naming.rebind("rmi://localhost:1099/hello", hello); /* URL和对象绑定 */
IHello hello = (IHello) Naming.lookup("rmi://localhost:1099/hello"); /* 通过URL获取对象引用 */
- LocateRegistry类获取到Registry对象引用后,通过Registry类方法对“远程对象注册表”进行相关操作,比如:
<span style="font-weight: normal;"><span style="font-size:14px;">String hostName="192.168.1.22";//RMIService的地址
int port=33333;//RMIService监听的端口
Registry registry=LocateRegistry.getRegistry(hostName,port);
Hello hello=(Hello)registry.lookup("HelloServer");</span></span>
<span style="font-weight: normal;"><span style="font-size:14px;">int port = 33333;//RMIService监听的端口,自己指定!
registry = LocateRegistry.createRegistry(port);//在RMIServer上创建
registry.rebind("HelloServer", helloServer); //HelloServer就是对外暴露出的名称</span></span>