感觉这篇文章不错,直接转了
RMI全称是Remote Method Invocation-远程方法调用,Java RMI在JDK1.1中实现的,其威力就体如今它强大的开发分布式网络应用的能力上,是纯Java的网络分布式应用系统的核心解决方式之中的一个。事实上它能够被看作是RPC的Java版本号。可是传统RPC并不能非常好地应用于分布式对象系统。而Java RMI 则支持存储于不同地址空间的程序级对象之间彼此进行通信。实现远程对象之间的无缝远程调用。
RMI眼下使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信。因为JRMP是专为Java对象制定的,Java RMI具有Java的"Write Once,Run Anywhere"的长处,是分布式应用系统的百分之百纯Java解决方式。用Java RMI开发的应用系统能够部署在不论什么支持JRE(Java Run Environment Java,执行环境)的平台上。但因为JRMP是专为Java对象制定的,因此,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通信。
RMI可利用标准Java本机方法接口与现有的和原有的系统相连接。RMI还可利用标准包与现有的关系连接。
RMI/JNI和RMI/JDBC相结合,可帮助您利用RMI与眼下使用非Java语言的现有server进行通信,并且在您须要时可扩展Java在这些server上的使用。RMI可帮助您在扩展使用时充分利用Java的强大功能。
一、RMI(远程方法调用)的组成
一个正常工作的RMI系统由以下几个部分组成:
·远程服务的接口定义 ·远程服务接口的详细实现 ·桩(Stub)和框架(Skeleton)文件 ·一个执行远程服务的server ·一个RMI命名服务。它同意去发现这个远程服务 ·类文件的提供者(一个HTTP或者FTPserver) ·一个须要这个远程服务的client程序
二、RMI(远程方法调用)原理示意图
方法调用从客户对象经占位程序(Stub)、远程引用层(Remote Reference Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传 输层。向上穿过远程调用层和骨干网(Skeleton),到达server对象。
占位程序扮演着远程server对象的代理的角色。使该对象可被客户激活。 远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个server还是多个。
传输层管理实际的连接,而且追踪能够接受方法调用的远程对象。
server端的骨干网完毕对server对象实际的方法调用,并获取返回值。返回值向下经远程引用层、server端的传输层传递回client。再向上经传输层和远程调用层返回。最后。占位程序获得返回值。
要完毕以上步骤须要有下面几个步骤:
1、 生成一个远程接口
2、 实现远程对象(server端程序)
3、 生成占位程序和骨干网(server端程序)
4、 编写server程序
5、 编写客户程序
6、 注冊远程对象
7、 启动远程对象
三、RMI(远程方法调用)的长处
从最主要的角度看。RMI是Java的(RPC)机制。与传统的RPC系统相比。RMI具有若干长处。由于它是Java的一部分。传统的RPC系统採用中性语言,所以是最普通的系统--它们不能提供全部可能的目标平台所具有的功能。
RMI以Java为核心。可与採用本机方法与现有系统相连接。这就是说,RMI可採用自然、直接和功能全面的方式为您提供分布式计算技术,而这样的技术可帮助您以不断递增和无缝的方式为整个系统加入Java功能。
RMI的主要长处例如以下:
:RMI可将完整的对象作为參数和返回值进行传递,而不不过提前定义的数据。
也就是说。您能够将类似Java哈希表这种复杂类型作为一个參数进行传递。而在眼下的RPC系统中,您只能依靠客户机将此类对象分解成基本数据类型。然后传递这些数据类型,最后在server端又一次创建哈希表。RMI则不需额外的客户程序代码(将对象分解成基本数据类型),直接跨网传递对象。
可移动属性:RMI可将属性(类实现程序)从客户机移动到server,或者从server移到客户机。这样就能具备最大的灵活性,由于政策改变时仅仅须要您编写一个新的Java类,并将其在server主机上安装一次就可以。
设计方式:对象传递功能使您能够在分布式计算中充分利用面向对象技术的强大功能。如二层和三层结构系统。假设您能够传递属性,那么您就能够在您的解决方式中使用面向对象的设计方式。全部面向对象的设计方式无不依靠不同的属性来发挥功能,假设不能传递完整的对象--包含实现和类型--就会失去设计方式上所提供的长处。
安 全:RMI使用Java内置的安全机制保证下载运行程序时用户系统的安全。
RMI使用专门为保护系统免遭恶意侵害而设计的安全管理程序。可保护您的系统和网络免遭潜在的恶意下载程序的破坏。在情况严重时,server可拒绝下载不论什么运行程序。
便于编写和使用:RMI使得Java远程服务程序和訪问这些服务程序的Java客户程序的编写工作变得轻松、简单。远程接口实际上就是Java接口。服务程序大约用三行指令宣布本身是服务程序,其他方面则与不论什么其他Java对象类似。
这样的简单方法便于高速编写完整的分布式对象系统的服务程序,并高速地制做的原型和早期版本号,以便于进行測试和评估。由于RMI程序编写简单,所以维护也简单。
可连接现有/原有的系统:RMI可通过Java的本机方法接口JNI与现有系统进行进行交互。
利用RMI和JNI,您就能用Java语言编写client程序,还能使用现有的server端程序。
在使用RMI/JNI与现有server连接时,您能够有选择地用Java又一次编写服务程序的不论什么部分,并使新的程序充分发挥Java的功能。
类似地。RMI可利用JDBC、在不改动使用数据库的现有非Java源码的前提下与现有关系数据库进行交互。
编写一次。到处执行:RMI是Java“编写一次,到处执行 ”方法的一部分。不论什么基于RMI的系统均可100%地移植到不论什么上,RMI/JDBC系统也不例外。假设使用RMI/JNI与现有系统进行交互工作,则採用JNI编写的代码可与不论什么Java虚拟机进行编译、执行。
分布式垃圾收集:RMI採用其分布式垃圾收集功能收集不再被网络中不论什么客户程序所引用的远程服务对象。与Java 虚拟机内部的垃圾收集类似。分布式垃圾收集功能同意用户依据自己的须要定义server对象。而且明白这些对象在不再被客户机引用时会被删除。
并行计算:RMI採用多处理方法。可使您的server利用这些Java线程更好地并行处理client的请求。Java分布式计算解决方式:RMI从JDK 1.1開始就是Java平台的核心部分,因此。它存在于不论什么一台1.1 Java虚拟机中。全部RMI系统均採用同样的公开协议,所以。全部Java 系统均可直接相互对话。而不必事先对协议进行转换。
四、RMI与的关系
RMI 和 CORBA 常被视为相互竞争的技术。由于两者都提供对远程分布式对象的透明訪问。但这两种技术实际上是相互补充的。一者的好处正好能够弥补还有一者的短处。RMI 和 CORBA 的结合产生了 RMI-IIOP,RMI-IIOP 是企业server端 Java 开发的基础。
1997 年,IBM 和 Sun Microsystems启动了一项旨在促进 Java 作为企业开发技术的发展的合作计划。两家公司特别着力于怎样将 Java 用作server端语言。生成能够结合进现有的企业级代码。
所须要的就是一种远程传输技术。它兼有 Java 的 RMI(Remote Method Invocation,远程方法调用)较少的资源占用量和更成熟的 CORBA(Common Request Broker 。)技术的健壮性。
出于这一须要,RMI-IIOP问世了,它帮助将 Java 语言推向了眼下server端企业开发的主流语言的率先地位。
(来源:sun。matrix.org.cn)
Java RMI 简单演示样例一
下面用一个最简单的Hello的演示样例来介绍RMI的应用。
1:定义一个远程接口
IHello.java代码例如以下:
import java.rmi.Remote; public interface IHello extends Remote { public String sayHello(String name) throws java.rmi.RemoteException; }
2:实现远程的接口(服务端就在此远程接口的实现类中)
HelloImpl.java代码例如以下:
import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class HelloImpl extends UnicastRemoteObject implements IHello { // 这个实现必须有一个显式的构造函数。而且要抛出一个RemoteException异常 protected HelloImpl() throws RemoteException { super (); } /** * 说明清楚此属性的业务含义 */ private static final long serialVersionUID = 4077329331699640331L ; public String sayHello(String name) throws RemoteException { return " Hello " + name + " ^_^ " ; } public static void main(String[] args) { try { IHello hello = new HelloImpl(); java.rmi.Naming.rebind( " rmi://localhost:1099/hello " , hello); System.out.print( " Ready " ); } catch (Exception e) { e.printStackTrace(); } } }
3:新建RMIclient调用程序
Hello_RMI_Client.java代码例如以下:
import java.rmi.Naming; public class Hello_RMI_Client { public static void main(String[] args) { try { IHello hello = (IHello) Naming.lookup( " rmi://localhost:1099/hello " ); System.out.println(hello.sayHello( " zhangxianxin " )); } catch (Exception e) { e.printStackTrace(); } } }
4:编译并执行
4.1 用javac命令编译IHello.java、HelloImpl.java、Hello_RMI_Client.java
>javac *.java
4.2 用rmic命令生成桩和框架文件
>rmic HelloImpl
成功执行完上面的命令能够发现生成一个HelloImpl_stub.class文件,假设JDK是使用Java2SDK。那么还能够发现多出一个HelloImpl_Skel.class文件。假设服务端程序与client程序在同一台机器上并在同一文件夹中,则能够省略掉接口实现类生成的桩和框架文件,但这就失去了使用RMI的意义。而假设要在不同的JVM上执行时。client程序就必须得依靠服务端运程方法实现的桩和框架文件以及接口类。
4.3 执行注冊程序RMIRegistry,必须在包括刚写的类的文件夹下执行这个注冊程序。
>rmiregistry
注冊程序開始执行了,不要管他,如今切换到另外一个控制台执行server
4.4 执行serverHelloImpl
>java HelloImpl
当启动成功出现Ready...... 这个server就開始工作了。把接口的实现载入到内存等待client的联接。
如今切换到第三个控制台,启动我们的client。
4.5 启动client:为了在其它的机器执行client程序你须要一个远程接口(IHello.class) 和一个stub(HelloImpl_Stub.class)。 使用例如以下命令执行client
>java Hello_RMI_Client
当执行成功会在控制台打印:Hello zhangxianxin ^_^
备注:假设不想在控制台上开启RMI注冊程序RMIRegistry的话,可在RMI服务类程序中加入LocateRegistry.createRegistry(1099); 例如以下所看到的:
改动后的HelloImpl.java代码例如以下:
import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.server.UnicastRemoteObject; public class HelloImpl extends UnicastRemoteObject implements IHello { // 这个实现必须有一个显式的构造函数,而且要抛出一个RemoteException异常 protected HelloImpl() throws RemoteException { super (); } private static final long serialVersionUID = 4077329331699640331L ; public String sayHello(String name) throws RemoteException { return " Hello " + name + " ^_^ " ; } public static void main(String[] args) { try { IHello hello = new HelloImpl(); LocateRegistry.createRegistry( 1099 ); // 加上此程序,就能够不要在控制台上开启RMI的注冊程序,1099是RMI服务监视的默认port java.rmi.Naming.rebind( " rmi://localhost:1099/hello " , hello); System.out.print( " Ready " ); } catch (Exception e) { e.printStackTrace(); } } }
Java RMI 简单演示样例二
下面用一个文件交换程序来介绍RMI的应用。
这个应用同意client从服务端交换(或下载)全部类型的文件。
第一步是定义一个远程的接口,这个接口指定的签名方法将被服务端提供和被client调用。
1.定义一个远程接口
IFileUtil.java代码例如以下:
import java.rmi.Remote; import java.rmi.RemoteException; public interface IFileUtil extends Remote { public byte [] downloadFile(String fileName) throws RemoteException; } 接口IFileDownload提供了一个downloadFile方法,然后返回一个对应的文件数据。
2.实现远程的接口 类FileImpl继承于UnicastRemoteObject类。
这显示出FileImpl类是用来创建一个单独的、不能复制的、远程的对象,这个对象使用RMI默认的基于TCP的通信方式。
FileUtilImpl.java代码例如以下:
import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class FileUtilImpl extends UnicastRemoteObject implements IFileUtil { protected FileUtilImpl() throws RemoteException { super (); } private static final long serialVersionUID = 7594622080290821912L ; public byte [] downloadFile(String fileName) throws RemoteException{ File file = new File(fileName); byte buffer[] = new byte [( int ) file.length()]; int size = buffer.length; System.out.println( " download file size = " + size + " b " ); if (size > 1024 * 1024 * 10 ){ // 限制文件大小不能超过10M。文件太大可能导制内存溢出。 throw new RemoteException( " Error:<The File is too big!> " ); } try { BufferedInputStream input = new BufferedInputStream( new FileInputStream(fileName)); input.read(buffer, 0 , buffer.length); input.close(); System.out.println( " Info:<downloadFile() hed execute successful!> " ); return buffer; } catch (Exception e) { System.out.println( " FileUtilImpl: " + e.getMessage()); e.printStackTrace(); return null ; } } }
3.编写服务端
(1)创建并安装一个RMISecurityManager实例。
(2)创建一个远程对象的实例。
(3)使用RMI注冊工具来注冊这个对象。
FileUtilServer.java 代码例如以下:
import java.rmi.Naming; import java.rmi.RMISecurityManager; public class FileUtilServer { public static void main(String argv[]) { try { IFileUtil file = new FileUtilImpl(); // LocateRegistry.createRegistry(1099); // 加上此程序。就能够不要在控制台上开启RMI的注冊程序。1099是RMI服务监视的默认port Naming.rebind( " rmi://127.0.0.1/FileUtilServer " , file); System.out.print( " Ready " ); } catch (Exception e) { System.out.println( " FileUtilServer: " + e.getMessage()); e.printStackTrace(); } } } 声明Naming.rebind("rmi://127.0.0.1/FileUtilServer", file) 中假定了RMI注冊工具(RMI registry )使用并启动了1099port。假设在其它port执行了RMI注冊工具,则必须在这个声明中定义。比如,假设RMI注冊工具在4500port执行,则声明应为: Naming.rebind("rmi://127.0.0.1:4500/FileUtilServer", file)
另外我们已经同一时候假定了我们的服务端和RMI注冊工具是执行在同一台机器上的。否则须要改动rebind方法中的地址。
4.编写client
client能够远程调用远程接口(FileInterface)中的不论什么一个方法。
不管怎样实现。client必须先从RMI注冊工具中获取一个远程对象的引用。当引用获得后。方法downloadFile被调用。在执行过程中,client从命令行中获得两个參数,第一个是要下载的文件名称,第二个是要下载的机器的地址,在相应地址的机器上执行服务端。
FileUtilClient.java 代码例如以下:
import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.rmi.Naming; public class FileUtilClient { public static void main(String args[]) { if (args.length != 3 ) { System.out.println( " 第一个參数:RMI服务的IP地址 " ); System.out.println( " 第二个參数:要下载的文件名称 " ); System.out.println( " 第三个參数:要文件保存位置 " ); System.exit( 0 ); } try { String name = " rmi:// " + args[ 0 ] + " /FileUtilServer " ; IFileUtil fileUtil = (IFileUtil) Naming.lookup(name); byte [] filedata = fileUtil.downloadFile(args[ 1 ]); if (filedata == null ){ System.out.println( " Error:<filedata is null!> " ); System.exit( 0 ); } File file = new File(args[ 2 ]); System.out.println( " file.getAbsolutePath() = " + file.getAbsolutePath()); BufferedOutputStream output = new BufferedOutputStream( new FileOutputStream(file.getAbsolutePath())); output.write(filedata, 0 , filedata.length); output.flush(); output.close(); System.out.println( " ~~~~~End~~~~~ " ); } catch (Exception e) { System.err.println( " FileUtilServer exception: " + e.getMessage()); e.printStackTrace(); } } }
5.执行程序为了执行程序,我们必须使用rmic来编译生成stubs和skeletons:
>rmic FileUtilImpl
这将会生成FileUtilImpl_Stub.class和FileUtilImpl_Skel.class两个文件。stub是client的代理,而skeleton是服务端的框架。服务端和client採用javac来编译(假设服务端和client在两个不同的机器。则必须复制一个IFileUtil接口)。
使用rmiregistry或者start rmiregistry 命令来执行RMI注冊工具到window系统默认的port上:> rmiregistry portNumber此处的portNumber为端口,RMI注冊工具执行之后。须要执行服务FileUtilServer,由于RMI的安全机制将在服务端发生作用,所以必须添加一条安全策略: grant{permission java.security.AllPermission "", "";};
为了执行服务端,须要有除客户类(FileUtilClient.class)之外全部的类文件。确认安全策略在policy.txt文件之后,使用例如以下命令来执行server。
> java -Djava.security.policy=policy.txt FileUtilServer
为了在其它的机器执行client程序,须要一个远程接口(IFileUtil.class)和一个stub(FileUtilImpl_Stub.class)。 使用例如以下命令执行client:> java FileUtilClient fileName machineName savePath 这里fileName是要下载的文件名称,machineName 是要下载的文件所在的机器(也是服务端所在的机器),savePath 是要将下载过来的文件保存的路径(带文件名称)。
假设所有通过的话,当client执行后。则这个文件将被下载到本地。
Spring对RMI的支持
1.使用RMI暴露服务
使用Spring的RMI支持,你能够通过RMI基础设施透明的暴露你的服务。
设置好Spring的RMI支持后。你会看到一个和远程EJB接口类似的配置。仅仅是没有对安全上下文传递和远程事务传递的标准支持。当使用RMI调用器时,Spring对这些额外的调用上下文提供了钩子,你能够在此插入安全框架或者定制的安全证书。
2. 使用 RmiServiceExporter 暴露服务
使用 RmiServiceExporter,我们能够把AccountService对象的接口暴露成RMI对象。能够使用 RmiProxyFactoryBean 或者在传统RMI服务中使用普通RMI来訪问该接口。RmiServiceExporter 显式地支持使用RMI调用器暴露不论什么非RMI的服务。
当然,我们首先须要在Spring BeanFactory中设置我们的服务:
< bean id ="accountService" class ="example.AccountServiceImpl" > <!-- any additional properties, maybe a DAO? --> </ bean > 然后,我们将使用 RmiServiceExporter 来暴露我们的服务:
< bean class ="org.springframework.remoting.rmi.RmiServiceExporter" > <!-- does not necessarily have to be the same name as the bean to be exported --> < property name ="serviceName" value ="AccountService" /> < property name ="service" ref ="accountService" /> < property name ="serviceInterface" value ="example.AccountService" /> <!-- defaults to 1099 --> < property name ="registryPort" value ="1199" /> </ bean > 正如你所见,我们覆盖了RMI注冊的port号。通常,你的应用服务也会维护RMI注冊,最好不要和它冲突。更进一步来说,服务名是用来绑定以下的服务的。所以本例中。服务绑定在 rmi://HOST:1199/AccountService。在client我们将使用这个URL来链接到服务。
注意:我们省略了一个属性,就是 servicePort 属性,它的默认值为0。 这表示在服务通信时使用匿名port。当然假设你愿意的话。也能够指定一个不同的port。
3. 在client链接服务
我们的client是一个使用AccountService来管理account的简单对象:
public class SimpleObject { private AccountService accountService; public void setAccountService(AccountService accountService) { this .accountService = accountService; } } 为了把服务连接到client上,我们将创建还有一个单独的bean工厂,它包括这个简单对象和服务链接配置位:
< bean class ="example.SimpleObject" > < property name ="accountService" ref ="accountService" /> </ bean > < bean id ="accountService" class ="org.springframework.remoting.rmi.RmiProxyFactoryBean" > < property name ="serviceUrl" value ="rmi://HOST:1199/AccountService" /> < property name ="serviceInterface" value ="example.AccountService" /> </ bean > 这就是我们在client为支持远程account服务所须要做的。Spring将透明的创建一个调用器而且通过RmiServiceExporter使得account服务支持远程服务。
在client,我们用RmiProxyFactoryBean连接它。
Spring对RMI支持的实际应用实例
在OMAS系统中提供给业务系统的RMI客户反馈服务的实现服务暴露是通过Resource/modules/interfaces/spring-conf/serviceContext.xml配置文件实现的,而远程接口的实现类必须序列化(即实现Serializable接口)。
Resource/modules/interfaces/spring-conf/serviceContext.xml的内容例如以下:
<? xml version="1.0" encoding="UTF-8" ?> <! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd" > < beans default-autowire ="byName" default-lazy-init ="false" > <!-- service实现类的配置 --> < bean id ="fbWebService" class ="com.ce.omas.interfaces.service.impl.FeedbackWebServiceImpl" /> < bean class ="org.springframework.remoting.rmi.RmiServiceExporter" > <!-- does not necessarily have to be the same name as the bean to be exported --> < property name ="serviceName" value ="FeedbackRMIService" /> < property name ="service" ref ="fbWebService" /> < property name ="serviceInterface" value ="com.ce.omas.interfaces.service.IFeedbackWebService" /> <!-- <property name="registryHost" value="rmi://192.168.100.7"/> --> <!-- defaults to 1099 --> < property name ="registryPort" value ="1199" /> </ bean > </ beans >
相应的暴露的服务接口例如以下:
public interface IFeedbackWebService { /** * <b>方法用途和描写叙述:</b> 客户反馈RMI服务端接口方法<br> * <b>方法的实现逻辑描写叙述:</b> 通过RMI提供服务,Spring支持的RMI远程调用 * @param systemID : 业务系统的唯一标识 * @param feedbackType :用户反馈的类型(1-系统BUG、2-系统易用性、3-客服人员态度、4-运维人员态度、5-其它) * @param feedbackContent :用户反馈的正文内容 * @return 反馈是否成功 true | false */ public boolean setFeedback(String systemID, FeedbackType feedbackType, String feedbackContent) throws OMASServiceException ; }
暴露的服务接口实现例如以下:
public class FeedbackWebServiceImpl implements IFeedbackWebService, Serializable { private static Log _log = LogFactory.getLog(FeedbackWebServiceImpl. class ); private static final long serialVersionUID = - 5532505108644974594L ; /** * 客户反馈服务接口 */ private IFeedbackOperationService feedbackService; /** * 方法用途和描写叙述: 注入运营模块的加入客户反馈的服务 * @param feedbackWebService 运营模块服务 */ public void setFeedbackService(IFeedbackOperationService feedbackWebService) { _log.info( " 注入运营模块的加入客户反馈的服务 " ); this .feedbackService = feedbackWebService; } /** * 方法用途和描写叙述: 外部接口子系统中加入客户反馈的方法 * @param systemID :业务系统ID * @param feedbackType :反馈类型 * @param feedbackContent :反馈内容 * @return 操作是否成功 ture or false * @throws ServiceException */ public boolean setFeedback(String systemID, FeedbackType feedbackType, String feedbackContent) throws OMASServiceException { _log.info( " 进入到外部接口的加入客户反馈的方法 " ); if (BlankUtil.isBlank(systemID) || BlankUtil.isBlank(feedbackType) || BlankUtil.isBlank(feedbackContent)) { _log.error( " 加入客户反馈的接口參数为空! " ); throw new OMASServiceException( " omas.interfaces.001 " ); // 加入客户反馈的接口參数为空 } WebServiceFeedbackVO vo = new WebServiceFeedbackVO(); vo.setFeedbackType(String.valueOf(feedbackType.getValue())); vo.setFeedbackContent(feedbackContent); vo.setSystemID(systemID); _log.info( " 调用运营子系统的加入客户反馈的方法開始! " ); try { if (feedbackService == null ) { _log.error( " 运营模块服务为空 " ); throw new OMASServiceException( " omas.interfaces.002 " ); // 运营模块服务为空 } feedbackService.addFeedbackOperation(vo); } catch (ServiceException e) { _log.error( " 调用运营子系统的加入客户反馈出现异常: " + e.getMessage()); if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_VO.equals(e.getMsgKey())){ // 客户调用接口的对像为空 throw new OMASServiceException( " omas.interfaces.003 " ); } if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_SYSTEMID.equals(e.getMsgKey())){ // 业务系统标识ID为空 throw new OMASServiceException( " omas.omasservice.010 " ); } if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_SIZE.equals(e.getMsgKey())){ // 非法的业务系统唯一标识 throw new OMASServiceException( " omas.interfaces.004 " ); } if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_BASE.equals(e.getMsgKey())){ // 数据库訪问出了一点小问题! throw new OMASServiceException( " omas.interfaces.005 " ); } throw new OMASServiceException( " omas.omasservice.000 " ); // 未捕获到的异常信息。 } return true ; } }
接口方法setFeedback(String, FeedbackType, String)的实现大家不用关心,其与RMI并无关系,仅仅是一些纯业务处理逻辑而已,要注意的是接口实现类必须实现 IfeedbackWebService和Serializable接口。
在客户本地的omasservice.jar包中客户反馈的RMIclient的配置例如以下:
Resource/config/omasrmi-client.xml
<? xml version="1.0" encoding="UTF-8" ?> <! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd" > < beans default-autowire ="byName" default-lazy-init ="true" > < bean id ="fbWebServiceProxy" class ="org.springframework.remoting.rmi.RmiProxyFactoryBean" > < property name ="serviceUrl" > < value > rmi://127.0.0.1:1199/FeedbackRMIService </ value > </ property > < property name ="serviceInterface" > < value > com.ce.omas.interfaces.service.IFeedbackWebService </ value > </ property > </ bean > < bean class ="com.ce.omas.omasservice.service.impl.FeedbackRMIClientImpl" > < property name ="feedbackWebService" ref ="fbWebServiceProxy" /> </ bean > </ beans >
client调用RMI服务的方法例如以下所看到的:
/** * 方法用途和描写叙述: 客户反馈:通过RMI方法与OMAS通讯 * 方法的实现逻辑描写叙述: * @param feedbackType * @param feedbackContent * @return * @throws OMASServiceException */ public static boolean setFeedback_RMIClient(String systemID, FeedbackType feedbackType, String feedbackContent) throws OMASServiceException { if (systemID == null || "" .equals(systemID)) { _log.error( " 业务系统标识<SystemID>为空或不是对象 " ); throw new OMASServiceException( " omas.omasservice.010 " ); } String rmiClientConfigFilePath = PropertyReader .getValue(ConfigConstants.OMASSERVICE_CONFIG_PATH, ConfigConstants.RMI_CLIENT_CONFIG_FILEPATH); if (rmiClientConfigFilePath == null || "" .equals(rmiClientConfigFilePath)) { _log.error( " 配置文件错误:Key<rmiClientConfigFile>为空或不存在 " ); throw new OMASServiceException( " omas.omasservice.006 " ); } _log.info( " rmiClientConfigPath = " + rmiClientConfigFilePath); ApplicationContext context = null ; try { context = new ClassPathXmlApplicationContext(rmiClientConfigFilePath); } catch (Exception e) { _log.error( " 客户反馈:解析rmi-config.xml文件时出现异常: " + e); _log.info( " rmi-config.xml文件路径: " + rmiClientConfigFilePath); throw new OMASServiceException( " omas.omasservice.007 " ); } IFeedbackWebService service = null ; try { service = (IFeedbackWebService) context .getBean("fbWebServiceProxy"); } catch (Exception e) { _log.error( " 从Spring的RMIclientBean配置文件获取服务对象时出现异常: " + e); throw new OMASServiceException( " omas.omasservice.009 " ); } boolean bln = service.setFeedback(systemID, feedbackType, feedbackContent); _log.info( " 反馈操作是否成功[true|false]: " + bln); return bln; } 在此client调用的程序中,你要关注的主要是以上背景色标志为黄色的相关代码。
自己加的
1、关于问题【java RMIclient引用了服务端的接口类?】解答
答:两边是通过接口来交换信息的,当然都须要接口和它的 Stub。
2、stub、Skeleton什么意思?
答:.为屏蔽客户调用远程主机上的对象,必须提供某种方式来模拟本地对象,这样的本地对象称为存根(stub),存根负责接收本地方法调用,并将它们委派给各自的详细实现对象。在远程虚拟机中,每一个远程对象都有一个Skeleton相应,Skeleton负责将调用分配给实际的远程对象来实现。他的工作是在server端的。
Stub的操作任务:
a、初始化与包括远程对象远程虚拟机的连接
b、与远程虚拟机參数进行编组,也就是调度。包含參数的写入以及传输
c、等待方法的调用结果
d、解编返回值和返回的异常。
也就是读取server上的返回值,也称为反调度
e、将结果返回给调用程序。
Skeleton相应的任务:
a、读取远程方法的參数。也就是解编
b、调用实际远程对象上的方法
c、将结果或者异常组编(写入并传输)给调用程序。
Stub与Skeleton的关系以及操作是相应的关系。
仅仅要我们有编译好的远程对象的类,就能够调用jdk的rmic命令来生成stub和skeleton了。关键是我们须要理解Stub是client应用程序的代理,Skeleton是server端应用程序的代理,他们之间协作完毕client与server之间的方法调用时的通信。