【前面的话】

最近过的好舒服,每天过的感觉很充实,一些生活和工作的技巧注意了就会发现,其实生活也是可以过的如此的有滋有味,满足现在的状况,并且感觉很幸福。

学习java RMI的原因是最近在使用dubbo框架做一个系统,所以对这java RMI进行学习,做一些笔记,基础性文章,选择性阅读。

【定义】

      Java RMIJava远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。

我们知道远程过程调用(Remote Procedure Call, RPC)可以用于一个进程调用另一个进程(很可能在另一个远程主机上)中的过程,从而提供了过程的分布能力。Java 的 RMI 则在 RPC 的基础上向前又迈进了一步,即提供分布式对象间的通讯。

RMI(Remote Method Invocation)为远程方法调用,是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。

这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。

JavaRMI

一、工作原理

RMI能让一个Java程序去调用网络中另一台计算机的Java对象的方法,那么调用的效果就像是在本机上调用一样。通俗的讲:A机器上面有一个class,通过远程调用,B机器调用这个class 中的方法。

RMI,远程方法调用(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分布式Java应用程序的方便途径。RMI是非常容易使用的,但是它非常的强大。

RMI的基础是接口,RMI构架基于一个重要的原理:定义接口和定义接口的具体实现是分开的。下面我们通过具体的例子,建立一个简单的远程计算服务和使用它的客户程序

二、RMI包含部分:

  1. 远程服务的接口定义
  2. 远程服务接口的具体实现
  3. 桩(Stub)和框架(Skeleton)文件
  4. 一个运行远程服务的服务器
  5. 一个RMI命名服务,它允许客户端去发现这个远程服务
  6. 类文件的提供者(一个HTTP或者FTP服务器)
  7. 一个需要这个远程服务的客户端程序

三、RMI的用途?

RMI的用途是为分布式Java应用程序之间的远程通信提供服务,提供分布式服务。

目前主要应用时封装在各个J2EE项目框架中,例如Spring,EJB(Spring和EJB均封装了RMI技术)

在Spring中实现RMI:

①在服务器端定义服务的接口,定义特定的类实现这些接口;

②在服务器端使用org.springframework.remoting.rmi.RmiServiceExporter类来注册服务;

③在客户端使用org.springframework.remoting.rmi.RmiProxyFactoryBean来实现远程服务的代理功能;

④在客户端定义访问与服务器端服务接口相同的类

四、RMI的局限?                                                                    

RMI目前使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信。JRMP是专为Java的远程对象制定的协议,由于JRMP是专为Java对象制定的,因此,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通信(意思是只支持客户端和服务器端都是Java程序的代码的远程调用)。

五、RMI的使用局限?

由于客户机和服务器都是使用Java编写的,二者平台兼容性的要求仅仅是双方都运行在版本兼容的Java虚拟机上。

六、RMI调用远程方法的参数和返回值

当调用远程对象上的方法时,客户机除了可以将原始类型的数据作为参数一外,还可以将对象作为参数来传递,与之相对应的是返回值,可以返回原始类型或对象,这些都是通过Java的对象序列化(serialization)技术来实现的。(换而言之:参数或者返回值如果是对象的话必须实现Serializable接口)

七、 RMI应用程序的基本模型

八、RMI体系结构

桩/框架(Stub/Skeleton)层:客户端的桩和服务器端的框架;

远程引用(remote reference)层:处理远程引用行为

传送层(transport):连接的建立和管理,以及远程对象的跟踪

九、 RMI类和接口(完成一个简单RMI需要用到的类)。

(一) Remote接口:是一个不定义方法的标记接口

Public interface Remote{}

在RMI中,远程接口声明了可以从远程Java虚拟机中调用的方法集。远程接口满足下列要求:

1、远程接口必须直接或间接扩展Java.rmi.Remote接口,且必须声明为public,除非客户端于远程接口在同一包中

2、在远程接口中的方法在声明时,除了要抛出与应用程序有关的一场之外,还必须包括RemoteException(或它的超类,IOExcepion或Exception)异常

3、在远程方法声明中,作为参数或返回值声明的远程对象必须声明为远程接口,而非该接口的实现类。

(二) RemoteObject抽象类实现了Remote接口和序列化Serializable接口,它和它的子类提供RMI服务器函数。

(三) LocateRegistry final()用于获得特定主机的引导远程对象注册服务器程序的引用(即创建stub),或者创建能在特定端口接收调用的远程对象注册服务程序。

服务器端:向其他客户机提供远程对象服务

SomeService servcie=……;//远程对象服务

  1. Registry registry=LocateRegisty.getRegistry();//Registry是个接口,他继承了Remote,此方法返回本地主机在默认注册表端口 1099 上对远程对象 Registry 的引用。
  2. getRegistry(int port) 返回本地主机在指定 port 上对远程对象 Registry 的引用;
  3. getRegistry(String host)  返回指定 host 在默认注册表端口 1099 上对远程对象 Registry 的引用;
  4. getRegistry(String host, int port) 返回指定的 hostport 上对远程对象 Registry 的引用
  5. registry.bind(“I serve”,service);// bind(String name,Remote obj) 绑定对此注册表中指定 name 的远程引用。name : 与该远程引用相关的名称 obj : 对远程对象(通常是一个 stub)的引用
  6. unbind(String name)移除注册表中指定name的绑定。
  7. rebind(String name,Remote obj)重新绑定,如果name已存在,但是Remote不一样则替换,如果Remote一样则丢弃现有的绑定
  8. lookup(String name) 返回注册表中绑定到指定 name 的远程引用,返回Remote
  9. String[] list()   返回在此注册表中绑定的名称的数组。该数组将包含一个此注册表中调用此方法时绑定的名称快照。

客户机端:向服务器提供相应的服务请求。

  1. Registry registry=LocateRegisty.getRegistry();
  1. SomeService servcie=(SomeService)registry.lookup(“I serve”);
  1. Servcie.requestService();

(四) Naming和Registry类类似。

客户端:

Naming.lookup(String url)

url 格式如下"rmi://localhost/"+远程对象引用
服务器端:
     Registry registry=LocateRegistry.createRegistry(int port);
     Naming.rebind(“service”,service);

(五) RMISecurityManager

在RMI引用程序中,如果没有设置安全管理器,则只能从本地类路径加载stub和类,这可以确保应用程序不受由远程方法调用所下载的代码侵害

在从远程主机下载代码之前必须执行以下代码来安装RMISecurityManager:

System.setSecurityManager(new RMISecurityManager());

十、demo开发

为了编写一个demo,我们分为两部分,一部分是server端的代码,一部分是client端的代码,client端的代码主要是为了使用server端的代码。当然这个代码是非常简单的,只是为了说明问题,现实的使用会使比较复杂的。

(一) 我们的目的

建立一个server端的java project,包含远程端的代码,定义接口,定义接口实现,然后在建立一个client端的java project,通过RMI使用远端服务中的方法。

(二) 我们的代码结构

(三) 远程服务代码

1. 远程服务的接口定义

第一步就是建立和编译服务接口的Java代码。这个接口定义了所有的提供远程服务的功能,下面是源程序:

UserManagerInterface.java

  1. package cn.com.tt.rmiserver.stub;
  2.  
  3. import java.rmi.Remote;
  4. import java.rmi.RemoteException;
  5.  
  6. import cn.com.tt.rmiserver.bean.Account;
  7.  
  8. public interface UserManagerInterface extends Remote{
  9. public String getUserName() throws RemoteException;
  10. public Account getAdminAccount() throws RemoteException;
  11. }

接口必须继承Remote类,每一个定义地方法都要抛出RemoteException异常对象。

2. 接口的具体实现

第二步就是对于上面的接口进行实现:

UserManagerImp.java

  1. package cn.com.tt.rmiserver;
  2.  
  3. import java.rmi.RemoteException;
  4.  
  5. import cn.com.tt.rmiserver.stub.UserManagerInterface;
  6. import cn.com.tt.rmiserver.bean.Account;
  7.  
  8. public class UserManagerImp implements UserManagerInterface {
  9. public UserManagerImp() throws RemoteException {
  10.  
  11. }
  12. private static final long serialVersionUID = -3111492742628447261L;
  13.  
  14. public String getUserName() throws RemoteException{
  15. return "TT";
  16. }
  17. public Account getAdminAccount() throws RemoteException{
  18. Account account=new Account();
  19. account.setUsername("TT");
  20. account.setPassword("123456");
  21. return account;
  22. }
  23. }

3. 定义一个bean,实现implements Serializable序列化接口。也就是可以在client和server端进行传输的可序列化对象。

Account.java

  1. package cn.com.tt.rmiserver.bean;
  2.  
  3. import java.io.Serializable;
  4.  
  5. public class Account implements Serializable,Cloneable{
  6. private static final long serialVersionUID = -1858518369668584532L;
  7. private String username;
  8. private String password;
  9.  
  10. public String getUsername() {
  11. return username;
  12. }
  13. public void setUsername(String username) {
  14. this.username = username;
  15. }
  16. public String getPassword() {
  17. return password;
  18. }
  19. public void setPassword(String password) {
  20. this.password = password;
  21. }
  22. }

4. 定义server端的主程序入口。

Entry.java

  1. package cn.com.tt.rmiserver.entry;
  2.  
  3. import java.rmi.AlreadyBoundException;
  4. import java.rmi.RemoteException;
  5. import java.rmi.registry.LocateRegistry;
  6. import java.rmi.registry.Registry;
  7. import java.rmi.server.UnicastRemoteObject;
  8.  
  9. import cn.com.tt.rmiserver.UserManagerImp;
  10. import cn.com.tt.rmiserver.stub.UserManagerInterface;
  11.  
  12. public class Entry {
  13. public static void main(String []args) throws AlreadyBoundException, RemoteException{
  14. UserManagerImp userManager=new UserManagerImp();
  15. UserManagerInterface userManagerI=(UserManagerInterface)UnicastRemoteObject.exportObject(userManager,0);
  16. // Bind the remote object's stub in the registry
  17. Registry registry = LocateRegistry.createRegistry(2002);
  18.  
  19. registry.rebind("userManager", userManagerI);
  20. System.out.println("server is ready");
  21. }
  22. }

(四) client端代码

  1. 把Server端的Account类和接口UserManagerInterface 导出Export成jar包,命名为:RmiServerInterface.jar。导入到client中。
  2. 项目——右键——Export——java——jar file——next——选择Account类和接口UserManagerInterface——命名为:RmiServerInterface.jar如下图:

3. 新建一个java Project,导入jar包,编写客户端代码。

4. 代码

ClientEntry.java

  1. package weiblog.rmi;
  2.  
  3. import java.rmi.NotBoundException;
  4. import java.rmi.RemoteException;
  5. import java.rmi.registry.LocateRegistry;
  6. import java.rmi.registry.Registry;
  7.  
  8. import cn.com.tt.rmiserver.stub.UserManagerInterface;
  9.  
  10. public class ClientEntry {
  11.  
  12. public static void main(String []args){
  13.  
  14. try {
  15. Registry registry = LocateRegistry.getRegistry("localhost",2004);
  16. UserManagerInterface userManager = (UserManagerInterface)registry.lookup("userManager");
  17. System.out.println("用户名是"+userManager.getAdminAccount().getUsername()
  18. +"密码"+userManager.getAdminAccount().getPassword());
  19. } catch (RemoteException e) {
  20. // TODO Auto-generated catch block
  21. e.printStackTrace();
  22. } catch (NotBoundException e) {
  23. // TODO Auto-generated catch block
  24. e.printStackTrace();
  25. }
  26.  
  27. }
  28.  
  29. }

5. 先运行服务器端代码, 然后运行客户端代码,就会显示运行结果,客户端可以运行多次,每次都可以取得服务器端的对象。如果要再次运行客户端代码就需要更改端口号,如果不更改就会显示端口号被占用。

【参考资料】

1. demo参考的下面文章:

RMI网络编程开发之二 如何搭建基于JDK1.5的分布式JAVA RMI 程序

2. 理论知识参考的下面文章:

RMI入门教程

java_RMI技术讲解

【后面的话】

快要放假了多么值得高兴。

时至今日都是我咎由自取,学java那是自找,与任何人无关。大学生活过的平顺,造就了我轻信java,编写众多bug的脾气,导致今日岌岌可危的地步,我今天愿意承担一切后果。其实,我很感激你们让我在测试的时候发现bug,而不是到了生产环境,我必须重新梳理我所造成的bug,坦然面对每个bug。我,在学java的路上编写了大量的bug。我辜负了c和c++,辜负了C#,辜负了程序员和码农的称呼,辜负了所有以为我可以写出漂亮代码的人。对不起,请接收我发自内心的歉意和忏悔。晚上和周末本来有一个温暖和美的生活,可是这一切被我学习java给打破了,我学java的行为不配得到原谅,我造成的无休止的bug现状也难以修改,但我还是想修改,我必须修改,这是我今日之后的生活。至于我自己,已咎由自取,愿日后不在产生bug。java程序员。——我真是越来越喜欢自黑了。哈哈,让我笑一会。

——TT

Java学习笔记(十六)——Java RMI的更多相关文章

  1. Java学习笔记十六:Java中的构造方法

    Java中的构造方法 1.使用new+构造方法 创建一个新的对象: 2.构造方法是定义在Java类中的一个用来初始化对象的方法: 3.构造方法与类同名且没有返回值: 4.语法格式: public 构造 ...

  2. Java学习笔记十五:Java中的成员变量和局部变量

    Java中的成员变量和局部变量 一:成员变量: 成员变量在类中定义,用来描述对象将要有什么 成员变量可以被本类的方法使用,也可以被其他类的方法使用,成员变量的作用域在整个类内部都是可见的 二:局部变量 ...

  3. Java 读书笔记 (十六) Java 继承

    例: 开发动物类,其中动物分别为企鹅以及老鼠,要求如下: 企鹅: 属性(姓名,id), 方法(吃,睡,自我介绍) 老鼠: 属性(姓名,id), 方法(吃,睡,自我介绍) 企鹅类: public cla ...

  4. Java学习笔记十九:Java中的访问控制修饰符

    Java中的访问控制修饰符 一:Java修饰符的种类: 访问修饰符 非访问修饰符 修饰符用来定义类.方法或者变量,通常放在语句的最前端.我们通过下面的例子来说明: public class Hello ...

  5. Java学习笔记十八:Java面向对象的三大特性之封装

    Java面向对象的三大特性之封装 一:面向对象的三大特性: 封装 继承 多态   二:封装的概念: 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访 ...

  6. Java学习笔记十二:Java中方法的重载

    Java中方法的重载 什么是方法的重载呢? 如果同一个类中包含了两个或两个以上方法名相同.方法参数的个数.顺序或类型不同的方法,则称为方法的重载,也可称该方法被重载了.如下所示 4 个方法名称都为 s ...

  7. 201671010140. 2016-2017-2 《Java程序设计》java学习第十六周

    java学习第十六周-并发        本周,学习了Java中线程,并发的知识,在老师的带领下,进行了对知识的理解学习,以及对实验的运行讲解,对这一块内容掌握的还可以,在自主编程中,也能够完成.线, ...

  8. python3.4学习笔记(十六) windows下面安装easy_install和pip教程

    python3.4学习笔记(十六) windows下面安装easy_install和pip教程 easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安 ...

  9. Java学习笔记心得——初识Java

    初识Java 拿到这本厚厚的<Java学习笔记>,翻开目录:Java平台概论.从JDK到TDE.认识对象.封装.继承与多态...看着这些似懂非懂的术语名词,心里怀着些好奇与担忧,就这样我开 ...

  10. Java 学习笔记 ------第六章 继承与多态

    本章学习目标: 了解继承的目的 了解继承与多态的关系 知道如何重新定义方法 认识java.lang.object 简介垃圾回收机制 一.继承 继承是java面向对象编程技术的一块基石,因为它允许创建分 ...

随机推荐

  1. MATLAB命令大全

    一.常用对象操作:除了一般windows窗口的常用功能键外.1.!dir 可以查看当前工作目录的文件. !dir& 可以在dos状态下查看.2.who 可以查看当前工作空间变量名, whos ...

  2. H5canvas赛车游戏-基于lufylegend引擎

    lufylegend引擎是canvas游戏中,比较简单的引擎之一,它不需要配置环境,类似引入jquery包的方式,引用对应js文件即可 lufylegend官方网站:http://www.lufyle ...

  3. elasticsearch 口水篇(5)es分布式集群初探

    es有很多特性,分布式.副本集.负载均衡.容灾等. 我们先搭建一个很简单的分布式集群(伪),在同一机器上配置三个es,配置分别如下: cluster.name: foxCluster node.nam ...

  4. PHP判断用户操作系统(Android,ipad,iphone,windows)

    这段脚本可以运用在:针对不同的操作系统,把用户引导向相应的网站或做相应的处理. <?php // PHP 判断客户端平台(PC.安卓.iPhone.平板) // strpos() 函数返回字符串 ...

  5. poj 1011

    http://poj.org/problem?id=1011 这是一道POJ的搜索的题目,最开始确实难以理解,但做过一些搜索的题目后,也没那么难了. 大概题意就是,现在有N根木头,要拼成若干根木头,并 ...

  6. liunux mysql MySQL表名不区分大小写的设置方法

    原来Linux下的MySQL默认是区分表名大小写的,通过如下设置,可以让MySQL不区分表名大小写:1.用root登录,修改 /etc/my.cnf:2.在[mysqld]节点下,加入一行: lowe ...

  7. hdu3709

    枚举+数位dp 注意处理数字为0和1的情况. #include <cstdio> #include <cstring> using namespace std; #define ...

  8. (转载)使用 udev 高效、动态地管理 Linux 设备文件

    概述: Linux 用户常常会很难鉴别同一类型的设备名,比如 eth0, eth1, sda, sdb 等等.通过观察这些设备的内核设备名称,用户通常能知道这些是什么类型的设备,但是不知道哪一个设备是 ...

  9. javascript 解析dom字符串

    知识要求:1:熟悉dom结构层次(如childNodes,nodeType,parent,children)等. 2:熟悉jq对象转换js 对象,反之 毕竟不是专业js人.借助第3方框架.其实jq也是 ...

  10. ACM/ICPC 之 拓扑排序+DFS(POJ1128(ZOJ1083)-POJ1270)

    两道经典的同类型拓扑排序+DFS问题,第二题较第一题简单,其中的难点在于字典序输出+建立单向无环图,另外理解题意是最难的难点,没有之一... POJ1128(ZOJ1083)-Frame Stacki ...