所有Java开发者都知道JAR文件只是包含Java类树的压缩(ZIP)文件。但是,不是人人(包括一些经验丰富的开发者)了解这种文件格式的其它优点。在本文中,我将简单介绍JAR文件格式,并说明利用它可以实现的各种可能性。
JAR文件简介
JAR文件以流行的二进制ZIP文件格式为基础,用以把许多文件合并成一个文件。它还包含一个名为META-INF的可选目录,这个目录位于文件根目录下。
有两种方法可以建立JAR文件:应用命令行工具jar,或使用Java中的java.util.jar API编程。如果JAR文件包含在Java类路径中,使JVM可看到它,则JAR文件包含Java类和/或可由类加载器运行、使用及加载的资源。
在许多情况下,JAR文件不仅仅是简单的Java类档案文件和/或资源;它们还可用来为应用程序和扩展建立语句块。META-INF目录(如存在)用于存储数据包和扩展配置数据,包括安全、版本、扩展和服务。
META-INF目录的作用
META-INF目录中的下列文件和目录获得Java 2平台的认可与解释,用来配置应用程序、扩展程序、类加载器和服务:
- MANIFEST.MF:清单文件,用来定义与扩展和数据包相关的数据。
- INDEX.LIST:这个文件由JAR工具的新“-i”选项生成,其中包含在一个应用程序或扩展中定义的数据包的地址信息。它是JarIndex的一部分,被类加载器用来加速类加载过程。
- x.SF:JAR文件的签名文件。x代表基础文件名。
- x.DSA:这个签名块文件与同名基础签名文件有关。此文件存储对应签名文件的数字签名。
- services/:这个目录存储所有服务提供程序配置文件。
下面我们来了解一下每种组合。
清单文件
清单文件由“AttributeName: Value”对构成,一个换行符将其划分成两个部分:主要部分和单独部分,这两个被另外一个换行符分开。
- 主要部分:这个部分包含JAR文件本身的安全和配置信息,以及此JAR文件构成的应用程序或扩展。它还定义适用于每个单独清单项的主要属性。这个部分没有名为“Name”的属性。本部分以一个空行结束。
- 单独部分:这个部分为此JAR文件中包含的数据包或文件定义各种属性。并非所有的JAR文件都必须列举在清单文件中,但必须列出所有签名文件。清单文件本身不得列出。每个部分必须以一个名为“Name”的属性开始,它的值必须是一个文件相对路径,或档案文件外的一个绝对URL参考数据。(我将在本文后部分讨论JAR签名。)
下面是清单文件最重要的属性:
- Manifest-Version:定义清单文件版本。它的值是一个上面规范描述的合法版本号。
- Created-By:详细说明生成这个清单文件的Java程序的版本和生产商。这个属性由JAR工具生成。
- Signature-Version:定义JAR文件的签名版本。该值应为一个有效的版本号字符串。
- Class-Path:这个属性值指定这个应用程序或扩展需要的扩展或库的相对URL。URL由一个或几个空格分隔。应用程序或扩展类加载器使用这个属性值建立它的内部搜索路径。
- Main-Class:这个属性用于单机应用程序。这个属性值定义主要应用程序类的相对路径,发射器在启动时会加载这些类。这个属性值不能在类名称后附加.class扩展名。如果你已经指定这个属性,JAR文件即变成可执行文件,应用程序将通过java –jar x.jar命令自动启动。
- Sealed:这个属性定义此JAR文件是否被密封。该值可为真或假,且忽略大小写。当JAR文件被密封时,一个可选的数据包可在某个特殊的版本中执行一贯性。在JAR中密封的数据包规定所有在那个数据包中定义的类必须源自相同的JAR;否则,就会产生一个SecurityException。例如,这段代码:
Name: javax/servlet/internal/
Sealed: true
说明javax.servlet.internal数据包被密封,且这个数据包中的所有类必须从相同的JAR文件加载。要了解可选数据包的详细内容,请查看扩展机制。
JAR签名文件
任何JAR文件都可以通过java.security API,使用命令行jarsigner工具或目录进行签名。通过文件签名,你可以保证没人能够改变一个文件的内容,以及你正在使用一个知名厂商的JAR文件。如果JAR文件用jarsigner工具签名,那么每个文件,包括META-INF目录中的相关非签名文件,都将被签名。下面是与签名有关的文件:
- META-INF/MANIFEST.MF
- META-INF/*.SF
- META-INF/*.DSA
- META-INF/*.RSA
- META-INF/SIG-*
每个签名器由一个扩展名为.SF的签名文件说明,这个文件的主要内容与清单文件相似。它由一个主要部分构成,其中包含由签名器提供的信息,但并不特别针对某个特定的JAR文件。主要部分的项目x-Digest-Manifest-Main-Attributes(这里的x是一个摘要算法)包含清单主要属性的摘要值。
要使一个文件生效,首先将签名文件的一个摘要值与根据清单文件中对应的项目计算的摘要进行比较;然后,再将清单文件中的一个摘要值与根据“Name:”属性中的实际引用数据计算的摘要比较。“Name:”属性指定一个相对文件路径或RUL。
例如,下面是一个签名JAR的清单文件:
Manifest-Version: 1.0
Created-By: 1.3 (Sun Microsystems, Inc)?br>
Name: common/class1.class
MD5-Digest: (base64 representation of MD5 digest)?br>
Name: common/class2.class
MD5-Digest: (base64 representation of MD5 digest)
SHA-Digest: (base64 representation of SHA digest)
下面是对应的签名:
Signature-Version: 1.0
MD5-Digest-Manifest-Main-Attributes: (base64 representation of MD5 digest)?br>
Name: common/class1.class
MD5-Digest: (base64 representation of MD5 digest)?br>
Name: common/class2.class
MD5-Digest: (base64 representation of MD5 digest)
数字签名是.SF签名文件的一个签名版本。这些是人类无法解译的二进制文件。数字签名文件和.SF文件的名称相同,但扩展名不同。数字签名的类型不同,其扩展名也各不相同,通常为RSA或DSA。
JAR目录
为优化类加载器在网络应用程序,特别是applet中的类搜索过程,因此引入了JAR目录(一般称作JarIndex机制)。JarIndex机制收集在一个applet中定义的所有JAR文件,并将信息存储在applet的类路径的第一个JAR文件的一个目录文件中。下载第一个JAR文件后,applet类加载器将收集内容信息,这样可提高JAR文件的下载效率。
系统对现有的JAR工具进行强化,使其能够检查一个JAR文件列表,并生成目录信息,说明哪些类和资源位于哪个JAR文件中。这些目录信息存储在根JAR文件META-INF目录中的一个名为INDEX.LIST的简单文本文件中。当类加载器加载根JAR文件时,它阅读INDEX.LIST文件,并用它建立一个由文件和数据包名称到JAR文件名列表的映射散列表。为了找到一个类或资源,类加载器查询散列表找到正确的JAR文件,如有必要,再下载这个文件。
提供服务
META-INF/services目录中的文件为服务提供程序配置文件。一个服务通常是一组著名的界面和(一般是抽象的)类。一个服务提供程序是一项服务的特殊执行过程。提供程序中的类往往执行上述界面并继承服务本身定义的类。
服务提供程序通过把一个提供程序配置文件放在META-INF/services源目录中来识别自己。这个文件名应由完全合格的抽象服务类名称构成。
应用这些特性
了解JAR文件的这些特性后,你就可以在实际开发工作中创造性的应用它们。