第一次使用RMI实现java分布式,利用一个简单的例子进行测试。
首先需要一个实现了Remote的接口,这个接口提供远程对象的方法集
这个接口如下:
package com.hello;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Date;
public interface HelloServer extends Remote
{
public String echo(String msg)throws RemoteException;
public Date getTime()throws RemoteException;
}
在客户端和服务器上都应该有这样一个接口,只是可以使用的服务。
然后再服务端需要一个实现了HelloServer的类,并使其成为能够提供远程服务的远程对象,这里通过继承UnicastRemoteObject使其导出远程对象:
package com.hello;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Date;
public class HelloServerImpl extends UnicastRemoteObject implements HelloServer
{
private String name;
protected HelloServerImpl(String name) throws RemoteException
{
this.name=name;
}
public String echo(String msg) throws RemoteException
{
System.out.println("调用了echo()方法");
return "echo "+msg+" from "+name;
}
public Date getTime() throws RemoteException
{
System.out.println("调用了getTime()方法");
return new Date();
}
}
接下来编写服务端程序
package com.hello;
import javax.naming.Context;
import javax.naming.InitialContext;
public class SimpleServer
{
public static void main(String[]args)
{
try
{
HelloServer service1=new HelloServerImpl("service1");
HelloServer service2=new HelloServerImpl("service2");
Context namingContext=new InitialContext();
namingContext.rebind("rmi://localhost:1099/HelloServer1",service1);
namingContext.rebind("rmi://localhost:1099/HelloServer2",service2);
System.out.println("注册了两个对象");
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
接下来编写客户端程序
package com.hello;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
public class SimpleClient
{
public static void main(String[]args)
{
String url="rmi://localhost:1099/";
try
{
Context namingContext=new InitialContext();
//获得远程对象的存根对象
HelloServer service1=(HelloServer)namingContext.lookup(url+"HelloServer1");
HelloServer service2=(HelloServer)namingContext.lookup(url+"HelloServer2");
Class stubClass=service1.getClass();
//测试存根类所属的类
System.out.println("service1是 "+stubClass.getName());
//测试存根类实现的接口
Class[] interfaces=stubClass.getInterfaces();
for(int i=0;i<interfaces.length;i++)
{
System.out.println("存根类实现了"+interfaces[i].getName());
}
System.out.println(service1.echo("hello"));
System.out.println(service1.getTime());
System.out.println(service2.echo("hello"));
System.out.println(service2.getTime());
NamingEnumeration<NameClassPair> e=namingContext.list("rmi:");
while(e.hasMore())
{
System.out.println(e.next().getName());
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
在运行程序之前需要进入到HelloServer的bin目录下开启rmiregistry
命令:start rmiregistry
如果没有错误,就会弹出新的黑框;
接下来客户端还需要一个存根类,为了获得它需要在服务端的bin目录下用如下命令:
rmic com.hello.HelloServer
这个命令注意:HelloServer是完整的类名,并且没有.class后缀
如果成功的话会产生一个HelloServerImpl_Stub.class,将其与客户端class文件放在一起。
下来就可以运行程序了。
首先运行服务端,运行结果如下:
然后运行客户端,运行结果如下:
而此时的服务端状态如下:
说明客户端对远程对象的方法调用其执行过程确实是在服务端进行的。
注意:在jdk1.3以后就不用再手动生成存根类了,RMI会自己完成。
在真正的RMI实现里需要利用java.rmi.server.codebase进行动态加载存根类,为了实现它必须完成:
1.使用安全管理器
System.setProperty("java.security.policy",SimpleClient.class.getResource("client.policy").toString());
System.setSecurityManager(new RMISecurityManager());
2.建立一个供客户端下载的目录,目录的结构和包目录一致,假如你的HelloServer.class位于包com.hello下,则该目录结构也必须具有com和hello子目录。