|
RMI 使用笔记
Java 远程方法调用,即 Java RMI( Java Remote Method Invocation ) 。顾名思义,可以使客户机上运行的程序能够调用远程服务器上的对象(方法)。
下面主要介绍一下使用步骤:
1.定义远程接口(服务端)
远程接口定义出可以让客户远程调用的方法。
此接口必须实现 java.rmi.Remote 接口,来表示其支持远程调用;同时其中声明的所有方法,需要抛出RemoteException异常,因为远程调用的不稳定性(如网络原因等),这样可以让客户端在调用失败时进行相应的处理。
public interface DemoService extends Remote {
String sayHello() throws RemoteException;
} |
2.实现远程接口(服务端)
远程接口的实现类如果想要被远程访问,可以有如下实现方式:
继承java.rmi.server.UnicastRemoteObject类
public class DemoServerImpl extends UnicastRemoteObject implements DemoService{
​
public DemoServerImpl() throws RemoteException {
// 因为 UnicastRemoteObject 构造器抛出 RemoteException
// 所以此处只能声明一个构造器并抛出对应异常
}
​
@Override
public String sayHello() throws RemoteException {
return "Hello World";
}
} |
如果不想继承UnicastRemoteObject类,则需要使用 UnicastRemoteObject类的静态方法exportObject(Remote obj, int port)将对象导出
其中如果端口设为 0 的话,则表示任何合适的端口都可用来监听客户连接
public class DemoServerImpl implements DemoService{
​
public DemoServerImpl() throws RemoteException {
UnicastRemoteObject.exportObject(this, 0);
}
​
@Override
public String sayHello() throws RemoteException {
return "Hello World";
}
} |
这两者方法本质上是一样的,在UnicaseRemoteObject类的构造方法中,其实也是调用了exportObject方法
// UnicaseRemoteObject中的部分源码
protected UnicastRemoteObject() throws RemoteException
{
this(0);
}
// if port is zero, an anonymous port is chosen
protected UnicastRemoteObject(int port) throws RemoteException
{
this.port = port;
exportObject((Remote) this, port);
} |
3.启动 RMI 注册表
注册表就像一个电话簿,启动后即可将提供的服务注册到其中,客户可以通过它查询到服务来进行调用
启动注册表有两种方法,一种是通过命令行rmiregistry来启动,另一种方式是通过LocateRegistry.createRegistry(int port)方法。
4.注册开启远程服务
注册服务共有三种方式:
LocateRegistry 类的对象的 rebind() 和 lookup() 来实现绑定注册和查找远程对象的
利用命名服务 java.rmi.Naming 类的 rebind() 和 lookup() 来实现绑定注册和查找远程对象的
利用JNDI(Java Naming and Directory Interface,Java命名和目录接口) java.naming.InitialContext 类来 rebind() 和 lookup() 来实现绑定注册和查找远程对象的
其中第二种方式实际是对第一种方式的简单封装,在内部仍是调用Registry类的bind方法
// Naming 类的部分源码 (为了节省篇幅,去除了抛出异常部分)
public static void bind(String name, Remote obj) throws ...
{
ParsedNamingURL parsed = parseURL(name);
Registry registry = getRegistry(parsed);
​
if (obj == null)
throw new NullPointerException("cannot bind to null");
​
registry.bind(parsed.name, obj);
} |
服务测试类:
public class ServerTest {
public static void main(String[] args) throws Exception{
String name = "rmi.service.DemoService";
// 创建服务
DemoService service = new DemoServerImpl();
// 创建本机 1099 端口上的 RMI 注册表
Registry registry1 = LocateRegistry.createRegistry(1099);
/***************** 以下为注册方法一 ************/
// 将服务绑定到注册表中
registry1.bind(name, service);
/***************** 以下为注册方法二 ************/
// Naming.bind(name, service);
/***************** 以下为注册方法三 ************/
//Context namingContext = new InitialContext();
//namingContext.bind("rmi:" + name, service); // 此方式 name 需要以 rmi: 开头
}
} |
客户端测试类:
public class ClientTest {
public static void main(String[] args) throws Exception {
String name = "rmi.service.DemoService";
/***************** 以下为查找服务方法一 ************/
// 获取注册表
Registry registry = LocateRegistry.getRegistry("localhost", 1099);
// 查找对应的服务
DemoService service = (DemoService) registry.lookup(name);
/***************** 以下为查找服务方法二 ************/
// DemoService service = (DemoService) Naming.lookup(name);
/***************** 以下为查找服务方法三 ************/
//Context namingContext = new InitialContext();
//DemoService service = (DemoService) namingContext.lookup("rmi:" + name);
// 调用服务
System.out.println(service.sayHello());
}
} |
程序猿的技术大观园:www.javathinker.net
|
|