Spring 對 Java RMI 進行了深度封裝,通過 RmiServiceExporter(服務端)與 RmiProxyFactoryBean(客戶端)實現透明化遠程調用。本文提供完整可運行的雙端配置代碼,涵蓋接口定義、序列化實體、服務導出、註冊端口、客戶端代理生成與調用驗證。
┌─────────────┐ RMI/JRMP ┌─────────────┐
│ Server │ ◄─────────────────► │ Client │
│ (Exporter) │ 10.7.3.12:9999 │ (Proxy) │
└─────────────┘ └─────────────┘
Spring 容器 Spring 容器
RmiServiceExporter RmiProxyFactoryBean
// UserRmiService.java
package com.goodfan.rmi.service;
public interface UserRmiService {
String sayHello(User user);
}
// User.java
package com.goodfan.rmi.service;
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 8550373205815267923L;
private String userName;
// getter/setter...
}
// UserRmiServiceImpl.java
package com.goodfan.rmi.service.impl;
import com.goodfan.rmi.service.User;
import com.goodfan.rmi.service.UserRmiService;
public class UserRmiServiceImpl implements UserRmiService {
@Override
public String sayHello(User user) {
return "Hello, " + user.getUserName();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 服務實現 Bean -->
<bean id="userRmiServiceImpl" class="com.goodfan.rmi.service.impl.UserRmiServiceImpl" />
<!-- RMI 服務導出器 -->
<bean id="userRmi" class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="service" ref="userRmiServiceImpl" /> <!-- 目標服務 -->
<property name="serviceName" value="userRmi" /> <-- 註冊名稱 -->
<property name="serviceInterface" value="com.goodfan.rmi.service.UserRmiService" /> <-- 遠程接口 -->
<property name="registryPort" value="9999" /> <-- RMI 註冊表端口 -->
</bean>
</beans>
// ServerTest.java
package com.goodfan.rmi.service;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ServerTest {
public static void main(String[] args) {
// 關鍵:指定服務端綁定 IP,防止綁定 127.0.0.1 導致客戶端無法連接
System.setProperty("java.rmi.server.hostname", "10.7.3.12");
new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("RMI Server started on port 9999...");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="rmiProxy" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://10.7.3.12:9999/userRmi" /> <-- 完整 RMI URL -->
<property name="serviceInterface" value="com.goodfan.rmi.service.UserRmiService" />
</bean>
</beans>
// ClientTest.java
package com.goodfan.rmi.service;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ClientTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-client.xml");
UserRmiService service = (UserRmiService) ctx.getBean("rmiProxy");
User user = new User();
user.setUserName("RMI");
System.out.println(service.sayHello(user)); // 輸出:Hello, RMI
}
}
| 組件 | 核心作用 | 關鍵屬性 |
|---|---|---|
RmiServiceExporter |
將 Spring Bean 導出為 RMI 遠程服務 | service、serviceName、serviceInterface、registryPort |
RmiProxyFactoryBean |
創建遠程服務的客戶端代理 | serviceUrl、serviceInterface |
java.rmi.server.hostname |
指定服務端對外暴露的 IP(多網卡環境必須) | 系統屬性,啟動前設置 |
| 序列化 | 參數/返回值跨 JVM 傳輸 | 實體必須實現 Serializable,建議顯式 serialVersionUID |
| 註冊表 | 服務名稱 ↔ 存根 映射 | 默認端口 1099,此處配置 9999 |
java.rmi.server.hostname 是否為可訪問 IP、端口是否衝突。serialVersionUID 必須一致。java.rmi.server.hostname 並在防火牆開放註冊端口+隨機端口範圍,或使用 HTTP 隧道/VPN。sun.rmi.transport.tcp.responseTimeout 系統屬性控制調用超時。@Configuration
public class RmiConfig {
@Bean
public RmiServiceExporter userRmiExporter(UserRmiServiceImpl impl) {
RmiServiceExporter exporter = new RmiServiceExporter();
exporter.setService(impl);
exporter.setServiceName("userRmi");
exporter.setServiceInterface(UserRmiService.class);
exporter.setRegistryPort(9999);
return exporter;
}
@Bean
public RmiProxyFactoryBean userRmiProxy() {
RmiProxyFactoryBean proxy = new RmiProxyFactoryBean();
proxy.setServiceUrl("rmi://10.7.3.12:9999/userRmi");
proxy.setServiceInterface(UserRmiService.class);
return proxy;
}
}
RmiServiceExporter、客戶端 RmiProxyFactoryBean 即可實現透明遠程調用掌握 Spring RMI 配置,能快速實現傳統系統集成、單體應用模塊解耦、測試環境服務模擬等場景。