【0】README

1)本文文字描述转自 core java volume 2, 旨在学习 java分布式对象——远程方法中的参数和返回值+远程对象激活 的相关知识;

【1】远程方法中的参数和返回值

1) 在开始进行远程方法调用时,调用参数需要从客户端的虚拟机中移动到服务器的虚拟机中。
2) 对于从一个虚拟机向另一个虚拟机传值,我们将其区分成两种情况:传递远程对象和传递非远程对象。


【1.1】传递远程对象

1)当一个对远程对象的引用从一个虚拟机传递到另一个虚拟机时,该远程对象的发送者和接收者都将持有一个对同一个实体的引用。这个引用并非是一个内存位置(内存位置在单个虚拟机内才有意义),而是由网络地址和该远程对象的唯一标识符构成的。这个信息给封装在存根对象中。

  • 1.1) 需要始终牢记的是: 在远程引用上的方法调用明显比在本地引用上的方法调用执行得慢,并且潜在地也更不可靠。

【1.2】传递非远程对象

Conclusion)总结一下,在虚拟机之间传递值有两种机制:

  • c1)实现了Remote接口: 的类的对象将作为远程引用传递;
  • c2)实现了Serializable接口: 但是没有实现Remote接口的类的对象将使用序列化进行复制;

Attention)

  • A1)这两种机制都是自动化的,而且不需要任何程序员的干预。
  • A2)请记住,序列化对于大型对象来说会比较慢,而且远程方法不能改变被序列化的参数。
  • A3)当然,你可以通过传递远程引用来避免这些问题。但是这么做代价太大: 在远程引用上调用方法与调用本地方法相比,代价高得多。
  • A4)意识到这些开销有助于在设计远程服务时进行选择。

1)看个荔枝: 我们的下一个示例程序将展示远程和可序列化对象的传递。我们将Warehouse接口修改为代码11-5的样子,如果给定关键词列表,这个仓库将返回最佳匹配的Product。

public interface Warehouse extends Remote
{
double getPrice(String description) throws RemoteException;
Product getProduct(List keywords) throws RemoteException;
} // getProduct方法的参数具有List类型。因此,参数值必须属于实现了List接口的可序列化的类,例如ArrayList。

Attention) Product类是可序列化的,服务器构建了一个Product对象,而客户端获取了它的一个副本(参见图11-7)。


【1.3】动态类加载

1)应用背景:关键词列表发送到了服务器,而且仓库也返回了Product类的一个实例。当然,客户端程序在编译时需要Product.class类文件。但是,只要我们的服务器程序无法找到关键词的匹配,它就会返回一件肯定让每个人都很高兴的产品:《Java核心技术》这本书。这个对象是Book类的实例,而Book类是Product的子类。
2)problem+solution:

  • 2.1)problem: 当客户端编译时,它也许从未看到过Book类。但是在运行时,它需要能够执行覆盖了Product方法的Book方法,这说明客户端需要拥有在运行时加载额外类的能力。
  • 2.2)solution: 客户端使用了与RMI注册表相同的机制,即类由web服务器提供服务,RMI服务器类将URL传递给客户端,客户端创建要求下载这些类的HTTP请求。

3)problem+solution:

  • 3.1)problem: 只要一个程序从网络位置加载新的代码,就会涉及安全问题。
  • 3.2)solution:正是由于这个原因,我们需要在动态加载类的RMI应用中使用安全管理器。

4) 安装安全管理器:使用RMI的程序应该安装一个安全管理器,去控制动态加载类的行为。可以用下面的指令安装:

System.setSecurityManager(new SecurityManager());

5)problem+solution:

  • 5.1)problem: 默认情况下,SecurityManager会限制程序中所有的代码去建立网络连接,但是,我们的程序需要建立到三个远程位置的网络连接(connection):

    • c1)加载远程类的Web服务器;
    • c2) RMI注册表;
    • c3)远程对象;
  • 5.2)solution:为了允许这些操作,需要提供一个策略文件。

  • 5.3)看个荔枝: 下面的策略文件允许一个应用建立任何到端口号至少为1024的某个端口的网络连接。(RMI端口默认为1099,远程对象使用的端口也大于等于1024。我们使用8080端口来下载类。)

    grant
    {
    permission java.net.SocketPermission
    “*:1024-65535”, “connect”;
    };

6)需要通过将java.security.policy属性设置为这个策略文件名,来指示安全管理器去读取该策略文件。

  • 6.1)可以使用下面这个调用:

    System.setProperty(“java.security.policy”, “rmi.policy”);

  • 6.2)或者,可以在命令行指定系统属性设置:

    -Djava.security.policy=rmi.policy

7)为了运行示范程序,请确保已经关闭了在前面的示范程序中启动的RMI注册表、Web服务器和服务器程序。打开四个控制台窗口,然后执行下面的步骤:

  • 7.1) 编译接口、实现、客户端和服务器类的源文件。
    javac *.java
  • 7.2) 创建三个目录:client、server和download,然后按下面这样组装它们:

    client/
    WarehouseClient.class
    Warehouse.class
    Product.class
    client.policy
    server/
    Warehouse.class
    Product.class
    Book.class
    WarehouseImpl.class
    WarehouseServer.class
    server.policy
    download/
    Warehouse.class
    Product.class
    Book.class

  • 7.3) 在第一个控制台窗口中,转到download目录下,启动NanoHTTP

  • 7.4) 在第二个控制台窗口中,转到server目录下,启动服务器: java -Djava.rmi.server.codebase=http://localhost:8080/ WarehouseServer
  • 7.5) 在第三个控制台窗口中,转到client目录下,运行客户端:

8)看个荔枝:

  • 远程对象传送全部源代码参见:https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter11/rmi_transfer

【1.4】具有多重接口的远程引用(远程类实现多个接口)

1)可以使用instanceof操作符来查看一个特定的远程对象是否实现了某个接口。假设我们通过一个类型为Warehouse的变量得到了一个远程对象:

Warehouse location = product.getLocation();

2)这个远程对象可能是一个服务中心,但也可能不是。要确定到底是不是,可以使用下面的测试:

if (location instanceof ServiceCenter)

3)如果测试通过,就可以将location转型为ServiceCenter,并调用getReturnAuthorization方法。


【1.5】远程对象与 equals,hashCode 和 clone 方法

1) 插入集中的对象必须覆写equals方法。如果是散列集或散列映射表,则需定义hashCode方法。
2)不能在远程接口中定义equals方法 和 hashCode 方法:然而,比较远程对象时有一个问题。如果要比较两个远程对象是否具有相同的内容,调用equals则必须联系包含这些对象的服务器,然后比较它们的内容。而该调用可能会失败。但是,Object类中的equals方法并未声明会抛出RemoteException异常,而远程接口中的所有方法都必须抛出该异常。因为子类的方法不能比它覆写的父类的方法抛出更多的异常,所以不能在远程接口中定义equals方法。hashCode也是这样。
3)存根对象的equals 方法 和 hashCode 方法: 相反,存根对象的equals和hashCode方法只是查看远程对象的位置。只要它们指向相同的远程对象,equals方法就认为两个存根相同。
Conclusion)总之,可以使用集与散列表中的远程引用,但是必须记住,对它们进行等价测试以及散列计算并不会考虑远程对象的内容。你不能直接克隆远程引用。


【2】 远程对象激活(延迟构造远程对象)

1)激活机制: 激活机制(activation)允许延迟构造远程对象,仅当至少有一个客户端调用远程对象上的远程方法时,才真正去构造该远程对象。 (干货——激活机制定义)
2)引用激活机制的client 和 server:

  • 2.1)client:要享用激活机制的好处,客户端代码完全无需改动。客户端只是简单的请求一个远程引用,然后通过它进行调用而已。
  • 2.2)server:然而,服务器程序则需由一个激活程序来代替。该程序构造了对象的激活描述符(activation descriptors),这样的对象可以延迟构造,并且该程序通过命名服务为远程方法调用绑定了接收者。第一次对这样的对象进行方法调用时,激活描述符中的信息将会用来构造该对象。

3)应用激活机制的远程对象: 必须继承Activatable类而不是UnicastRemoteObject类,当然,还需实现一个或多个远程接口。例如,

class WarehouseImpl
extends Activatable
implements Warehouse
{

}

4)因为对象的构造是延迟进行的,所以它必须以标准方式实现。因此,构造器必须包含以下两个参数(params):

  • p1)一个激活ID(只需传递给父类的构造器)
  • p2)一个包含所有构造信息的对象,包装为MarshalledObject;

  • 4.1)在构建激活描述符时,需要像下面这样从构造信息中构造一个MarshalledObject:
    MarshalledObject param = new MarshalledObject(constructionInfo);

  • 4.2)在实现对象的构造器中,使用MarshalledObject类的get方法来获得反序列化之后的构造信息:
    T constructionInfo = param.get();

5)看个荔枝:为了演示激活,我们修改了WarehouseImpl类,使得构造信息是一个由描述和价格构成的映射表。这个信息被包装到了MarshalledObject中,并且在构造器中被拆包出来:

public WarehouseImpl(ActivationID id, MarshalledObject<Map<String, Double>> param)
throws RemoteException, ClassNotFoundException, IOException
{
super(id, 0);
prices = param.get();
System.out.println("Warehouse implementation constructed.");
}
  • 5.1)将0作为父类构造器的第二个参数: 代表RMI类库应该分配一个合适的端口号作为监听端口。构造器会打印一个信息,这样就可以看到所需的产品对象已经被激活了。
  • Attention) 其实远程对象不是一定要继承Activatable类,例如,可以将下面的静态方法调用放在服务器类的构造器中。

    Activatable.exportObject(this, id, 0)

6)如何构建激活程序

  • step1)需要定义一个激活组。一个激活组描述了启动远程对象所在的虚拟机所需的公共参数,其中最重要的参数是安全策略。
  • step2) 然后如下构造一个激活组描述符:

    Properties props = new Properties();
    props.put(“java.security.policy”, “/server/server.policy”);
    ActivationGroupDesc group = new ActivationGroupDesc(props, null);
    //第二个参数描述了一个特殊的命令选项,在这个例子中不需要任何选项,所以我们传递了一个null引用。

  • step3)创建一个组ID
    ActivationGroupID id = ActivationGroup.getSystem().registerGroup(group

  • step4) 构造一个激活描述符了。对于需要构造的每一个对象,都应该包括以下内容(contents):

    • c1)激活组ID,对象将在与其对应的虚拟机上被构造;
    • c2)类的名字(例如”ProductImpl”或”com.mycompany.MyClassImpl”);
    • c3) URL字符串,由该URL加载类文件。这应该是基本URL,不含包的路径;
    • c4)编组后的构造信息。

7)看个荔枝:

MarshalledObject param = new MarshalledObject(constructionInfo);
ActivationDesc desc = new ActivationDesc(id, “WarehouseImpl”,
“http://myserver.com/download/“, param);

  • 7.1)将此描述符传递给静态的Activatable.register方法。它返回一个对象,该对象实现了实现类的远程接口。可以使用命名服务绑定该对象:

    Warehouse centralWarehouse = (Warehouse) Activatable.register(desc);
    namingContext.bind(“rmi:central_warehouse”, centralWarehouse);

  • Attention) 与前例的服务器程序不同,激活程序在注册与绑定激活接收者之后就会退出。仅当远程方法调用第一次发生时,才会构造远程对象。

8)荔枝:

  • 8.1)启动远程对象激活程序,可按一下steps 进行:

    • step1) 编译所有文件;
    • step2)发布类文件:

      client/
              WarehouseClient.class
              Warehouse.class
      server/
               WarehouseActivator.class
               Warehouse.class
               WarehouseImpl.class
               server.policy
      download/
               Warehouse.class
               NanoHTTPD.java
      rmi/
               rmid.policy

    • step3)启动RMI注册表

    • step4)在rmi目录中启动 RMI激活守护程序

      rmid -J-Djava.security.policy=rmid.policy
      rmid程序监听激活请求,并且激活另一个虚拟机中的对象。要启动一个虚拟机,rmid 程序需要一定的权限。这些权限都在一个策略文件中说明了。使用-J, 可以传递一个选项给运行激活守护程序的虚拟机;

    • step5)在download 目录中启动NanoHTTPD Web 服务器;

    • step6) 从server目录运行激活程序
      java -Djava.rmi.server.codebase=http://localhost:8080/ WarehouseActivator

    • step7) 从client目录运行客户端程序;

Attention) 上述结果是可以正确查找远程对象,但是当调用Warehouse接口的实现类的getPrice 方法的时候,却抛出了异常,搞了一下午,我确实无能为力了,先留在这里,以后有机会再来解决。。

  • 相关命令行指令如下:
rmid -J-Djava.security.policy=rmid.policy
java com.corejava.chapter11.activation.download.NanoHTTPDMain
java -Djava.rmi.server.codebase=http://localhost:8080/ com.corejava.chapter11.activation.server.WarehouseActivator

  • for full source code, please visit https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter11/activation

java分布式对象——远程方法中的参数和返回值+远程对象激活相关推荐

  1. 以下是一个使用 VBA 的例子,演示了 ByVal 和 ByRef 的区别,以及如何在函数中传递参数和返回值。

    以下是一个使用 VBA 的例子,演示了 ByVal 和 ByRef 的区别,以及如何在函数中传递参数和返回值: Sub Example()Dim x As Integer, y As Integerx ...

  2. C++中函数模板的返回值是模板类型参数的调用方法

    1 函数模板 模板定义以关键字template开始,后接模板形参表,模板形参表是用尖括号扩住的一个或多个模板形参的列表,形参之间以逗号分隔.关于函数模板的详细介绍,请参考<C++中模板函数及模板 ...

  3. java方法传对象参数_Java方法中的参数太多,第2部分:参数对象

    java方法传对象参数 在上一篇文章中 ,我研究了与方法和构造函数的长参数列表相关的一些问题. 在那篇文章中,我讨论了用自定义类型替换基元和内置类型以提高可读性和类型安全性. 这种方法使方法或构造函数 ...

  4. Java方法中的参数太多,第2部分:参数对象

    在上一篇文章中 ,我研究了与方法和构造函数的长参数列表相关的一些问题. 在那篇文章中,我讨论了用自定义类型替换基元和内置类型以提高可读性和类型安全性. 这种方法使方法或构造函数的众多参数更具可读性,但 ...

  5. java命令行参数工具_Java方法中的参数太多,第8部分:工具

    java命令行参数工具 在我的系列文章的前七篇文章中,有关处理Java方法中期望的参数过多的内容集中在减少方法或构造函数期望的参数数量的替代方法上. 在本系列的第八篇文章中,我将介绍一些工具,这些工具 ...

  6. java中重载 参数顺序_Java方法中的参数太多,第4部分:重载

    java中重载 参数顺序 期望将过多的参数传递给Java方法的问题之一是,该方法的客户端很难确定它们是否以适当的顺序传递了适当的值. 在以前的文章中,我描述了如何使用自定义类型 , 参数对象和构建器来 ...

  7. java自定义方法参数注解_Java方法中的参数太多,第1部分:自定义类型

    java自定义方法参数注解 我认为构造函数和方法中冗长的参数列表是Java开发中的另一个" 危险信号 ",就逻辑和功能而言,它们不一定是"错误的",但通常暗示当 ...

  8. java 可变参数方法_Java方法中的参数太多,第7部分:可变状态

    java 可变参数方法 在我的系列文章的第七篇中,有关解决Java方法或构造函数中过多参数的问题 ,我着眼于使用状态来减少传递参数的需要. 我等到本系列的第七篇文章来解决这个问题的原因之一是,它是我最 ...

  9. java中方法的命名_Java方法中的参数太多,第5部分:方法命名

    java中方法的命名 在上一篇文章 (有关处理Java方法中过多参数的系列文章的 第4部分 )中,我将方法重载视为一种向客户提供需要较少参数的版本的方法或构造函数的方法. 我描述了该方法的一些缺点,并 ...

最新文章

  1. 关于通讯作者、第一作者的那点事,你想知道的全都在这里!
  2. Android 测量调用方法耗时的方法
  3. UML建模之时序图(Sequence Diagram)
  4. Java项目构建工具Gradle是否可以完全替代Maven?
  5. 史上最全阿里云服务器上Docker部署Springboot项目 实战 每一步都带详细图解!!!
  6. 深度学习与概率、统计的有趣探讨
  7. JavaFX鼠标移动事件
  8. 【CSS3】制作带光晕的网页“Button“
  9. 一张图剖析企业大数据平台的核心架构
  10. python如何获取表单数据_Python Django 获取表单数据的三种方式
  11. 上银驱动器使用手册_禾川伺服驱动器说明书
  12. creo怎么返回上一步_creo拔模怎么用?creo拔模操作技巧图文详解
  13. 2019 Python接口自动化测试框架实战开发(一)
  14. 原生app开发的优缺点
  15. Git解决pull无法操作成功
  16. 25个最适合摄影师的WordPress主题(2020)
  17. python秒表项目_第一个Python小项目——秒表
  18. Ubuntu安装Matlab其Simulink没有菜单栏的解决方案
  19. 【unity3d】如何学习unity3d
  20. 一文彻底理解:训练集,验证集,测试集,交叉验证

热门文章

  1. 【十二省联考2019】异或粽子【01Trie】【堆】【前k大套路】
  2. [BZOJ2616] SPOJ PERIODNI
  3. [WC2018]州区划分(FWT_OR卷积)
  4. scanf(“%s“)真的只开读入字符串大小就可以了吗??
  5. 动态规划:openjudge 2.6-3532 最大上升子序列和 解题心得
  6. bzoj2111,P2606-[ZJOI2010]排列计数【Lucas,组合计数,dp】
  7. jzoj3913-艰难的选择【差分,统计】
  8. 牛客练习赛 58——树链剖分
  9. 【二分】递增(luogu 3902)
  10. 初一模拟赛总结(3.30)