RMI (Remote Method Invocation)是从 JDK 1.1 开始就出现的 API 功能,它让客户端在使用远 端物件所提供的服务时,就如何使用本地物件一样,然而 RMI 在使用时必须一连串繁复的手续, 像是服务介 面在定义时必须继承 java.rmi.Remote 介面、服务 Server 在实作时必须继承 java.rmi.UnicastRemoteObject 类 别、必须使用 rmic 指令产生 stub 与 skeleton 等,设定上 手续繁杂。
您可以在 Spring 中透过 org.springframework.remoting.rmi.RmiServiceExporter 来简化使用 RMI 的手续,来实际看看例子,了解 Spring 在 RMI 上的使用与简化,首先来看一下 RMI 伺服端 的撰写,首先定义一个服务物件的介面:
• ISomeService.java
package onlyfun.caterpillar; public interface ISomeService { public String doSomeService(String some); public int doOtherService(int other); }
服务物件的介面不用继承 java.rmi.Remote 介面,而在实作 ISomeService 时也不用继承 java.rmi.UnicastRemoteObject 类别,例如:
• SomeService.java
package onlyfun.caterpillar; public class SomeServiceImpl implements ISomeService { public String doSomeService(String some) { return some + " is processed"; } public int doOtherService(int other) { return ++other; } }
这个实作只是个简单的示范,两个方法都只是传回一个已经修改过的值,接下来您只要在 Bean 定义档中定义,让 Spring 管理、生成 Bean 实例,如此即可注册、启动 RMI 服务,例如:
• rmi-server.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN""http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="someService" class="onlyfun.caterpillar.SomeServiceImpl"/> <bean id="serviceExporter" class="org.springframework.remoting. → rmi.RmiServiceExporter"> <property name="service"> <ref bean="someService"/> </property> <property name="serviceName"> <value>SomeService</value> </property> <property name="serviceInterface"> <value>onlyfun.caterpillar.ISomeService</value> </property> </bean> </beans>
很简单,只要告诉 org.springframework.remoting.rmi.RmiServiceExporter 服务物件、名称(注 意在 "serviceName"属性上设定为"SomeService")与要代理的介面,之后 Spring 读取完定义档 并生成 Bean 实例后,RMI 服务就 会启动,来撰写一个简单的 RMIServer 类别,以启动 RMI 服务:
• RMIServer.java
package onlyfun.caterpillar; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import org.springframework.context. support.FileSystemXmlApplicationContext; public class RMIServer { public static void main(String[] args) throws IOException { new FileSystemXmlApplicationContext("rmi-server.xml"); System.out.println("启动 RMI Server.."); System.out.println("请输入 exit 关闭 Server: "); BufferedReader reader =new BufferedReader( new InputStreamReader(System.in)); while(true) { if(reader.readLine().equals("exit")) { System.exit(0); } } } }
在运行上面的程式之后,RMI 服务就会启动,Spring 会自动使用另一个执行绪来执行 RMI 服务, 所以您不用关心执行绪的处理问题,您可以输入 "exit"直接离开程式,接着来看一下,如何实 作一个 RMI 客户端以向 RMI 伺服器要求服务,首先要记得的是,客户端是依赖于抽象的介面,也 就是先前的 ISomeService 介面之.class 档也必须在客户端有一份。
在客户端需要 RMI 服务时,只要透过 org.springframework.remoting.rmi.RmiProxyFactoryBean, 并告 知服务的 URL(对应至先前设定的"SomeService"名称)、代理的介面即可,在撰写程式时 就好像在使用本地端管理的服务一样,例如 Bean 定义 档可以如下撰写:
• rmi-client.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="someServiceProxy" class="org.springframework.remoting. → rmi.RmiProxyFactoryBean"> <property name="serviceUrl"> <value>rmi://localhost/SomeService</value> </property> <property name="serviceInterface"> <value>onlyfun.caterpillar.ISomeService</value> </property> </bean> </beans>
注意到"serviceUrl"属性的设定,它是以"rmi://"开头,接着指定伺服器位址与服务名称,来撰 写个简单的客户端程式以使用 RMI 伺服器上的服务
• RMIClient.java
package onlyfun.caterpillar; import org.springframework.context.ApplicationContext; import org.springframework.context. support.FileSystemXmlApplicationContext; public class RMIClient { public static void main(String[] args) { ApplicationContext context =new FileSystemXmlApplicationContext( "rmi-client.xml"); ISomeService service =(ISomeService) context.getBean("someServiceProxy"); String result1 = service.doSomeService("Some request"); System.out.println(result1); int result2 = service.doOtherService(1); System.out.println(result2); } }
在程式的实作中,您完全不需要处理到有关服务连结的种种细节,代理物件会自动帮您完成这些细节,单从程式来看,您根本不会注意到您正在取得远端伺服器上的服务。