java RMI简单例子+工场模式改进RMI


创建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; 
  } 
}

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(); 
    } 
  } 
}
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(); 
    } 
  } 
}
测试,先在命令行下输入“ start rmiregistry ”;再“ start java Server ”,最后才是“ java Client ”。过程如下:
    第二次运行 Client类: 
还可以根据需要自己多测试几次,就可以明白工厂方法模式对文章前面所指出的不足之处的改进了,而且也运用到了一个模式,何乐而不为?



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值