Java的Jar结构分析

背景

Java的工程导出方式有多种,对于Java SE项目而言,基本的就是导出可执行jar和普通jar。可执行jar可以通过java -jar命令调用调用,普通jar可以作为第三方包被其他项目引用;而Java EE项目则是导出war包交给应用服务器使用。

Jar包用压缩软件解压后,里面都包含一个META-INF的目录,目录下只有一个MANIFEST.MF文件,描述了Java的相关信息。不同类型的Jar,该文件的内容是有很大差异的。

导出Jar

导出jar的方式很简单,IDE工具提供了export导出功能,Eclipse的export菜单点击后进入如下窗口:
这里写图片描述
分别是三种jar。普通的JAR file,很简单就是将项目类打成一个jar,其MANIFEST.MF文件只有一行信息,就是版本信息。

Manifest-Version: 1.0

依赖jar的引入方式

Runnable JAR file导出相对复杂一点,它必须指定包含main方法的主类,且需要选择项目依赖的jar的处理方式。选择导出Runnable Jar file后,点击next按钮。我们看到的是如下界面:
这里写图片描述
我们需要做三件事:
首先,选择Launch configuration,即指定运行主类,即Rsrc-Main-Class属性信息。
其次,指定导出jar的名称及目录。
第三,Library handling,选择解决依赖Jar的方式。

抽取依赖包

即Library handling的第一个选项,该方式会将工程依赖的jar包中的内容抽取成具体的文件夹,放在工程目录下。
这里写图片描述
工程依赖了log4j.jar,那么整个jar的目录会被抽取到目标jar中。结构如下:
这里写图片描述
MANIFEST.MF文件内容:
这里写图片描述
没有额外指定classpath信息,因为依赖jar都在同级目录中了。

直接加入jar

即Library handling的第二个选项,该方式会将工程依赖的jar,放在工程目录下。
这里写图片描述
此处的org目录是Eclipse自带的信息。
这里写图片描述
这点与第一种方式不同。其MANIFEST.MF文件中会指定classpath信息:
这里写图片描述
Rsrc-Class-Path就是依赖jar的路径及名称;Rsrc-Main-Class即启动类名称。

启示录

首先,MANIFEST.MF文件的结构还有一些特别的用途,比如指定Java Agent代理类。Tomcat的JMX的启动方式就是通过sum.management实现的。jdk的management-agent.jar中的MANIFEST.MF文件内容是这样的:

Manifest-Version: 1.0
Created-By: 1.6.0_18 (Sun Microsystems Inc.)
Agent-Class: sun.management.Agent
Premain-Class: sun.management.Agent

其次,Tomcat启动时,JVM在调用Bootstrap的main方法之前,先调用了Agent的类的startAgent方法,从而开启了JMX的Connector供JMX客户端访问。

第三,Agent-Class配置允许在Main方法启动后再执行代理方法,这就是为什么Tomcat即使不配置-Dcom.sun.management.jmxremote,只要jconsole.exe程序一启动,JVM就会立即调用Agent启动JMX的原因了。

最后,我们也可以在自定义的main方法启动之前执行Premain-Class的一些信息,推荐一篇博客,http://blog.csdn.net/catoop/article/details/51034778

这需要我们手动编辑agent.jar的MANIFEST.MF文件,如果Agent类中依赖了其他jar,那么需要把依赖的jar方法agent.jar同级目录中,同时指定Boot-Class-Path属性,添加上依赖的jar。

Manifest-Version: 1.0
Premain-Class: MyAgent
Can-Redefine-Classes: true
Boot-Class-Path: javassist.jar

Premain-Class还有一种用途就是实现热部署,这点在那篇博客中也有提及。 对Java的感觉还是不变的,Java语言太博大精深了,我还是涉水太浅呐!