RMI执行过程
RMI
是Java编程语言里一种用于实现远程过程调用的应用程序编程接口。它使客户机上的运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能地简化远程接口对象的使用,简而言之就是为了调用远程方法。https://blog.csdn.net/u012291108/article/details/52863915
这个过程中涉及到对象的传输,所以会用到序列化和反序列化,以及JNDI(远程方法调用协议)
参考文章:
rmi详解:https://blog.csdn.net/weixin_44627989/article/details/93055791
stub(存根)和skeleton(框架):https://www.cnblogs.com/yin-jingyu/archive/2012/06/14/2549361.html
现在我们的需求是这样的:client想执行一个在远程机器上server的一个方法,如果我们手动去编写这些过程,socket网络编程或许是我们必须要面对的,由此便引入了stub和skeleton模型。
所有和网络相关的代码全部都由这两个部分来实现,这样一来,客户端和服务端就都不需要去处理网络相关的代码,,这样的逻辑结构

简单来说就是这样的一个逻辑
Client<->Stub<->socket<->skeleton<->server
在远程服务开启的时候,stub也没有办法知道server的域名和端口,这个时候就要用到RMIRegistry,server上会创建一个stub对象,然后把他注册到RMIRegistry,这样Client就能从RMIRegistry获取到对象,RMIRegistry会在1099端口导出自己,不设置的话默认是1099端口
调试代码
server端接口
package test;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloService extends Remote {
public String sayHello() throws RemoteException;
//要发布的服务类的方法必须都throws RemoteException
//在Util中,创建代理对象时会checkMethod,存在没有throws RemoteException的则抛出IllegalArgumentException
}
server端实现代码
package test;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {
//需要继承UnicastRemoteObject,因为Remote只是接口,UnicastRemoteObject是Remote的实现类
//还要实现HelloService这个接口
protected HelloServiceImpl() throws RemoteException {
super();
}
@Override
public String sayHello() throws RemoteException{
return "hello";
}
}
server端将端口开放出去
package test;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
//服务器端
public class Server {
final static String host = "127.0.0.1";
final static int port = 8080;
public static void main(String[] args) throws RemoteException, MalformedURLException {
HelloService helloService = new HelloServiceImpl();
Registry reg=LocateRegistry.createRegistry(port);
reg.rebind("rmi://"+host+":"+port+"/hello", helloService);//不写port默认是1099
System.out.println("服务启动...");
}
}
package test;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class Client {
public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
HelloService helloService = (HelloService) Naming.lookup("rmi://127.0.0.1:8080/hello");
//默认端口1099
System.out.println(helloService.sayHello());
}
}
在HelloServoceImpl里面实现父类无参构造
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mRo5wyTc-1626770660728)(D:\typora\picture\image-20210630141440888.png)]](https://img-blog.csdnimg.cn/img_convert/78d974ae77be5c868828263eafd02799.png)
进入UnicastRemoteObject方法
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ty91YKS3-1626770660729)(D:\typora\picture\image-20210630141428214.png)]](https://img-blog.csdnimg.cn/img_convert/36eb8d3ec05cabe90572a60f77c838cc.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BRnsPcPH-1626770660730)(D:\typora\picture\image-20210630141549404.png)]](https://img-blog.csdnimg.cn/img_convert/8b41b87a9687586a3e79778d6dad99c2.png)
步入
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pPClwYaF-1626770660732)(D:\typora\picture\image-20210630145656142.png)]](https://img-blog.csdnimg.cn/img_convert/ca87dd8d02f6b7dbdab20c33631ffdb5.png)
步入UnicastServerRef()函数,其中会new一个UnicastServerRef对象,进入UnicastServerRef类,会发现其父类中包含一个LiveRef类型的属性
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sLbYqtjF-1626770660733)(D:\typora\picture\image-20210630145845070.png)]](https://img-blog.csdnimg.cn/img_convert/00ff83b8a16f74ac55f5e828696aa175.png)
步入这个liveRef函数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vYMGW447-1626770660733)(D:\typora\picture\image-20210630150044758.png)]](https://img-blog.csdnimg.cn/img_convert/4aca87330ac8cc7172148feddc132098.png)
TCPEndpoint对象就获得了Ip以及端口的属性
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J5z7dtKk-1626770660733)(D:\typora\picture\image-20210630150346546.png)]](https://img-blog.csdnimg.cn/img_convert/4ae0d0d249aa14664c5cf45ad9e1f4c9.png)
紧接着步入
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-REfjAOrD-1626770660734)(D:\typora\picture\image-20210630150558956.png)]](https://img-blog.csdnimg.cn/img_convert/f7dc782c962c6736e01b12efc6fbed8a.png)
这时会判断obj是否为UnicastRemoteObject类型,由于obj为自定义的HelloServiceImpl,继承了UnicastRemoteObject,因此if条件成立,接着步入sref对象的exportObject函数
可以看到会先获得对象的类属性,然后创建一个代理对象,继续查看源码,可以知道该代理对象类型为HelloServiceImpl,handler为RemoteObjectInvocationHandler,其中handler会包括上面创建的LiveRef对象(前面也知道了该对象中包含Endpoint等通信所需信息),因此可以判断在远程调用该对象时,客户端获取到的其实是该代理对象,再往下看,会生成一个Target对象,可以看到该Target对象包含了许多数据(导出输入的原始对象,创建的代理对象等)
后面这个target对象又被this.ref的exportObject方法导出![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DwcFcvNE-1626770660735)(D:\typora\picture\image-20210630150848233.png)]](https://img-blog.csdnimg.cn/img_convert/45e366fb44fe4c10f2e52b25221da246.png)
步入ref.exportObject方法,此时就是在TCPEndpoint这个类里面,在transport这个里面是有这些属性的,
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hy3RkaHH-1626770660736)(D:\typora\picture\image-20210630151131232.png)]](https://img-blog.csdnimg.cn/img_convert/09870a50790bfcfb41d6b06422ab3090.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5slPTHVO-1626770660736)(D:\typora\picture\image-20210630151451197.png)]](https://img-blog.csdnimg.cn/img_convert/18b17db724dbe82f022c01bedb14a598.png)
跟进这个exportObject()函数,可以看到这个Listen函数,就是建立监听
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2OvEDqYG-1626770660737)(D:\typora\picture\image-20210630164524158.png)]](https://img-blog.csdnimg.cn/img_convert/b73c2f24fb5ad168c9d59c369a769187.png)
接着跟进,在listen函数里面首先获得TCPEndpoint对象
然后获取到端口
后面就会开启一个socket,而且这个端口是随机的
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DE41uc6V-1626770660737)(D:\typora\picture\image-20210630165314334.png)]](https://img-blog.csdnimg.cn/img_convert/3068d493923e8169af6e039da2b78295.png)
UnicastRemoteObject基类的构造方法将远程对象发布到一个随机端口上,当然端口也可以指定
每一个对象都会有一个单独的socket,端口值随机
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1AFucT7t-1626770660737)(D:\typora\picture\image-20210630173018594.png)]](https://img-blog.csdnimg.cn/img_convert/5e1899ffb7a1748f5c4e8f64925cb393.png)
可以看到在执行完构造HelloServiceImpl的构造的时候,这个对象属性里面就有了ip和端口的信息
执行下一行代码
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NLs27r0t-1626770660738)(D:\typora\picture\image-20210630172252918.png)]](https://img-blog.csdnimg.cn/img_convert/218fe678a020c3baec2f58144c767ae4.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jb0qcXK4-1626770660738)(D:\typora\picture\image-20210630165729405.png)]](https://img-blog.csdnimg.cn/img_convert/2db3f5d9d4effe5934c2ee27cc168cdf.png)
步入
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-txfIVUtI-1626770660739)(D:\typora\picture\image-20210630170043972.png)]](https://img-blog.csdnimg.cn/img_convert/fb39b6c09a8e3ed0767eabb1c2b50ca8.png)
步入setup函数,这里会执行exportObject()方法
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YBLvmShq-1626770660740)(D:\typora\picture\image-20210630170242690.png)]](https://img-blog.csdnimg.cn/img_convert/97386a5078ea85d2496d668ee7ffce35.png)
跟进这个方法,这个方法就会返回stub存根,生成skeleton对象
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YHxjAx8G-1626770660740)(D:\typora\picture\image-20210630170350594.png)]](https://img-blog.csdnimg.cn/img_convert/dee9a80ced37371914d6bfc4bd77a017.png)
总结
Registry reg=LocateRegistry.createRegistry(port);
reg.rebind("rmi://"+host+":"+port+"/hello", helloService);
上面这两行代码就是建立Registry对象,然后将对象和请求放进这个hashTable中去,当我们请求
rmi://127.0.0.1:8080/hello
就会对应的访问到这个对象
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xumCZHuX-1626770660740)(D:\typora\picture\image-20210630173217457.png)]](https://img-blog.csdnimg.cn/img_convert/ced79a1f93a817381f7d4ed2c6e82808.png)
1、首先接口要继承Remote这个类,然后实现类要继承UnicastRemoteObject这个类,通过这个类的构造方法将远程对象发布到一个随机端口上
2、然后新建Registry对象,这个对象的作用有两个
①在指定端口设置外部访问
②相当于建立了一个注册表,通过键和值来进行访问指定对象的方法
这就是整体建立客户端的一个逻辑
客户端执行流程跟踪
客户端的这行代码返回的是RegistryImpl_Stub,也就是一个存根
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DnINQFmX-1626770660741)(D:\typora\picture\image-20210701110611242.png)]](https://img-blog.csdnimg.cn/img_convert/1b5ab0b840973fff638114e5518cda01.png)
传入域名和端口获取到服务端的RegistryImpl的代理

浙公网安备 33010602011771号