java本身提供了一种RPC框架——RMI(即Remote Method Invoke 远程方法调用),在编写一个接口需要作为远程调用时,都需要继承了Remote,Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口,只有在“远程接口”(扩展 java.rmi.Remote 的接口)中指定的这些方法才可远程使用,下面通过一个简单的示例,来讲解RMI原理以及开发流程:

  为了真正实现远程调用,首先创建服务端工程rmi-server,结构如下:

  

  代码说明:

  1.User.java:用于远程调用时pojo对象的传输,该对象必须实现Serializable接口,否则在调用过程中,会抛出NotSerializableException异常,代码如下:

/**
* 用户信息,用于远程调用传输,必须实现Serializable接口
*
* @author andy
*
*/
public class User implements Serializable {
private static final long serialVersionUID = 1L; private String name; private int age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "name : " + this.name + ", age : " + this.age;
}
}

  2.IHello.java:远程接口,该接口需要继承Remote接口,并且接口中的方法全都要抛出RemoteException异常,代码如下:

import java.rmi.Remote;
import java.rmi.RemoteException; import pers.andy.rmi.bean.User; /**
* 定义一个远程接口,必须继承Remote接口,其中需要远程调用的方法必须抛出RemoteException异常
*
* @author andy
*
*/
public interface IHello extends Remote { /**
* 更新user信息
* @param user
* @return
* @throws RemoteException
*/
public User updateUser(User user) throws RemoteException;
}

  3.HelloImpl:远程接口实现类,必须继承UnicastRemoteObject(继承RemoteServer->继承RemoteObject->实现Remote,Serializable),只有继承UnicastRemoteObject类,才表明其可以作为远程对象,被注册到注册表中供客户端远程调用(补充:客户端lookup找到的对象,只是该远程对象的Stub(存根对象),而服务端的对象有一个对应的骨架Skeleton(用于接收客户端stub的请求,以及调用真实的对象)对应,Stub是远程对象的客户端代理,Skeleton是远程对象的服务端代理,他们之间协作完成客户端与服务器之间的方法调用时的通信。),代码如下:

/**
* 远程的接口的实现,继承了UnicastRemoteObject,表明该类作为一个远程对象
*
* @author andy
*
*/
public class HelloImpl extends UnicastRemoteObject implements IHello {
/**
*
*/
private static final long serialVersionUID = 1L; /**
* 因为UnicastRemoteObject的构造方法抛出了RemoteException异常,因此这里默认的构造方法必须写,必须声明抛出RemoteException异常
*
* @throws RemoteException
*/
public HelloImpl() throws RemoteException {
} public User updateUser(User user) throws RemoteException {
System.out.println("-------------- 客户端发送的user为" + user.toString());
user.setName("andy2");
user.setAge(30);
return user;
}
}

  4.HelloServer:服务端启动类,用于创建远程对象注册表以及注册远程对象,代码如下:

/**
* 服务端启动类
*
* @author andy
*
*/
public class HelloServer {
public static void main(String args[]) {
try {
       // 本地主机上的远程对象注册表Registry的实例,并指定端口为8888,这一步必不可少(Java默认端口是1099)
LocateRegistry.createRegistry(8888);
// 把远程对象注册到RMI注册服务器上,并命名为RHello
// 绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的)
Naming.bind("rmi://localhost:8888/RHello", rhello);
// Naming.bind("//localhost:8888/RHello",rhello);
System.out.println("------------远程对象IHello注册成功,等待客户端调用...");
} catch (RemoteException e) {
System.out.println("创建远程对象发生异常!");
e.printStackTrace();
} catch (AlreadyBoundException e) {
System.out.println("发生重复绑定对象异常!");
e.printStackTrace();
} catch (MalformedURLException e) {
System.out.println("发生URL畸形异常!");
e.printStackTrace();
}
}
}

  补充说明:为何HelloImpl继承了UnicastRemoteObject就可以被作为远程对象发布,查阅UnicastRemoteObject的源码可以发现:

    protected UnicastRemoteObject() throws RemoteException
{
this(0);
}
protected UnicastRemoteObject(int port) throws RemoteException
{
this.port = port;
exportObject((Remote) this, port);
}

  其实在启动server端的时候,new了HelloImpl对象,因为继承了UnicastRemoteObject,会先调用父类的构造方法,这时候,就会将this(当前对象)通过exportObject方法注册。

  所以,如果在被导出的对象需要继承其它的类,那么就可以不采用集成UnicastRemoteObject的方式,而是通过exportObject方法将其导出为远程对象:

...
// 创建一个远程对象
IHello rhello = new HelloImpl();
//HelloImpl不需要继承UnicastRemoteObject类,通过exportObject将其显示导出
UnicastRemoteObject.exportObject(rhello,0);
...

  以上即是服务端所有代码,接下来是创建客户端工程,结构如下:

  

  实际应用开发中,客户端的User.java和IHello.java应该是从服务端导出jar包的形式添加到依赖库里,因此这边只介绍HelloClient.java,该类为客户端启动类,用于在注册表中查找远程对象实现远程方法调用,代码如下:

/**
* 客户端启动类
*
* @author andy
*
*/
public class HelloClient {
public static void main(String args[]) {
try {
// 在RMI服务注册表中查找名称为RHello的对象,并调用其上的方法
IHello rhello = (IHello) Naming.lookup("rmi://localhost:8888/RHello");
       // 构造user对象,测试远程对象传输
User user = new User();
user.setAge(20);
user.setName("andy");
System.out.println("-------------- 服务端返回的的user为" + rhello.updateUser(user).toString());
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}

  到此为止,客户端和服务端的工程都搭建完毕,现在可以进行测试,执行次序和测试结果如下所示:

  1.首先运行服务端启动类HelloServer,结果如下:

  服务端:------------远程对象IHello注册成功,等待客户端调用...

  2.运行客户端启动类,结果如下:

  服务端:-------------- 客户端发送的user为name : andy, age : 20

  客户端:-------------- 服务端返回的的user为name : andy2, age : 30

  

java RMI原理详解的更多相关文章

  1. Java CAS 原理详解

    1. 背景 在JDK 5之前Java语言是靠 synchronized 关键字保证同步的,这会导致有锁.锁机制存在以下问题: 在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问 ...

  2. 淘宝JAVA中间件Diamond详解(2)-原理介绍

    淘宝JAVA中间件Diamond详解(二)---原理介绍 大家好,通过第一篇的快速使用,大家已经对diamond有了一个基本的了解.本次为大家带来的是diamond核心原理的介绍,主要包括server ...

  3. 【转载】JAVA消息服务JMS规范及原理详解

    转载:https://www.cnblogs.com/molao-doing/articles/6557305.html 作者: moyun- 一.简介 JMS即Java消息服务(Java Messa ...

  4. JAVA消息服务JMS规范及原理详解

    JAVA消息服务JMS规范及原理详解 一.简介 JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应 ...

  5. java环境配置及原理详解

    java环境配置及原理详解 1.java跨平台的本质 我们谈到java,总是提到跨平台这个词.那么java语言是怎么实现跨平台的呢? 我们编写的java代码不是直接让windows系统读取解析,而是在 ...

  6. Java网络编程和NIO详解6:Linux epoll实现原理详解

    Java网络编程和NIO详解6:Linux epoll实现原理详解 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NIO h ...

  7. Java web Cookie详解(持久化+原理详解+共享问题+设置中文+发送多个Cookie)

    Java web Cookie详解 啥是cookie? 查询有道词典得: web和饼干有啥关系? 这个谜底等等来为大家揭晓 会话技术 web中的会话技术类似于生活中两个人聊天,不过web中的会话指的是 ...

  8. Java反序列化漏洞详解

      Java反序列化漏洞从爆出到现在快2个月了,已有白帽子实现了jenkins,weblogic,jboss等的代码执行利用工具.本文对于Java反序列化的漏洞简述后,并对于Java反序列化的Poc进 ...

  9. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

随机推荐

  1. ES版本控制

    版本控制 ElasticSearch采用了乐观锁来保证数据的一致性,也就是说,当用户对document进行操作时,并不需要对该doucument作加锁和解锁的操作,只需要指定要操作的版本即可.当版本号 ...

  2. AngularJS 过滤器 Filter

    过滤器实质是数据转换或过滤,把ViewMode中的数据转化成View层用户友好的信息.可以看做一个函数,负责接收输入,转换成输出,每次参数变化时,它就被执行,输出被视图View使用. 一.基本定义及其 ...

  3. Python自动化开发 - 常用模块(二)

    本节内容 1.shutil模块 2.shelve模块 3.xml处理模块 4.configparser模块 5.hashlib模块 6.subprocess模块 7.re模块 一.shutil模块 高 ...

  4. B - Big String

    We will construct an infinitely long string from two short strings: A = "^__^" (four chara ...

  5. 用fastreport在进行多列打印时,默认是先行后列,如何改成先列后行排记录?

    例子程序中的6.fr3是在Page中设置 columns=2这样就是先行后列,7.fr3就是3列先列后行的例子 1     6 2     7 3     8 4     9 5     10 但如果 ...

  6. 如何获取帮助———— QQ群讨论摘要

    QQ群对话整理(删除一些简单的回应),对一些重要的地方,我做了一些加粗   宝玉 2015/9/21 1:49:05       这次题目还有个问题就是如何读取Excel,我想对于很多同学来说是个困难 ...

  7. 【vue】http-server开启本地服务

    在写前端页面中,经常会在浏览器运行HTML页面,从本地文件夹中直接打开的一般都是file协议,当代码中存在http或https的链接时,HTML页面就无法正常打开,为了解决这种情况,需要在在本地开启一 ...

  8. Facebook内部报告:争取青少年用户的鸡贼小技巧

    翻译:吴祺深 欢迎访问网易云社区,了解更多网易技术产品运营经验. 去年十月,Facebook收购了TBH,最后却关闭了这款APP,不过一则内部报告透露了,通过这款流行的投票APP,这家公司学会了如何去 ...

  9. Code Chef April Cook-Off 2019题解

    传送门 \(PEWDSVTS\) 我哪根筋不对了要把所有可行的拿出来\(sort\)一下--还有忘开\(long\ long\)真的好难受-- int main(){ // freopen(" ...

  10. 我的AI之路 —— 从裸机搭建GPU版本的深度学习环境

    之前一直在CPU上跑深度学习,由于做的是NLP方向所以也能勉强忍受.最近在做图像的时候,实在是扛不住了...还好领导们的支持买个虚拟机先体验下.由于刚买的机器,环境都得自己摸索,瞎搞过很多次,也走过很 ...