Java远程方法调用,即Java RMI(Java Remote Method Invocation),是java里一种实现远程过程调用的应用程序编程接口。java RMI极大地依赖于接口。在需要创建一个远程对象时,程序员通过传递一个接口来隐藏底层的实现细节。客户端得到的远程对象句柄正好与本地的桩代码连接,由后者负责透过网络通信。这样一来,程序员只需关心如何通过自己的接口句柄发送消息。
在spring中,同样提供了对RMI的支持,使得在spring下的RMI开发变得更方便。
/**
* 服务接口
*/
public interface HelloRMIService {
int getAdd(int a,int b);
}
/**
* 服务端接口实现
*/
@Component
public class HelloRMIServiceImpl implements HelloRMIService {
@Override
public int getAdd(int a, int b) {
return a+b;
}
}
@Configuration
public class RmiConfig {
/**
* 服务端发布注册
*/
@Bean
public RmiServiceExporter rmiServiceExporter(HelloRMIServiceImpl helloRMIService){
RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();
rmiServiceExporter.setService(helloRMIService);
rmiServiceExporter.setServiceName("helloRMI");
rmiServiceExporter.setRegistryPort(9999);
rmiServiceExporter.setServiceInterface(HelloRMIService.class);
return rmiServiceExporter;
}
/**
* 往spring环境注册一个客户端调用服务接口的实例
* 正常情况下,可以定义在远程客户端环境
*/
@Bean(name = {"myClient"})
public RmiProxyFactoryBean proxyFactoryBean(){
RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean();
rmiProxyFactoryBean.setServiceUrl("rmi://127.0.0.1:9999/helloRMI");
rmiProxyFactoryBean.setServiceInterface(HelloRMIService.class);
return rmiProxyFactoryBean;
}
}
@RestController
public class RmiController {
@Autowired
ApplicationContext applicationContext;
@GetMapping("/getAdd")
public int getAdd(){
HelloRMIService myClient = applicationContext.getBean("myClient", HelloRMIService.class);
return myClient.getAdd(1,2);
}
}
以上过程,客户端通过RMI进行了远程连接,连接到了服务端,使用服务端提供方法返回结果。接下来我们一起深入分析spring中对RMI功能的实现原理。
服务端实现:
我们知道RmiServiceExporter类是发布RMI的关键类。
服务端RMI服务激活调用:
由于之前bean初始化的时候做了服务名称绑定this.registry.bind(this.serviceName,this.exportedObject),其中exportedObject其实是被RMIInvocationWrapper进行封装过的,也就是说当其它服务器调用serviceName的RMI服务时,java会为我们封装其内部操作,而直接会将代码转向RMIInvocationWrapper的invoke方法中,进而在调用其增强RemoteInvocationTraceInterceptor的invoke方法。
客户端实现:
由上面配置可以看出,客户端的关键类是RmiProxyFactoryBean
可见,RmiProxyFactoryBean实现了几个关键接口,FactoryBean、InitializingBean以及Advice增强器
可以猜测客户端的实现应该都是在prepare()方法中
客户端调用:
还记得上面说过一句话,当其它服务器调用serviceName的RMI服务时,java会为我们封装其内部操作,而直接会将代码转向RMIInvocationWrapper的invoke方法中