Java之jar打包

1、jar简介

Java归档文件格式(Java Archive, JAR)能够将多个源码、资源等文件打包到一个归档文件中。这样,有如下好处:

  • 安全性
    可以对整个jar包的内容进行签名。
  • 减少了下载时间
    如果applet被打包成一个jar文件,那么所有相关的资源就可以在一个HTTP transaction中下载完成,而无需为每一个文件新建一个连接。
  • 压缩
    减少了磁盘空间的占用。
  • 容易扩展
    通过jar这种格式,可以和容易地将自己的程序打包提供给别人使用。
  • 包密封(Package Sealing)
    存储在jar文件中的包可以被密封,来保证版本的一致性。密封可以保证一个包中的所有类都来自同一个jar文件。
  • 包版本说明
    一个jar包可以存储关于其内容的信息,包括提供商、版本等。
  • 可移植性
    处理jar文件的机制是Java平台核心API的标准模块。

From docs.oracle.com

2、jar的使用

JDK自带的打包工具通过jar命令来调用,jar是通过zip格式进行打包的。所以,这个jar工具也可以作为日常的压缩、解压缩工具来进行使用。

2.1 jar命令说明

如果安装了JDK并配置了环境变量,在命令行中输入jar命令,不加任何参数,就可以看到jar命令的使用说明,最下面还包含了两个例子:

jar
用法: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files ...
选项:
    -c  创建新档案
    -t  列出档案目录
    -x  从档案中提取指定的 (或所有) 文件
    -u  更新现有档案
    -v  在标准输出中生成详细输出
    -f  指定档案文件名
    -m  包含指定清单文件中的清单信息
    -n  创建新档案后执行 Pack200 规范化
    -e  为捆绑到可执行 jar 文件的独立应用程序指定应用程序入口点
    -0  仅存储; 不使用任何 ZIP 压缩
    -P  保留文件名中的前导 '/' (绝对路径) 和 ".." (父目录) 组件
    -M  不创建条目的清单文件
    -i  为指定的 jar 文件生成索引信息
    -C  更改为指定的目录并包含其中的文件(可以理解为首先cd到指定目录)
如果任何文件为目录, 则对其进行递归处理。
清单文件名, 档案文件名和入口点名称的指定顺序与 'm', 'f' 和 'e' 标记的指定顺序相同。

示例 1: 将两个类文件归档到一个名为 classes.jar 的档案中:
       jar cvf classes.jar Foo.class Bar.class
示例 2: 使用现有的清单文件 'mymanifest' 并将 foo/ 目录中的所有文件归档到 'classes.jar' 中:
       jar cvfm classes.jar mymanifest -C foo/ .

摘自<jar命令的帮助文档>

2.2 jar使用举例

jar命令打包时默认会在jar包中添加清单(manifest)文件,如果不想添加,手动指定-M选项:

>jar -cvf HelloWorld.jar HelloWorld.class   #将HelloWorld.class文件打入jar包
    已添加清单
    正在添加: HelloWorld.class(输入 = 427) (输出 = 289)(压缩了 32%)
>jar -tf HelloWorld.jar   #查看归档文件的内容
    META-INF/
    META-INF/MANIFEST.MF
    HelloWorld.class
>jar -xf HelloWorld.jar META-INF/MANIFEST.MF   #解压出其中的META-INF/MANIFEST.MF文件
>type META-INF\MANIFEST.MF   #查看清单文件的内容
    Manifest-Version: 1.0
    Created-By: 1.8.0_51 (Oracle Corporation)
>jar -cvfM HelloWorld.jar HelloWorld.class   #将HelloWorld.class文件打入jar包,不要添加清单文件
    正在添加: HelloWorld.class(输入 = 427) (输出 = 289)(压缩了 32%)
>jar -tf HelloWorld.jar
    HelloWorld.class
>jar -xf HelloWorld.jar   #解压tar文件到当前目录

如果要生成可以运行的jar包,需要指定jar包的应用程序入口点,用-e选项:

>jar -cvfe HelloWorld.jar HelloWorld HelloWorld.class   #创建可以运行的jar包
    已添加清单
    正在添加: HelloWorld.class(输入 = 427) (输出 = 289)(压缩了 32%)
>jar -tf HelloWorld.jar   #查看归档的内容
    META-INF/
    META-INF/MANIFEST.MF
    HelloWorld.class
>type META-INF\MANIFEST.MF   #查看清单文件的内容
    Manifest-Version: 1.0
    Created-By: 1.8.0_51 (Oracle Corporation)
    Main-Class: HelloWorld
>java -jar HelloWorld.jar   #运行jar包
    Hello World!!

查看jar包的文件列表:

jar vtf  fileName.jar

3、清单文件MANIFEST.MF

jar打包时,会默认向jar包中添加一个清单文件(MANIFEST.MF),放在META-INF目录。上面的例子中,可以看到:如果指定了jar包的入口程序,清单文件中就会多一行Main-Class: HelloWorld。实际上,-e选项的作用就是向MANIFEST.MF文件中添加这样一行信息,来声明jar包的入口程序。当然,我们也可以直接修改清单文件的内容。

3.1 修改清单文件的内容

前面已经说了,-m选项可以添加指定清单文件中的清单信息,格式如下:

jar cfm jar-file manifest-addition input-file(s)

注意:

  • manifest-addition是一个已经存在的文本文件,其中包含着想要往jar包的清单文件中添加的清单信息。
  • manifest-addition的文件编码格式必须是UTF-8。
  • 清单信息每行后面必须有回车或者换行。(The text file from which you are creating the manifest must end with a new line or carriage return. The last line will not be parsed properly if it does not end with a new line or carriage return.)
  • 清单文件名, 档案文件名和入口点名称的指定顺序与 'm', 'f' 和 'e' 标记的指定顺序相同。(前面已经提到了)

3.2 在清单文件中声明入口程序

>type manifest.txt   #查看清单信息的内容
    Main-Class: HelloWorld
    
>jar -cvfm HelloWorld.jar manifest.txt HelloWorld.class   #添加清单信息到jar包
    已添加清单
    正在添加: HelloWorld.class(输入 = 427) (输出 = 289)(压缩了 32%)
>jar -xvf HelloWorld.jar META-INF\MANIFEST.MF
      已解压: META-INF/MANIFEST.MF
>type META-INF\MANIFEST.MF
    Manifest-Version: 1.0
    Created-By: 1.8.0_51 (Oracle Corporation)
    Main-Class: HelloWorld   #可以看到清单信息成功添加
>java -jar HelloWorld.jar   #这样指定入口程序,生成的jar包照样可以运行。
    Hello World!!

3.3 在清单文件中指定CLASSPATH

在运行java命令的时候,如果指定了-jar选项,那么环境变量CLASSPATH和在命令行中指定的所有类路径都被JVM所忽略。这时,如果一个jar包引用了其它的jar包,解决方案有两个:

  1. java -cp lib\log4j-1.2.14.jar;hello.jar com.dhn.Hello (com.dhn.Hello为主类)
    在windows下多个jar之间以分号(;)隔开,最后还需要指定运行jar文件中的完整的主类名。

  2. java -jar hello.jar
    这种,需要修改hello.jar中的MANIFEST.MF,通过MANIFEST.MF中的Class-Path 来指定运行时需要用到的其他jar,其他jar可以是当前路径也可以是当前路径下的子目录。多个jar文件之间以空格隔开。
    以下面的MANIFEST.MF文件为例

     Manifest-Version: 1.0
     Main-Class: com.ibm.portalnews.entrance.Main
     Class-Path: lib\commons-collections-3.2.jar lib\commons-configuration-1.5.jar lib\commons-lang-2.3.jar lib\commons-logging.jar lib\dom4j-1.6.1.jar lib\jaxen-1.1-beta-7.jar lib\jdom.jar lib\log4j-1.2.14.jar
    

    其中:

    • Manifest-Version表示版本号,一般由IDE工具(如eclipse)自动生成
    • Main-Class 是jar文件的主类,程序的入口
      
    • Class-Path 指定需要的jar,多个jar必须要在一行上,多个jar之间以空格隔开,如果引用的jar在当前目录的子目录下,windows下使用\来分割,linux下用/分割
      
    • 文件的冒号后面必须要空一个空格,否则会出错
      
    • 文件的最后一行必须是一个回车换行符,否则也会出错
      

    From: java -jar classpath心得

3.4 将jar包A引用的其它jar包一同打包到A里面

这个问题,是我一直想的。如果这样,那么将一个程序到处拷贝的话就方便的多了。然而,这是行不通的,因为JDK的类加载器不会加载jar包内包含的其它jar包中的类。

当然,这样想过的人不止我一个,可以通过一些第三方的工具来完成。比如有个工具叫one-jar,感兴趣的可以看下。

3.5 通过清单文件密封包

JAR中的Package Sealing功能是在Java 1.2引入的。在生成Jar文件时我们可以指定是否将整个Jar或者其中某几个Package进行密封,如果是将Jar文件整个进行密封,那就意味着其内所有的Package都被密封了。Package一旦密封,那么Java 虚拟机一旦成功装载密封Package中的某个类后,其后所有装载的带有相同Package名的类必须来自同一个Jar文件,否则将触发Sealing Violation安全异常。

From Java中Package Sealing的探秘之旅

对包的密封方式主要有两种:一种是对jar文件中的某些包进行密封,另一种是密封jar中的所有包。

  • 对某些包密封。

      Manifest-Version: 1.0
      Created-By: 1.8.0_51 (Oracle Corporation)
    
      Name: com/test/hello/  
      Sealed: true
      Name: com/test/world/  
      Sealed: true  
    
  • 密封jar中的所有包。

      Manifest-Version: 1.0
      Created-By: 1.8.0_51 (Oracle Corporation)
      Sealed: true
    

概括来说,有两种情况触发关于Sealing的安全异常:

  1. 检查当前试图加载的类对应的Package是否已经被JVM装载且密封。如果已经被装载且密封了,但被密封的Package与当前加载的类对应的Package不是来自同一个Jar文件,将触发安全异常。
  2. 检查当前试图加载的类对应的 Package是否已经被JVM装载且密封。如果已经被装载但没有被密封,但当前试图加载的类对应的Package确要试图进行密封操作,将触发安全异常。JVM不允许对已经装载但未密封的Package再进行密封操作。

Package Sealing的好处:
Package Sealing所能带来的好处主要是版本一致性. 我们知道Java 在运行时是严格按照classpath中定义的顺序进行装载和检查,尤其是现在Java开源包满天飞, 很有可能你的Java应用程序或者中间件的classpath中会在不同的Jar文件中包含同一个Package的不同版本。这会使得程序运行产生不一致性结果,很难发现。

From: Java中Package Sealing的探秘之旅

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,748评论 6 342
  • 来可以 但不能赖着不走
    你怎么想的阅读 372评论 0 0
  • 接下来要写的文字属于2014年。 在微博上,跨年的钟声敲响,每个人都在说新年快乐,对节日无感的我,也被渲染得发了一...
  • 纳兰性德的这首《长相思山一程》,写尽千山万里情。 山一程,水一程,身向榆关那畔行,夜深千帐灯。 风一更,雪一更,聒...
    小主正红阅读 178评论 0 0