RMI調(diào)用的過程,我的理解大致如下:
- 客戶端和服務(wù)器端約定好要用于遠程調(diào)用的方法的接口(包括方法名和輸入輸出參數(shù))。
- 服務(wù)器端將遠程服務(wù)綁定到某個端口,進行TCP/IP監(jiān)聽(例如監(jiān)聽地址為rmi://127.0.0.1:1099)。
- 客戶端與服務(wù)器建立TCP/IP連接(當然事先要知道服務(wù)器端的監(jiān)聽地址),按照接口聲明的方法發(fā)送數(shù)據(jù)(可序列化的Java對象)以及要調(diào)用的服務(wù)名、方法名等。
- 服務(wù)器端將接收到的數(shù)據(jù)(字節(jié)流)反序列化,得到要調(diào)用的方法和參數(shù)信息;然后服務(wù)器端進行本地計算(方法調(diào)用)。
- 服務(wù)器端將計算的結(jié)果(可序列化的Java對象)發(fā)送給客戶端。
- 客戶端從接收到的返回數(shù)據(jù)(字節(jié)流)中進行反序列化,得到運算結(jié)果。
- 客戶端斷開與服務(wù)器端的TCP/IP連接,遠程調(diào)用結(jié)束。
2 Spring中RMI的使用
開始下面的步驟之前,我們要從Spring的網(wǎng)站下載開發(fā)所需要的jar包(實現(xiàn)RMI的部分的包在org.springframework.remoting.rmi中)。
2.1 服務(wù)器端開發(fā)- 聲明要發(fā)布為RMI服務(wù)的接口
package demo.rmi.server;
/**
* The purpose of this class is to define all presence method
*
*/
public interface IPresence {
/**
* subscribe presence info for the watcher
*
* @param watcherUri
* @param presentities
*/
public void subscribePresence(String watcherUri, String[] presentities);
}
Ø 實現(xiàn)要發(fā)布為RMI服務(wù)的接口
package demo.rmi.server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The purpose of this class is to implment presence operation
*/
public class PresenceImpl implements IPresence {
private static Logger log = LoggerFactory.getLogger(PresenceImpl.class);
public void subscribePresence(String watcherUri, String[] presentities) {
if ((watcherUri == null) || (presentities == null)) {
log.warn("watcherUri or presentities is null!");
return;
}
if (log.isInfoEnabled()) {
StringBuilder s = new StringBuilder();
if (presentities != null) {
for (String str : presentities) {
s.append(str).append(" ");
}
}
log.info("subscribe presence for watcher:{}, presentities:{}",
watcherUri, s);
}
}
}
- 發(fā)布自己的RMI服務(wù)
首先在Spring配置文件中(這里我命名為rmi-server.xml),配置所需要的Bean。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="presenceService" class="demo.rmi.server.PresenceImpl" />
<!-- define a RMI service listening at port 1001 -->
<bean id="serviceExporter_1001" class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="service">
<ref bean="presenceService" />
</property>
<property name="serviceName">
<value>presenceService</value>
</property>
<property name="serviceInterface">
<value>demo.rmi.server.IPresence</value>
</property>
<!-- defaults to 1099 -->
<property name="registryPort" value="1001" />
</bean>
</beans>
然后啟動我們的RMI服務(wù),進行綁定和監(jiān)聽。
package demo.rmi.server;
import java.rmi.RemoteException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.remoting.rmi.RmiServiceExporter;
/**
* The purpose of this class is to represent a rmi server via spring remoting
* support
*
*/
public class RMIServer {
private static Logger log = LoggerFactory.getLogger(RMIServer.class);
/**
* start server by hard code
*/
public void startByAPI() {
RmiServiceExporter exporter = new RmiServiceExporter();
try {
exporter.setServiceInterface(IPresence.class);
exporter.setServiceName("serviceByAPI");
exporter.setService(new PresenceImpl());
exporter.setRegistryPort(1001);
exporter.afterPropertiesSet();
} catch (RemoteException e) {
log.error("error when export service", e);
}
}
/**
* start server by configuration in bean.xml
*/
public void startByCfg() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"rmi-server.xml");
if (log.isInfoEnabled()) {
log.info("application context 's display name :{}", context
.getDisplayName());
log.info("RMI server initialized successfully!");
}
}
/**
* main method for start a rmi server
*
* @param args
*/
public static void main(String[] args) {
// new RMIServer().startByAPI();
new RMIServer().startByCfg();
}
}
當我們初始化Spring的ApplicationContext后,Spring會自動完成RMI服務(wù)的綁定和監(jiān)聽。
2.2 客戶端開發(fā)首先在Spring配置文件中(這里我命名為rmi-client.xml),配置所需要的Bean。
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!-- define a RMI client service which send to port 1001 -->
<bean id="prensenceServiceProxy_1001" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl">
<value>rmi://127.0.0.1:1001/presenceService</value>
</property>
<property name="serviceInterface">
<value>demo.rmi.server.IPresence</value>
</property>
</bean>
</beans>
然后我們可以編寫客戶端進行RMI調(diào)用的代碼。
package demo.rmi.client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import demo.rmi.server.IPresence;
/**
* The purpose of this class is to represent a RMI client
*/
public class RMIClient {
private static Logger log = LoggerFactory.getLogger(RMIClient.class);
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"rmi-client.xml");
if (log.isInfoEnabled()) {
log.info("application context 's display name :{}", context
.getDisplayName());
log.info("RMI client initialized successfully!");
}
// test get a RMI client service via Spring API at runtime
// RmiProxyFactoryBean factory = new RmiProxyFactoryBean();
// factory.setServiceInterface(IPresence.class);
// factory.setServiceUrl("rmi://127.0.0.1:1002/presenceService");
// factory.afterPropertiesSet();
//
// IPresence service2 = (IPresence) factory.getObject();
//
// String[] wt = service2.getMyWatchers("sip:test@mycompany.com");
// System.out.println("service2 works fine,response watchers 's length:"
// + wt.length);
IPresence service = (IPresence) context
.getBean("prensenceServiceProxy_1001");
String watcher = "sip:alice@mycompany.com";
String[] presentities = new String[] { "sip:bob@ mycompany.com",
"sip:susan@mycompany.com" };
StringBuilder presentitiesInfo = new StringBuilder();
for (String str : presentities) {
presentitiesInfo.append(str).append(" ");
}
if (log.isInfoEnabled()) {
log.info("now start send subscribe presnece request for watcher:{}"
+ " ,presentities:{}", watcher, presentitiesInfo);
}
service.subscribePresence(watcher, presentities);
}
}
大家可以注意到,我分別在服務(wù)器端和客戶端放入了使用Spring API動態(tài)(即不在Spring配置文件中固定配置)進行RMI監(jiān)聽和調(diào)用的代碼,可供需要時參考。