1 JACOB
1.1 概述
jacob
(java com bridge
,java com桥
)分为两个部分,jacob.jar
,jacob.dll
,使用时两个东西的版本要一致,而且还分32位
和64位
,它的位数和jdk的位数有关,与操作系统的位数无关。它的原理是通过java
的jni
功能,调用系统组件dll
,通过这个com桥
来操作com组件
(windows的一种软件编程技术)
如果可以在 Java
中调用 COM
组件,就可以充分利用 Java 技能和现有的成熟 COM
工具包,大大简化应用开发的过程。
COM
组件提供了一种与其他语言的互操作方式,叫做自动化(Automation)。
现有的 Java COM
互操作的解决方案有很多种,由于设计目的的不同,在性能、易用性等方面都有很大的区别。本文介绍的 JACOB
开源项目,致力于简化 Java
操作 COM
组件,提供了一个虚拟机独立的自动化服务器实现,由于其通用性设计,您可以非常简单地完成对现有应用的集成。
1.2 JACOB 项目的由来
首先,我们将了解 JACOB
项目的基本情况,探讨在什么样的情况下选择它来完成任务调用 COM 中暴露出来的方法,主要有两种机制:早期绑定
和晚期绑定
。
- 早期绑定
早期绑定显式的声明对象、数据类型等,编译器获取了足够的信息在编译期进行链接和优化,这样通常可以获得更好的性能,通过这种机制实现Bridge
调用可以参考IBM
的RJCB
项目,它提供了一套高性能的解决方案。当然需要了解更多COM
组件的细节,虽然框架完成了大部分的生成Bridge
代码的工作,但是总体来说,编码工作量还是偏大,编码难度也比较高,而且RJCB
仅支持那些提供早期绑定的vtable
接口的COM API
- 晚期绑定
晚期绑定方式是通过IDispatch
接口来实现,类似Java
的反射机制,可以按照名称或者ID
进行方法调用,这种设计主要目的是支持脚本语言操作COM
,因为脚本是解释执行的,通常都不支持指针也就没有C++
中的vtable
机制,无法实现早期绑定。这种方式的缺点是在一定程度上性能较差,由于是在运行时按照名字或者ID
进行对象的调用,只有运行时才确切知道调用的是哪个对象,哪个方法,这样必然带来一定的开销和延迟。但是这种方式的优点也是非常明显的,简单、灵活,可以不必关注动态链接库的细节,可以非常快地完成代码开发工作。
JACOB
开源项目提供的是一个 JVM
独立的自动化服务器实现,其核心是基于 JNI
技术实现的 Variant
, Dispatch
等接口,设计参考了 Microsoft VJ++
内置的通用自动化服务器,但是 Microsoft
的实现仅仅支持自身的 JVM
。通过 JACOB
,可以方便地在 Java
语言中进行晚期绑定方式的调用。
下图是一个对 JACOB
结构的简单说明
可以综合考虑时间、技术难度、性能要求等各方面来选择适合您的解决方案,下面结合在实际的订单处理系统的部分功能的开发,展示使用 JACOB 技术进行开发的成本优势。
1.3 JACOB操作邮箱
1.3.1 使用背景
某开发团队要利用一个订单处理的工作流系统,对现有的业务方式进行集成、优化。原有的业务处理方式是基于邮件系统的半人工方式,业务人员将订单以邮件方式发送到特定邮箱,然后后续人员处理订单。
现在设计的集成方式是,处理引擎定期轮询订单邮箱,以特定命名规则区分订单,获取存储在邮件正文和命名属性中的订单信息,将订单放入处理队列进行进一步处理,最后将源邮件标记状态并移出订单目录。
开发工作中遇到的难点是,应用的核心部分是基于 Java
实现的工作流应用,而客户使用 Outlook/Exchange
邮件系统,开发团队并没有足够的人力和时间去为其开发纯 Java
实现的客户端,Java
程序也难以操作邮件命名属性的信息。
下面我们就尝试利用 JACOB
和一些成熟的 COM
组件来解决这个问题。
1.3.2 配置您的开发和运行环境
我们的例子是一个基于 Eclipse
的 Java
工程。首先,使用 JACOB
来搭桥
,下载最新的 JACOB
相关类库,包括Jacob.jar
和 Jacob.dll
,将jar
文件添加到Eclipse
工程的构建路径。
由于我们要通过
JNI
调用 DLL
文件,所以您需要把 DLL
文件添加到系统的 PATH
环境变量中,或者您也可以在程序中设置运行时的 PATH
变量。一个简单的
桥
已经搭起来了,就可以轻松的调用熟悉的 COM
类库了,我们这里以 Redemption
工具包为例来完成 Outlook
相关操作,它提供了对 Outlook
对象的比较完整的操作,可以方便的绕过 Outlook
对象模型所隐藏的一些细节部分。同时,Redemption
提供了对任何 IDispatch
友好的语言的支持。需要在操作系统中注册
COM
组件,可以使用 Redemption
工具包本身提供的安装程序,也可以采用 Windows
系统自带的 REGSVR32
工具来注册 DLL
文件
1.3.3 实现业务逻辑
完成上面的准备工作后,就开始实现具体的业务逻辑了。
首先,要获取邮件您需要登录到指定邮箱,Outlook profile
中存储了您的邮箱的基本信息,下面的例子是如何登录特定 profile:
public void logon(String profile, String password) {
// 登录到指定 Profile,并获取一个会话
ActiveXComponent session = new ActiveXComponent("Redemption.RDOSession");
Dispatch.call(session, "Logon", profile, password, false, true);
}
现在已经能够访问用户邮箱,我们要做的是获取收件箱中的标题以ORDER NO:
开始的邮件,把信息存储到订单管理系统之中
Dispatch items = Dispatch.call(folder, "Items").toDispatch();
int count = Dispatch.call(items, "Count").getInt();
// 轮询所有邮件,并检查符合标准的邮件
for (int x = 1; x <= count; x++) {
Dispatch item = Dispatch.call(items, "_Item", new Integer(x)).toDispatch();
ActiveXComponent sMail = new ActiveXComponent("Redemption.SafeMailItem");
sMail.setProperty("Item", item);
Variant vSubj = Dispatch.get(sMail, "Subject");
String subj = vSubj.getString();
if (subj.startWith(“ORDER NO”)){
// 将获取的定单信息存储到订单处理队列之中
OrderMail mail = new OrderMail(item);
mail.persistToOrderQueue();
}
}
获取订单之后,将订单标记为已处理,这里我们通过操作命名属性的方式来标记状态,然后移动邮件到放置已处理邮件的目录:
// 获取命名属性的 ID
ActiveXComponent utils = new ActiveXComponent("Redemption.MAPIUtils");
Variant namedPropID = Dispatch.call(utils, "GetIDsFromNames", orderMail,
GUID_OF_USERPROP, ORDER_STATE_DONE, true );
// 将邮件标记处理状态
ActiveXComponent utils = new ActiveXComponent("Redemption.MAPIUtils");
Dispatch.call(utils, "HrSetOneProp", orderMail, namedPropID, "Done", true );
// 将处理过的邮件移出现在的目录
Dispatch.call(orderMail, "Move", folderForProcessedMsg);
可以看到,无需关注太多类库的细节,只需要知道要使用的对象名称、方法和输入变量,就可以轻松的调用 COM
组件。JACOB
的准备工作非常简单,并且只需要非常有限的编码,就完成了现有业务系统和新应用的无缝集成。
1.4 JACOB操作邮箱
现在的项目中操作word
文件比较多,word
文件的加密解密,转换为各种格式,插入图片,添加水印、html
转word
等等各种东西,大家也都知道,java
语言是不能直接操作word
或者excel
的,不像C#
,可以调用VBA
的类来直接操作office
目前java
也有一些操作office
的开源框架,比如poi
,这个没怎么研究过,估摸着它是通过解析office
的xml
文档模型来操作office
的,因为office03
和office07
以及以上的文档模型结构不太一样,导致了poi
处理不同版本office
时要用不同的类,用的最多的是jacob
,它对office03
和office07
的操作基本一致,高版本的还没有试过
1.4.1 环境配置:
1、在工程中引入jar
包
2、将dll文件
放在jdk
的path
目录下面,通过System.getProperty("java.library.path")
;可以看到path路径
,或者直接放到jdk/bin、jre/bin、system32/system64
下面都放一份,总能找得到,版本要急着对应好。
3、附件为使用的组件,配合jdk使用。
1.4.2 常用类以及方法
常用类以及方法:
-
ComThread
:com组件管理,用来初始化com
线程,释放线程,所以会在操作office
之前使用,操作完成再使用。 -
ActiveXComponent
:创建office的一个应用,比如操作的是word
还是excel
-
Dispatch
:调度处理类,封装了一些操作来操作office
,里面所有的可操作对象基本都是这种类型,所以jacob
是一种链式操作模式,就像StringBuilder
对象,调用append()
方法之后返回的还是StringBuilder
对象 -
Variant
:封装参数数据类型,因为操作office
是的一些方法参数,可能是字符串类型,可能是数字类型,虽然都是1,但是不能通过,可以通过Variant
来进行转换通用的参数类型,new Variant(1),new Variant("1"),
Dispatch
的几种静态方法:(这些方法就是要用来操作office
的)
-
call( )
方法:调用COM
对象的方法,返回Variant
类型值。 -
invoke( )
方法:和call
方法作用相同,但是不返回值。 -
get( )
方法:获取COM
对象属性,返回variant
类型值。 -
put( )
方法:设置COM
对象属性。
以上方法中有的有很多重载方法,调用不同的方法时需要放置不同的参数,至于哪些参数代表什么意思,具体放什么值,就需要参考vba
代码了,仅靠jacob
是无法进行变成的。
Variant
对象的toDispatch()
方法:将以上方法返回的Variant
类型转换为Dispatch
,进行下一次链式操作。
1.4.3 初始化com线程
使用jacob
之前使用下面的语句来初始化com
线程,大概意思是打开冰箱门,准备放大象。。。
ComThread.InitSTA();
使用完成后使用下面的语句来关闭现场,大概意思是关上冰箱门。。。
ComThread.Release();
1.4.4 创建应用程序对象
创建应用程序对象,设置参数,得到文档集合
操作一个文档之前,我们必须要创建一个应用对应,比如是word
还是excel
,设置一些文档应用的参数,得到文档集合对象,(大家应该知道word
是Documents
,excel
是WorkBooks
)
ActiveXComponent wordApp = new ActiveXComponent("Word.Application");//word
ActiveXComponent wordApp = new ActiveXComponent("Excel.Application");//excel
wordApp.setProperty("Visible", new Variant(false));//设置应用操作是文档不在明面上显示,只在后台静默处理。
这个操作就是模仿vba
的写法,Application.Visible = False;
vba代码中的对application
的设置这里都可以用
Dispatch document = wordApp.getProperty("Documents").toDispatch();
1.4.5 打开文档
有了文档对象集合,我们就可以来操作文档了,链式操作就此开始:
call
方法,调用open
方法,传递一个参数,返回一个我们的word
文档对象,
Dispatch doc = Dispatch.call(document, "Open",new Variant("D:\\my.doc")).toDispatch();
1.4.6 保存文档
Dispatch.call(doc, "Save");
doc是我们打开的文档的对象
1.4.7 退出wordapplication
参数有很多个,我们一个都不传,执行完后winword
进程关闭
wordApp.invoke("Quit", new Variant[] {});
1.4.8 释放com线程
ComThread.Release();
网友评论