背景
Java 的工程导出方式有多种,对于 Java SE 项目而言,基本的就是导出可执行jar和普通jar。可执行 jar 可以通过 java -jar 命令调用调用,普通 jar 可以作为第三方包被其他项目引用;而 Java EE 项目则是导出 war 包交给应用服务器使用。
Jar 包用压缩软件解压后,里面都包含一个 META-INF 的目录,目录下只有一个MANIFEST.M F文件,描述了 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 的一些信息。
这需要我们手动编辑 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 语言的一部分,仔细研究下还是蛮有意思的!!