知识大全 Java和.NET互操作究竟有什么用

Posted

篇首语:读书也像开矿一样“沙里淘金”本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 Java和.NET互操作究竟有什么用相关的知识,希望对你有一定的参考价值。

Java和.NET互操作究竟有什么用?  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  并非所有的开发者都清楚 时下最流行的两个程序运行环境(Java虚拟机JVM和 NET通用语言运行时CLR)事实上就是一组共享的类库 不论是JVM还是CLR 都为程序代码的执行提供了各种所需的功能服务 这其中包括内存管理 线程管理 代码编译(或Java特有的即时编译JIT)等等 由于这些特性的存在 在一个操作系统中 如果程序同时运行在JVM和CLR两种环境之上 由于任何一个进程都可以加载与之对应的任何共享类库 这使得相应的操作将变得非常繁琐

  然而 当话题讨论到这些问题的时候 大多数开发者都会停下来 向一侧仰著头 非常认真的问道 可是……这样的互操作对我们来说究竟有什么用?

  近些年来 基于Java平台的程序开发 一直都有为数众多的API类库和新技术为其提供强大的支持 与此同时 NET的通用语言运行时CLR 天生就具备Windows操作系统所提供的那些丰富的编程支持 在Windows操作系统环境下 常有许多Windows编程中易于实现的功能目前却很难使用Java语言编程实现 然而有的时候 使用Java语言实现特定功能较之Windows编程却更为简洁 这是在Java编程中 使用Java本地接口JNI技术实现互操作时的通常看法 同时这对于Java的开发者来说也应当是非常熟悉 可能会让开发者感觉有所陌生的 是那些尝试在Java虚拟机中实现 NET编程语言特性的想法 例如在最新的 NET 中 包含工作流 WPF和InfoCard等广受关注的特性 或是在 NET过程中使用Java虚拟机提供的工具 比如说部署Java语言编写的那些包含复杂业务逻辑的Spring组件 或者实现通过ASP NET访问JMS消息队列这样的功能

  加载动态链接库以及与底层代码托管环境进行交互 是解决互操作问题所面临的两个不同问题 然而 每一项操作都为之提供了标准的应用程序接口来完成这样的功能 举例来说 下面列出的非托管C++代码来自于Java本地接口JNI的官方文档 目的是利用标准过程(相关的代码句柄在JNIHosting子目录里以InProcInterop方案的一部分存在 构建它的最好方法是在命令行里用指向JDK 目录位置的JAVA_HOME环境变量来操作 )创建基于Java虚拟机的函数调用

   #include stdafx h #include int _tmain(int argc _TCHAR* argv[])     JavaVM *jvm;       /* 表示一个Java虚拟机 */     JNIEnv *env;       /* 指向本地方法调用接口 */     JavaVMInitArgs vm_args; /* JDK或JRE 的虚拟机初始化参数 */     JavaVMOption options[ ]; int n = ;     options[n++] optionString = Djava class path= ;     vm_args version = JNI_VERSION_ _ ;     vm_args nOptions = n;     vm_args options = options;     vm_args ignoreUnrecognized = false;     /* 加载或初始化Java虚拟机 返回Java本地调用接口      * 指向变量 env */     JNI_CreateJavaVM(&jvm (void**)&env &vm_args); // 传入C++所需的参数     /* 使用Java本地接口调用 Main test 方法 */     jclass cls = env >FindClass( Main );     jmethodID mid = env >GetStaticMethodID(cls test (I)V );     env >CallStaticVoidMethod(cls mid );     /* 完成工作 */     jvm >DestroyJavaVM();     return ;

  在编译上述代码时 Java开发工具包JDK中的include和include\\win 目录将被添加在C++程序的include路径中 并且JDK中lib目录下的jvm lib必须位于目标代码连接器的路径之中 程序运行时 默认情况下程序的主类Main class作为程序执行的入口类 与上述文件位于相同的目录之中 并且保证Java运行环境JRE中的jvm dll动态链接库存在 一般来说这个动态链接库是存在于系统环境变量的PATH路径之中 (jvm dll通常不需要手动添加在PATH路径中 因为java exe将会动态的查找jvm dll动态链接库的位置 并在找到链接库后记录下它的位置 )

  同样 NET通用语言运行时CLR提供自有的应用程序调用接口 作为本地API接口来实现同样的功能 代码如下

   #include stdafx h #include int _tmain(int argc _TCHAR* argv[])ICLRRuntimeHost* pCLR = (ICLRRuntimeHost*) ;HRESULT hr = CorBindToRuntimeEx(NULL L wks STARTUP_CONCURRENT_GC CLSID_CLRRuntimeHost IID_ICLRRuntimeHost (PVOID*)&pCLR);if (FAILED(hr))return ;hr = pCLR >Start();if (FAILED(hr))return ;DWORD retval = ;hr = pCLR >ExecuteInDefaultAppDomain(L HelloWorld exe L Hello L Main NULL &retval);if (FAILED(hr))return ;hr = pCLR >Stop();if (FAILED(hr))return ;return (int)retval;

 如同Java本地接口JNI的示例一样 上面的示例假定应用程序HelloWorld exe在执行时与 NET编译(这儿 因为我们期望正用到的(ExecuteInDefaultAppDomain)这个特殊的宿主API能有一个调用它里面Hello的类 这个类要有一个名为Main的方法 以将一个字符串当作声明处理并返回整数值 注意这和传统的C#或者VB NET的入口通道有所不同 )都位于当前目录之下 由于 NET通用语言运行时CLR与操作系统具备更紧密的集成关系 所以CLR的动态链接库路径不需要手动设置在环境变量的PATH路径之中(关于CLR启动程序如何进行工作处理 详细内容请参考《CLI的共享源代码实现》一书

  当程序开发者使用非托管的C++代码编写应用成为可能 即可以加载CLR和JVM这两种不同的运行时环境来完成处理过程 这使得大部分业务逻辑的程序编写陷入开发者不敢去涉及的境地 然而吸引人的是 这可以作为锻炼编程技巧与能力的一种方式 对于我们大多数人 在这个过程中都会找到一系列的替代方案

  首先 比如说 CLR和JVM两种技术都支持非托管代码的 Calling Down 操作(在Java虚拟机中 被称作Java本地接口 而在 NET的CLR中 被称作P/Invoke调用) 这样的机制使得开发者可以在其中一个运行环境下定义功能方法 通过少量的 Trampoline(弹簧床) 编码 将程序迁移到另一个运行时环境下编译执行 例如 在Java程序中 通过本地方法接口JNI实现函数的调用操作较为繁琐 并且需要记录配置文档(比如 可以参见Liang或者Gordon的书 或者JDK中JNI的文档 ) 而在实现C++本地代码调用的过程中 较为繁琐的操作是使用微软Visual Studio 中提供的C++/CLI或Visual Studio 提供的C++托管代码 来进行代码编译的过程

  在这个步骤中 复杂之处在于程序运行时 需要确保Java虚拟机得到访问动态链接库的路径 这项工作可以分为两部分来完成 首先 当Java类函数的本地方法被程序加载时 需要询问Java虚拟机是否通过Runtime loadLibrary()操作来请求加载共享库函数 值得注意的是 本地类库请求是在没有指定文件拓展名的情况下完成这样的操作 不指定拓展名 是因为不同的操作系统往往使用不同的约定来共享类库 所以只需指定共享类库名称即可 比如在Windows操作系统下 共享类库具有 DLL后缀 然而在Unix或Linux操作系统之下 共享类库常用的约定是使用类似于libNAME so这样的名称 就这方面来讲 Java虚拟机首先需要在特定的操作系统中查询共享类库的约定惯例 在Windows操作系统之下 针对于加载类库的LoadLibrary()函数 官方文档中有明确的API接口说明 但所需的类库通常都包含在操作系统的安装目录中(在Windows操作系统中即为C:\\WINDOWS 和 C:\\WINDOWS\\SYSTEM 目录) 或是当前的工作目录 或者已经包含在环境变量PATH的设定之中 对于Java虚拟机的类库调用 也需要在其他两个目录中查找 即在由java library path系统参数指定的目录中 或是JRE运行环境所在目录的lib\\i 路径之下 通常来说 推荐使用的方法是在自定义属性java library path中指定本地代码执行参数(在Java虚拟机启动的时候 可以设置好系统参数的路径) 或者指定在JRE运行环境的i 目录中 在这个特定的例子中 很容易想象的到 指定Java虚拟机的系统参数常常是出乎开发者预期的事情(因为有时可能会有数目众多的应用服务需要设置) 所以有时动态链接函数库需要被Servlet容器或应用服务器复制到Java运行环境下的函数库Lib之中 当DLL动态链接库被应用程序发现时 事实上这种所谓 混合模式 的 NET动态链接方式(即同时管理托管和非托管的代码) 将会强制CLR通用语言运行时在进程启动时自动绑定 并且使得 NET通用语言运行时提供的全部功能 都集中体现在Java本地接口的动态链接库提供的操作之中

  值得一提的是 NET应用可以通过Trampoline(弹簧床)机制 调用Java程序代码 并使用非托管的动态链接库 然而 Java虚拟机不包含 NET所具有的那些Bootstrapping引导等神奇的机制(即 一次编写 到处运行 的特性) 在进程调用中 非托管的动态链接库需要正确的加载Java虚拟机 通过与先前一样的方式来使用相同的API程序调用接口 一旦Bootstrapping引导机制就位 使用Java本地接口的反射机制 就像API调用允许类库加载 对象创建和方法调用的过程一样 通过 NET CLR程序代码来访问非托管的动态链接库 实现起来仅是如何去调用P/Invoke接口的过程 并且接口调用过程具备详尽的文档说明

  如果所有这些工作 看起来需要占用很多的时间来完成 那一定会有人帮你想到更简洁的解决方法 幸运的是 已有相关的工具和技术让这个过程变得非常简单

  首先来看一款开源的工具包JACE() JACE可以简化JNI本地调用的互操作过程 其设计目的是使得编写符合JNI规范的代码变得轻松简单 特别是对于Java虚拟机的Bootstrapping引导机制方面 JACE的功能相对完善 并且JACE为非托管的C++代码提供支持 这样可能意味着我们仍然需要反过头来以Windows动态链接库的方式编写各种 不安全 的代码

  另外还有一个叫做IKVM的开源类库 现在已经成为Mono项目的一个部分 IKVM在JVM(现在(和可预见的未来)IKVM只会从CLR到JVM 不会反过来 )和CLR之间搭建了桥梁 为Java与 NET互操作提供了与其他已提到解决方案不同的实现途径 IKVM的实现并非是将Java字节码翻译成CIL代码 所以不需要将JVM加载到同一个进程之中 这包含一些有趣的含义 既然Java虚拟机没有被加载 在代码中就不需要考虑Java虚拟机所需的运行机制 即不需要Hotspot技术 不具备JMX监测程序(这意味着没有Java控制台来监测你的Java代码运行)等等 当然 既然所有的代码将转化为CIL语言 就可以利用 NET CLR通用语言运行时的所有益处 这些功能包括 CLR通用语言运行时的JIT即时编译技术 CLR性能监视器统计等功能 自从IKVM可以执行字节码翻译之后 这样的效果就对于CLR的开发者来说就变得相对透明

  然而 我们也可能真的需要加载Java虚拟机环境 并且代码的过程代理需要在程序中释放 就像Codemesh的JuggerNET工具(JuggerNET是Java C++代理工具的 NET版本 )生成的代码那样 它提供了两个功能 可以与 NET完善集成的Java本地接口调用API 使其可以更方便的使用 NET环境创建Java应用程序 并且提供 NET代码生成器产生 NET的代理程序 用来配置必须的参数并且执行Java对象中定义的函数方法 这样 使用JuggerNET在 NET应用中加载JVM程序的示例代码应该符合下面的过程

     /*  * Copyright by Codemesh Inc   ALL RIGHTS RESERVED   */using System;using Codemesh JuggerNET;//// 下面的代码设定JVM环境并且在程序中进行Java调用 //// 使用的Java虚拟机由平台依赖的业务逻辑决定 // 在这个例子中 也可以使用JvmPath属性来设置程序将要使用的JVM public class Applicationpublic static void Main( string[] argv )try// // 下面的代码提供了访问一个对象的途径 你可以使用这个对象来初始化运行时设置 //IJvmLoader loader = JvmLoader GetJvmLoader();// // 配置Java设置//// 设置classpath参数为当前的工作目录loader ClassPath = ;// 在classpath中添加CWD的父目录loader AppendToClassPath( );// 设置堆栈的最大值loader MaximumHeapSizeInMB = ;//  设置一组 D 选项loader DashDOption[ myprop ] = myvalue ;loader DashDOption[ prop_without_value ] = null;// 指定 TraceFile记录文件 如果不指定 所有的记录输出将会加入到 stderr标准错误之中loader TraceFile = \\\\trace log ;// // 你可以将这一项置空 在第一个代理操作执行时 或是可以精确加载Java虚拟机的时候 // 使用配置设置来去除程序对于JVM环境的需求 // 如果有错误发生 将会抛出一个异常 //loader Load();catch( System Exception )Console WriteLine( !!!!!!!!!!!!!!!  we caught an exception  !!!!!!!!!!!!!!!! );  Console WriteLine( ***************  we re leaving Main() **************** );  return;

   NET到Java代码生成的代理机制中 具备一定的编程技巧 因为存在一些手动设置来指定哪一个Java类和包应该被设为代理 实现这样的过程可以使用JuggerNET的GUI工具来指定描述包和类清单的模型文件 或者可以使用Ant脚本(这意味着一部分或全部的 NET程序发布需要使用Java的Ant工具来实现 对于互操作项目来说 这并非是完全不切合实际的) 通过使用

    /*  * Copyright by Codemesh Inc   ALL RIGHTS RESERVED   */using System;using Codemesh JuggerNET;using Java Lang;using Java Util;///

/// 使用 NET类型来定义数据成员 /// 通过拓展序列化的代理接口 我们自动为 NET类型产生被称为 peer 的参数 /// 序列化接口在代码生成器中进行标记 /// 并且使用Java同等的类型来保持 NET实例的序列化信息 /// public class MyDotNetClass : Java Io Serializable  public int  field = ;  public int  field = ;  public string strField = ;  public MyDotNetClass()      public MyDotNetClass( int f int f string s )     field = f ;   field = f ;   strField = s;    public override string ToString()     return MyDotNetClass[field = + field + field = + field + strField= + strField + ] ;  /// /// 另一个 NET的类型继承自Serializable /// 但是声明为不同类型的数据元素 /// public class MyDotNetClass : Java Io Serializable  public int[] test = new int[] ;  public MyDotNetClass ()      public MyDotNetClass ( int f int f )     test[ ] = f ;   test[ ] = f ;    public override string ToString()     System Text StringBuilder result = new System Text StringBuilder();    result Append( MyDotNetClass [test=[ );   for (int i = ; i < test Length; i++)       if( i != )     result Append( );    result Append( + test[i] );      result Append( ]] );   return result ToString();  /// /// 这个类型阐明了如何实现等同序列化的目标 /// 通过为 NET类型添加JavaPeer属性 /// 创建相似的用法来继承Java Io Serializable/// 但是有些不很方便的地方是 在需要使用 Serializable的时候 /// 在 PureDotNetType处不能使用生成的实例 /// JavaPeer属性列出了两个不同的属性 /// 分别是 PeerType 和 PeerMarshaller /// 第一个属性指定保持数据的Java类型 /// 第二个属性指定如何序列化 NET实例来生成Java实例及其逆过程 /// [JavaPeer(PeerType= demesh peer SerializablePeer            PeerMarshaller= Codemesh JuggerNET ReflectionPeerValueMarshaller )]public class PureDotNetType  private char ch = a ;     ///   /// 一个字段的设置来帮助我们阐明从Java中读出的实际信息   ///   public char CharProperty     set ch = value;     public override string ToString()     return PureDotNetType[ch= + ch + ] ;  /// /// 类型阐明了控制同等序列化细节的字段属性 /// [JavaPeer(PeerType= demesh peer SerializablePeer            PeerMarshaller= Codemesh JuggerNET ReflectionPeerValueMarshaller )]public class PureDotNetType   ///   /// 在去除编组之后的字段值将一直保持是 因为它的值没有被序列化或反序列化   ///   [NonSerialized]  public int    NotUsed = ;  ///   /// 在去除编组之后的字段值将一直保持是空值 因为它的值没有被序列化或反序列化   ///   [JavaPeer(Ignore=true)]  public string   AlsoNotUsed = null;  ///   /// 这个字段的值经过序列化或反序列化   /// 但是对于Java 这个字段是归类在 CustomFieldName 之下   /// 你可能通常不会关心Java的名称 但是如果Java程序可以访问peer对象   /// 并且需要访问自己的数据 则可以对其加以关注   ///   [JavaPeer(Name= CustomFieldName )]  public int    OnlyUsedField = ;  public override string ToString()     return PureDotNetType [NotUsed= + NotUsed +                 AlsoNotUsed= + ( AlsoNotUsed == null ? null : AlsoNotUsed ) +                 OnlyUsedField= + OnlyUsedField + ] ;  public class Peer  public static void Main( string[] args )     try       IJvmLoader loader = JvmLoader GetJvmLoader();    if( args Length > && args[ ] Equals( info ) )     ;//loader PrintLdLibraryPathAndExit();    // 生成哈希表的实例    Java Util Hashtable ht = new Java Util Hashtable();    // 创建一些纯 NET实例    object   obj = new MyDotNetClass();    object   obj = new MyDotNetClass ( );    PureDotNetType obj = new PureDotNetType();    PureDotNetType obj = new PureDotNetType ();    obj CharProperty = B ;    // 这两个值将在我们的哈希表中得到对象返回值后被消除    obj NotUsed = ;    obj AlsoNotUsed = test ;    // 这个值将会被保留 但是在Java代码中将会以另外一个名称出现    obj OnlyUsedField = ;    // 将 NET 实例放入Java哈希表     // 请注意这里没有可用的Java原始类型提供给 NET类型     // NET对象状态被拷贝到通用的Java实例之中     ht Put( obj obj );    ht Put( obj obj );    ht Put( obj obj );    ht Put( obj obj );    // 这是一个真实的测试!    // 现在我们尝试去得到最初的 NET信息     object o = ht Get( obj );    Console WriteLine( o = o ToString());    object o = ht Get( obj );    Console WriteLine( o = o ToString());    object o = ht Get( obj );    Console WriteLine( o = o ToString());    object o = ht Get( obj );    Console WriteLine( o = o ToString());    Console WriteLine( ht= ht ToString() );      catch( JuggerNETFrameworkException jnfe )       Console WriteLine( Exception caught: \\n \\n jnfe GetType() Name                                       jnfe Message jnfe StackTrace );    

  总的来说 在上述的程序互操作过程之中 在不考虑单一运行环境的速度优势情况下(在单一过程中的数据移动 远比网络传输中的数据移动速度更快 甚至高于快速比特) 程序互操作过程包含以下的一些优点

  集中化 在许多情况下 我们希望特定资源(比方说代码中的数据库序列标识符)只存在于一个且仅此一个进程之中 来避免复杂的进程间代码同步的实现

  可靠性 较少的硬件相关性 以及整个系统单一的硬件损耗 使得系统很少会有受到攻击的可能性

  结构化要求 在某些情况下 现有的结构化模型将要求所有程序处理过程替代已有的处理过程 比如说 应用程序的现有用户接口如果使用ASP NET编写 并且应用程序部分的互操作性 用以实现为EJB消息驱动Bean在JMS消息队列中的消息传送处理过程 则在本地程序中传送消息给Java服务 并且仅是释放消息到JMS队列之中 这样的过程就显得有些多余 特别是在假定JMS客户端代码非常简洁的时候 程序实现代价较高 将JMS的客户端代码放入ASP NET进程之中(Codemesh为JuggerNET代理实现JMS消息客户端提供了特别的版本) 来实现与现有程序架构保持一致的简洁途径

  此外 并非是所有的互操作解决方案都将通过in proc方法来实现 但其中一些会使用这样的方法 并且开发者无需害怕这样的想法 即便是提供这些操作的工具有着非常大的使用价值

关于作者

  Ted Neward是大规模企业应用系统方面的独立咨询人 也是Java NET和XML服务相关主题的会议上的演讲人 致力于Java与 NET的互操作技术 在Java与 NET方面 他曾撰写过几本广受认可的书籍 其中包括最近出版的《高效企业级Java开发》一书

资源

   The Java Native Interface (Liang)

   Java Native Interface (Gordon)

  The JNI page at the Java SE website ()

   Customizing the Common Language Runtime (Pratschner)

   Shared Source CLI (Stutz Neward Shilling)

cha138/Article/program/Java/JSP/201311/19187

相关参考

知识大全 用.net和协议快速开发下载软件

  在互连网广泛应用的今天将有用的信息下载到本地是一件很普遍的事当然这个过程我们可以通过许多下载工具实现目前的这些下载工具绝大多数都是使用协议完成的虽然使用编写下载工具是一种很简单的方法但美中不足的是

知识大全 使用Web服务和Ja.NET来实现互用性

使用Web服务和Ja.NET来实现互用性  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  简介  

知识大全 Java WS和C#调用

JavaWS和C#调用  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  互操作性是WebServi

知识大全 Linux下IPV4和IPV6的互操作性研究

Linux下IPV4和IPV6的互操作性研究  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  作为

知识大全 NetBeans 6.9增强了互操作性

NetBeans6.9增强了互操作性  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  NetBea

知识大全 是否能让JAVA和.NET框架共存?[3]

是否能让JAVA和.NET框架共存?[3]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  NET

知识大全 .Net和Java的socket机制比较

.Net和Java的socket机制比较  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  sock

知识大全 VB.NET中有用的通用对象列表

VB.NET中有用的通用对象列表  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  要使用List(

知识大全 是否能让JAVA和.NET框架共存?[1]

是否能让JAVA和.NET框架共存?[1]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  我个人

知识大全 是否能让JAVA和.NET框架共存?[2]

是否能让JAVA和.NET框架共存?[2]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  JAV