spring rmi
RMI 能够让在某个Java虚拟机上的对象像调用本地对象一样调用另一个java 虚拟机中的对象上的方法。
使用spring框架IOC 依赖注入,大大简化了rmi的开发,仅仅需要在xml中配置远程服务,然后调用即可
示例:
public interface HelloRMIService {
int getAdd(int a,int b);
}
public class HelloRMIServiceImpl implements HelloRMIService{
@Override
public int getAdd(int a, int b) {
return a + b;
}
}
public class MyRMIClient {
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext_RMIClient.xml");
HelloRMIService service = (HelloRMIService)context.getBean("myClient");
System.out.println(service.getAdd(5, 4));
}
}
public class MyRMIServer {
public static void main(String[] args){
new ClassPathXmlApplicationContext("applicationcontext_RMIServer.xml");
}
}
applicationcontext_RMIClient.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myClient" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://localhost:1099/helloRMI" />
<property name="serviceInterface" value="com.zhb.rmi.spring.HelloRMIService" />
</bean>
</beans>
applicationcontext_RMIServer.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloRMIServiceImpl" class="com.zhb.rmi.spring.HelloRMIServiceImpl" />
<bean id="myHelloRMI" class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="service" ref="helloRMIServiceImpl" />
<property name="serviceName" value="helloRMI" />
<property name="serviceInterface" value="com.zhb.rmi.spring.HelloRMIService" />
<!-- <property name="registryPort" value="9999" /> -->
</bean>
</beans>
服务定义
服务端服务使用RmiServiceExporter输出和绑定服务
RmiServiceExporter 实现InitializingBean接口
该对象初始化的时候,执行afterPropertiesSet方法。
该类有三个步骤:
1. 获取远程服务注册机registry
2. 获取远程服务对象,使用RmiInvocationWrapper封装远程对象代理
3. 导出远程对象
4. registry绑定远程服务。bind
客户端使用 RmiProxyFactoryBean 得到的是一个具体的服务代理对象。
继承RmiClientInterceptor
public Object invoke(MethodInvocation invocation) throws Throwable {
Remote stub = getStub();
try {
return doInvoke(invocation, stub);
}
catch (RemoteConnectFailureException ex) {
return handleRemoteConnectFailure(invocation, ex);
}
catch (RemoteException ex) {
if (isConnectFailure(ex)) {
return handleRemoteConnectFailure(invocation, ex);
}
else {
throw ex;
}
}
}
invoke方法
1. 获取stub
2. 调用stub方法
3. stub通过socket调用服务端真正的对象方法
protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler)
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
return "RMI invoker proxy for service URL [" + getServiceUrl() + "]";
}
return invocationHandler.invoke(createRemoteInvocation(methodInvocation));
}
invocationHandler.invoke(createRemoteInvocation(methodInvocation));
调用的是服务端注册服务时,获取被封装的远程对象的invoke方法。
底层使用的是socket链接服务端。
The major advantage of RMI, compared to Hessian and Burlap, is serialization.
Hessian/Burlap 是基于HTTP的,使用自己私有的对象序列化机制,能很好地穿越防火墙。
spring rmi java标准的序列化机制,不能穿越防火墙。
spring HttpInvoker
基于http,使用Java 对象序列号机制。
下面我们来看看HttpInvoker实现的流程
服务端实现
服务端核心类 HttpInvokerServiceExporter
afterPropertiesSet方法
得到服务对象的代理对象
handleRequest方法
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
RemoteInvocation invocation = readRemoteInvocation(request);
RemoteInvocationResult result = invokeAndCreateResult(invocation, getProxy());
writeRemoteInvocationResult(request, response, result);
}
catch (ClassNotFoundException ex) {
throw new NestedServletException("Class not found during deserialization", ex);
}
}
- 解析客户端调用请求,读取序列化对象
- 执行调用
- 将结果的序列化对象写入输出流
客户端实现
核心类 HttpInvokerProxyFactoryBean
类似于RmiProxyFactoryBean,但是不同于invoke方法,该bean使用http请求获取服务。
发送http请求核心类 HttpComponentsHttpInvokerRequestExecutor
@Override
protected RemoteInvocationResult doExecuteRequest(
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
throws IOException, ClassNotFoundException {
HttpPost postMethod = createHttpPost(config);
setRequestBody(config, postMethod, baos);
try {
HttpResponse response = executeHttpPost(config, getHttpClient(), postMethod);
validateResponse(config, response);
InputStream responseBody = getResponseBody(config, response);
return readRemoteInvocationResult(responseBody, config.getCodebaseUrl());
}
finally {
postMethod.releaseConnection();
}
}