java remote

java 远程对象
*
典型CS场景中,服务端会被客户端通过网络来访问,他们处于不同的jvm进程,机器中,如何让不同进程中的java对象互相访问呢,一种技术就是使用java远程对象技术。远程技术的核心设计模式就是代理设计模式,这个模式可让一个对象被“封装”为一个具备被远程调用能力的对象,代理模式是所有远程技术的核心模式:还包括CORBA。我们知道网络通信最本质的就是使用一种协议,如果你有Socket编程的经历,你就会知道协议实现者必须对协议的单元头部进行不同的响应,这典型地会用到switch语句或if语句——即根据不同的协议头部采取不同的行为(就是一个状态机的实现而已)。这个底层的协议实现在抽象级更高一点就可以是:发送一个字符串就可引起一个远程方法的调用,RMI更抽象,它是一个基础通信系统,他替我们“拉近”两个不同进程中的Java对象的“距离”,让我们能将远程对象如同本地对象那样使用,所有的底层后台问题都被封装了。但请千万记住一点远程的毕竟是远程的它有其缺点(如:方法响应肯定不如本地同级别对象快,并且它也有一些最佳实践,请参考《企业Java最佳实践》)

远程技术涉及:远程接口,远程对象 ,名字服务,rmic,远程安全管理器,策略文件(Policy)

Java远程接口指,实现了远程接口java.rmi.Remote并且所有需要被远程调用的方法都需要抛出RemoteException异常。远程是相对的,对于不在同一机器上(也可能是虚拟机上)的其他环境中的Java对象而言,当然其相对词应是本地(local)。

远程对象是指扩展 UnicastRemoteObject或者是实现Activatable接口

服务端需要获取远程对象的引用,并按照一个安全管理器,客户端也需要获取一个远程对象的引用,并安装安全管理器。

不同环境中的(是泛指)java对象需要通讯

请不要被远程对象技术所吓倒,他的原理其实也好理解,请让我为朋友们描述一个场景(如不妥,请见谅,毕竟是学习者):

每一个Java虚拟机中的所有对象,我们可认为是一个集合,这些对象都是本地的,这里我再引入一个集合对象如 HashTable romoteObjects,所有想被远程调用的对象其引用可以添加到这个集合中,远程调用方在调用对象时,先从此集合中找到本地,然后再调用相应的方法(通用框架一般会使用反射来调用方法,比如当你具有对象引用,方法名称,方法参数,你就可以通过反射来调用该对象上的方法,所有的返回值异常都是最泛化的,如Object类型的返回值,Throwable类型的异常根),最后将返回值或异常返回给调用方,他们都应该是可序列化或外部化的,通过网络传给调用者。这是在服务方应该做的;来看看客户端如何调用一个远程对象:
首先它应该同处理本地对象一样,拥有远程对象的一个引用,但是这个引用只是远程对象的一个代理而已(就像电视机的遥控器一样,通过遥控器来控制远端的电视机,遥控器上的按钮可能同电视机上的某些按钮功能一致,就像一个远程接口映射,我们可以通过这些或远或近的接口来控制电视机),当你调用代理的某个方法时,代理会联系服务端找到这个代理的“本体”对象,并调用远端对象的相应方法,因为代理的接口方法同远端对象一致,所以方法名,方法参数可以获取,这样就满足使用反射调用一个对象的条件,将这些条件(都是可序列化或外部化的对象)通过网络传递到服务端,这时如果服务端再有一个专门从事远程调用处理的组件存在,那么它应该先初始化一个本地对象(注意这里的类型也可知,可通过网络将类型用字符串的形式传递过来,这里可能有一个池,或者是一个哈希表——可能从池或表中取出或查询一个对象,所以远程实现可能有不同的策略:每调用实例化一个对象,或根据引用Id来共用一个对象,或全局共享一个对象),然后利用反射来调用相应的方法,并将结果封装返回给调用方,调用方在接收到泛化的返回值后将其转型为适当类型,之后再将结果流入到客户端的对象集中以供使用。所有这些处理细节都被封装好了,我们只需要把一个远程对象当成本地对象那样使用就行了。
*
**
上面所说的东西只不过是为了便于理解远程对象技术,以下给出“实际”结构(只讨论最典型的结构):
在典型的C/S结构上再引进一些内容,C端JVM中的对象想访问远端SJVM中的对象——并且想以访问本地对象那样来访问远程对象,它首先需要获取远程对象的引用,引用获取到以后就可以象本地对象那样使用了。
但是如何获取远程对象的引用呢?答案是通过注册表,大家都知道Windows有个注册表概念,但这个概念只是限于一台机器,如果把思路放大了,可不可以全世界的机器共用一个注册表呢?可以,DNS其实就是这样做的,UDDI也是这个思路,这个注册表就是RMI注册表它运行在一个服务进程上,JDK的bin子目录下的rmiregistry.exe程序就是该服务程序。所有想被远程访问的对象都注册到这里,以供客户端引用。自JDK1.3后RMI命名服务被整合到JNDI。JNDI中的Context接口声明了注册,查找,以及注销对象的方法。该服务默认监听端口1099。
绑定是将一个字符串同一个对象的引用关联起来,就像字典(或Hashtable)那样使用,发展至今绑定的初始用法以有所扩展,可以将一个对对象的功能或性质的描述(可以是一个字符串也可以是一个XML文件等)同对象的位置(IP+port[+进程ID]+内存引用)关联,你可以想象这个绑定是数据库中表的一个列,我们可以使用select语句查询一个满足条件的对象:select ObjectID from GlobalRegisterTable where description like '%查询股票价格%'; 当然这是一个比喻,但注册表本质就是用字符描述来关联对象,不过如果你使用一个表来表示关联的话就可能用到“多维”(即以多个列,而不是两个列),可以用任一维来定位另一维当然返回的对象引用可能有多个,选择则在你。这个思想其实和UDDI也是一致的。关于注册表的注销,查找类似对表的操作。值得一提的是因为真正的注册表并不保证注册键(就是字符串)的唯一性,所以注册表一般只注册少量的远程对象,对于同一JVM环境中的多个远程对象你可以只注册一个“根”对象,然后使用导航操作来导航到其他远程对象,或者人为的创造一个集合来聚集这些远程对象,并将这个集合作为“根”注册。这里只是个人想法,并没有实际操作过!!!!
Context的lookup操作一般返回一个动态代理实例,它就是远程对象的存根对象。
要使用RMI技术,在运行程序前确保rmi注册表运行了 Dos命令下转到jdk或jre的bin子目录下:start rmiregister [可选的端口] 。

rmi框架简要描述:


**
***
小练习:

1.先定义远程接口:
package RemoteTest.RemoteInterfaces;

import java.rmi.Remote; 
import java.rmi.RemoteException;

//先定义一个远程接口,该接口至少有三个作用:
/*
* 1.供本地对象来实现;
* 2.供客户端来依赖3.可能供rmic来生成桩(stub)和骨架(skeleton)
*/
public interface IR_Service extends Remote{
public Object doSomeService()
throws RemoteException;
}

2.远程对象对接口的实现

package RemoteTest.RemoteImpls;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.text.SimpleDateFormat;

import RemoteTest.RemoteInterfaces.IR_Service;


/*
* 该类是远程对象,它实现远程接口IR_Service并继承了UnicastRemoteObject
* 该类功能很简单,就是返回当前的时间
* 远程技术使用了“针对接口编程”对象技术原则,这样符合“依赖抽象”
* 对象技术原理,可随时更改实现,而改动仅局部影响,可以配合“工厂模式”
* 来返回不同的接口实现者
*/
public class R_ServerImpl extends UnicastRemoteObject implements IR_Service {
    //一个时间格式化对象
private SimpleDateFormat sdf;

//远程对象的无参构造器是必须的,可能被使用来动态生成实例
public R_ServerImpl() throws RemoteException {
   super();
   this.sdf =new SimpleDateFormat("MMM dd '('hh:mm:ss')'");
}

@Override
public Object doSomeService() throws RemoteException {
   // 返回当前的时间:
   return this.sdf.format(new java.util.Date());
}

}

3.服务端注册远程对象:
package RemoteTest.RemoteServer;

import java.rmi.RMISecurityManager;
import javax.naming.Context;
import javax.naming.InitialContext;

import RemoteTest.RemoteImpls.R_ServerImpl;

public class Server {
//private static String urlBase="rmi://localhost";
/**
* 该方法启动服务端
*/
public static void main(String[] args) {
   if(System.getSecurityManager()==null){
    //并未安装安全管理器,按装一个
    System.setSecurityManager(
      new RMISecurityManager());
   }
  
   try{
   R_ServerImpl r_component=new R_ServerImpl();
  
   //获取JNDI名字服务上下文对象
   Context context=new InitialContext();
   //绑定远程对象,绑定键必须以rmi:做前缀
   context.bind("rmi:RmtServerComp",r_component);
  
   System.out.println("成功绑定远程对象");
  
   }catch(Exception ex){
    System.out.println("绑定远程对象失败:"+ex.getMessage());
   }
  
}
}
4.运行服务端时,请使用rmic编译远程接口的类文件(我在自己的机子上运行失败!)务必搞清楚
源文件是接口还是实现的类文件,生成的桩文件和骨架文件(即目标文件)应该放在那个类路径下??
请看到这篇文章的朋友自己在网上查下,这个步骤很关键可惜我在Eclipse和NetbeansIDE下都没有
看到对rmic工具的直接集成或许有我没找到而已,知者告知,谢了!命令行又没运行成功。。。
5.运行rmiregister,必要时换掉默认的端口,自己指定一个。如果你换了默认端口则在查找时必须
在查找键中指定端口号

6.运行客户端:
package RemoteTest.RemoteClient;

import java.rmi.RMISecurityManager;
import javax.naming.Context;
import javax.naming.InitialContext;

import RemoteTest.RemoteInterfaces.IR_Service;
//远程对象的使用者
public class Client {
private static String rmiUrlBase="rmi://localhost:1099//";
//jdk5后新增的变参语法
public static void main(String... args){
   //先安装安全管理器:
   if(System.getSecurityManager()==null){
    System.setSecurityManager(
      new RMISecurityManager());
   }
   try{
    //获取上下文
    Context context=new InitialContext();
    //通过名字服务来查找远程对象
    IR_Service r_serverComp=
     (IR_Service)context.lookup(rmiUrlBase+"RmtServerComp");
   
    String rsltStr=
    (String)r_serverComp.doSomeService();
   
    System.out.println("调用远程对象成功;结果:"+rsltStr);
   }catch(Exception ex){
    System.out.println("调用远程对象失败:"+ex.getMessage());
   }
}
}

结构示意:

***
××××
这里只是rmi最简单的流程演示,当然还有其他的rmi技巧,比如设计模式的融合等,愿于朋友们共同学习

参考书籍《Enterprise java best Practice》,孙卫琴:《Java 网络编程精解》
××××

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值