创建Rrmote Method Invoke共需要4个对象。
通俗的说分别为: 1.远程服务器端的真正的对象,2.远程服务器端的帮助对象;3.客户端的访问对象,4.客户端的帮助对象。
其中2个帮助对象 用于封装底层的网络交互,以及保证和处理不同JVM间的使用同一个对象的完整性。
远程服务器端的帮助对象为:Skeleton 客户端的帮助对象:Stub
客户端要访问服务器上对象的方法实际上是先通过 访问 本地的帮助对象(Stub),同理要访问服务器端真正对象的方法必须要先和Skeleton 交互。所以客户端的一个用户调用服务器端的一个方法实际上是通过了2个代理对象才最后真正调用了目的方法。
----------------------
1.创建 server 端的 Interface 并 extends Remote
///
package server;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloInterface extends Remote
{
public String say()throws RemoteException;
}
/
创建接口实现类,继承UnicastRemoteObject 并 实现server 端的Interface
/
package server;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Hello extends UnicastRemoteObject implements HelloInterface
{
private String message;
public Hello(String msg)throws RemoteException
{
message=msg;
}
private static final long serialVersionUID = -4497383228004817199L;
public String say() throws RemoteException
{
return message;
}
}
/
2.注册 该实现类为 服务器端的 Skeleton. 方法为: rmic server.Hello//其中Hello 为 实现类名,实际中需要加上完整的package
3.编写客户端代码:
package client;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import server.HelloInterface;
public class Client
{
public static void main(String[] args)
{
try
{
HelloInterface hello=(HelloInterface)Naming.lookup("//localhost/Hello");
System.out.println(hello.say());
} catch (MalformedURLException e)
{
e.printStackTrace();
} catch (RemoteException e)
{
e.printStackTrace();
} catch (NotBoundException e)
{
e.printStackTrace();
}
}
}
4.编写服务器端的代码:服务器端的代码为绑定 具体的远程对象名
package server;
import java.rmi.Naming;
public class Server
{
public static void main(String[] args)
{
try
{
Naming.rebind("Hello",new Hello("Hello,World!"));
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
5.启动服务.
a。新建cmd窗口到所在的classpath。输入:rmiregistry
注册远程对象。
b。再新建cmd窗口启动server端:java server.Server
///
6.客户端访问.
新建cmd窗口.模拟客户端: java client.Client
完成!
...........................................................................
工场模式的改进:
前面一篇文章中大概介绍了Java RMI框架的基础实现,服务器端Server类一开始就预先向RMI表中注册了一定数量的远程服务,被动地等待客户端Client类通过远程访问获得服务器端对应的远程对象的存根。请详看文章“Java RMI 框架(远程方法调用)”http://haolloyin.blog.51cto.com/1177454/332426。
这种方式有以下缺点:
1、 如果服务器预先创建好远程对象一直都没有被客户访问到,那么就浪费了服务器的一些资源;
2、 而且这种方式使得服务器只能预先提供少量的远程对象,并且服务器很难保证每个远程对象都具有唯一的名字,即服务器可能重复创建同样名称的远程对象。
为了改善这种不足,我们运用工厂方法模式(Factory Method)来实现一下。
具体方案:设计一个专门获取远程对象的工厂类,该工厂类中可以用一个Hashtable对象来
缓存远程对象
。客户端需要远程对象时,根据具体名称来访问服务器端,当该缓存
区存在
以该名称命名的远程对象
时
,则返回一个远程对象的存根。否则,该工厂立即创建并注册一个符合客户请求的远程对象
,这是不是也起到延迟服务器端创建远程对象的作用呢?
当然,我们必须保证该工厂类也是一个远程对象,因为客户端与服务器端的一切交互都从该工厂类开始。
具体类图如下:
先给出具体的交互时序图:
具体代码实现如下:
import java.rmi.*;
import java.rmi.server.*;
import java.util.Hashtable;
import javax.naming.*;
//抽象的服务接口,定义远程服务的标准service()方法
//因为是远程对象类,所以要extends标识的远程接口Remote,
//而且远程方法要声明抛出RemoteException异常
interface IService extends Remote{
public String service(String contents) throws RemoteException;
}
//具体的RMI服务类,实现具体的远程方法服务
class RMIService extends UnicastRemoteObject implements IService{
private String name;
//默认的构造函数也要抛出RemoteException异常
public RMIService(String name) throws RemoteException{
super();
this.name = name;
}
//具体的远程方法
public String service(String contents) throws RemoteException{
return "from " + this.name + " >> " + contents;
}
}
//抽象的工厂类,因为工厂类也需要被远程访问,故扩展Remote接口
interface IFactory extends Remote{
//工厂方法,获得服务
public IService getService(String name) throws RemoteException;
}
//具体的RMI工厂类
class RMIFactory extends UnicastRemoteObject implements IFactory{
//Hashtable对象作为远程对象的缓冲区
private Hashtable<String, IService> remoteObjects;
public RMIFactory() throws RemoteException{
this.remoteObjects = new Hashtable<String, IService>();
}
//关键:可以延迟远程对象的创建
public IService getService(String name) throws RemoteException{
IService object = this.remoteObjects.get(name);
//若为空,则说明不存在客户端需要的远程对象,立即创建以满足客户要求
if(object == null){
System.out.println( "客户要访问的远程对象" + name + "不存在!立即创建!");
object = new RMIService(name);
this.remoteObjects.put(name, object);
} else {
System.out.println( "客户要访问的远程对象" + name + "已存在!");
}
return object;
}
}
import java.rmi.server.*;
import java.util.Hashtable;
import javax.naming.*;
//抽象的服务接口,定义远程服务的标准service()方法
//因为是远程对象类,所以要extends标识的远程接口Remote,
//而且远程方法要声明抛出RemoteException异常
interface IService extends Remote{
public String service(String contents) throws RemoteException;
}
//具体的RMI服务类,实现具体的远程方法服务
class RMIService extends UnicastRemoteObject implements IService{
private String name;
//默认的构造函数也要抛出RemoteException异常
public RMIService(String name) throws RemoteException{
super();
this.name = name;
}
//具体的远程方法
public String service(String contents) throws RemoteException{
return "from " + this.name + " >> " + contents;
}
}
//抽象的工厂类,因为工厂类也需要被远程访问,故扩展Remote接口
interface IFactory extends Remote{
//工厂方法,获得服务
public IService getService(String name) throws RemoteException;
}
//具体的RMI工厂类
class RMIFactory extends UnicastRemoteObject implements IFactory{
//Hashtable对象作为远程对象的缓冲区
private Hashtable<String, IService> remoteObjects;
public RMIFactory() throws RemoteException{
this.remoteObjects = new Hashtable<String, IService>();
}
//关键:可以延迟远程对象的创建
public IService getService(String name) throws RemoteException{
IService object = this.remoteObjects.get(name);
//若为空,则说明不存在客户端需要的远程对象,立即创建以满足客户要求
if(object == null){
System.out.println( "客户要访问的远程对象" + name + "不存在!立即创建!");
object = new RMIService(name);
this.remoteObjects.put(name, object);
} else {
System.out.println( "客户要访问的远程对象" + name + "已存在!");
}
return object;
}
}
Server类:
import java.rmi.*;
import javax.naming.*;
//测试类
public class Client {
public static void main(String[] args) {
String url = "rmi://localhost/";
// Server.registerFactory();
try {
Context namingContext = new InitialContext();
//远程访问,获得工厂的存根
IFactory factory = (IFactory)namingContext.lookup(url + "factory");
IService service01 = factory.getService("service01");
System.out.println(service01.service("访问服务器 ..."));
IService service02 = factory.getService("service02");
System.out.println(service02.service("访问服务器 ..."));
} catch (Exception e) {
e.printStackTrace();
}
}
}
import javax.naming.*;
//测试类
public class Client {
public static void main(String[] args) {
String url = "rmi://localhost/";
// Server.registerFactory();
try {
Context namingContext = new InitialContext();
//远程访问,获得工厂的存根
IFactory factory = (IFactory)namingContext.lookup(url + "factory");
IService service01 = factory.getService("service01");
System.out.println(service01.service("访问服务器 ..."));
IService service02 = factory.getService("service02");
System.out.println(service02.service("访问服务器 ..."));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Client类:
import java.rmi.*;
import javax.naming.*;
//服务器端,一定需要预先向服务器注册一个工厂对象,以备客户的远程访问
public class Server{
public static void main(String [] args) {
registerFactory();
}
//这里使用了静态方法来注册工厂对象,
public static void registerFactory(){
String url = "rmi://localhost/";
try {
IFactory factory = new RMIFactory();
Context namingContext = new InitialContext();
namingContext.rebind(url + "factory", factory);
System.out.println("服务器注册了一个工厂!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
import javax.naming.*;
//服务器端,一定需要预先向服务器注册一个工厂对象,以备客户的远程访问
public class Server{
public static void main(String [] args) {
registerFactory();
}
//这里使用了静态方法来注册工厂对象,
public static void registerFactory(){
String url = "rmi://localhost/";
try {
IFactory factory = new RMIFactory();
Context namingContext = new InitialContext();
namingContext.rebind(url + "factory", factory);
System.out.println("服务器注册了一个工厂!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试,先在命令行下输入“
start rmiregistry
”;再“
start java Server
”,最后才是“
java Client
”。过程如下:
还可以根据需要自己多测试几次,就可以明白工厂方法模式对文章前面所指出的不足之处的改进了,而且也运用到了一个模式,何乐而不为?