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);
}
}
在程式的實作中,您完全不需要處理到有關服務連結的種種細節,代理物件會自動幫您完成這些細節,單從程式來看,您根本不會注意到您正在取得遠端伺服器上的服務。