ITEEDU

RMI

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);
	}
}

在程式的实作中,您完全不需要处理到有关服务连结的种种细节,代理物件会自动帮您完成这些细节,单从程式来看,您根本不会注意到您正在取得远端伺服器上的服务。