在 JAVA 语言这个圈子里面摸爬滚打,除了对于语言层面和框架层面的学习以外,有一些东西它一直存在,可是确没有对它们有足够的重视,由于都以为它是理所固然,好比 JAR 是个什么?html
提到 JAR,最早可能想到的就是依赖,好比 fastjson.jar ,它能够做为依赖在项目中来引用,可是不能经过 java -jar 来执行,这种就是非可执行的 JAR。另一种,好比咱们项目打包以后生成的 JAR (固然也多是 war),咱们能够经过 java -jar 来运行程序,咱们把它称之为可执行的 JAR。java
JAR 做用大致能够分为如下几种:git
JAR 文件是一种归档文件,以 ZIP 格式构建,以 .jar 为文件扩展名。用户可使用 JDK 自带的 jar 命令建立或提取 JAR 文件。也可使用其余 zip 压缩工具,不过压缩时 zip 文件头里的条目顺序很重要,由于 MANIFEST 文件常需放在首位。JAR 文件内的文件名是 Unicode 文本。github
JAR 文件(Java 归档,英语:Java Archive)是一种软件包文件格式,一般用于聚合大量的 Java 类文件、相关的元数据和资源(文本、图片等)文件到一个文件,以便分发 Java 平台应用软件或库。spring
以上来自维基百科express
JAR 文件格式提供了许多优点和功能,其中不少是传统的压缩格式如 ZIP 或者 TAR 所没有提供的。它们包括:json
这里分别给出两个 JAR 的解压以后的示例api
以 fastjson 为例:浏览器
.
├── META-INF
│ ├── LICENSE.txt
│ ├── MANIFEST.MF
│ ├── NOTICE.txt
│ ├── maven
│ │ └── com.alibaba
│ │ └── fastjson
│ │ ├── pom.properties
│ │ └── pom.xml
│ └── services
│ ├── javax.ws.rs.ext.MessageBodyReader
│ ├── javax.ws.rs.ext.MessageBodyWriter
│ ├── javax.ws.rs.ext.Providers
│ └── org.glassfish.jersey.internal.spi.AutoDiscoverable
└── com
└── alibaba
└── fastjson
├── JSON.class
├── JSONArray.class
├── JSONAware.class
├── JSONException.class
├── JSONObject.class
....省略
复制代码
这个 jar 是从 start.spring.io 上下载下来的一个最简单的 demo 打包来的安全
├── BOOT-INF
│ ├── classes
│ │ ├── application.properties
│ │ └── com
│ │ └── example # 应用的.class 文件目录
│ │ └── demo
│ │ └── DemoApplication.class
│ └── lib # 这里存放的是应用的 Maven 依赖的jar包文件
│ ├── javax.annotation-api-1.3.2.jar
│ ├── jul-to-slf4j-1.7.26.jar
│ ├── log4j-api-2.11.2.jar
│ ├── log4j-to-slf4j-2.11.2.jar
│ ├── logback-classic-1.2.3.jar
│ ├── logback-core-1.2.3.jar
│ ├── slf4j-api-1.7.26.jar
│ ├── snakeyaml-1.23.jar
│ ├── spring-aop-5.1.8.RELEASE.jar
│ ├── spring-beans-5.1.8.RELEASE.jar
│ ├── spring-boot-2.1.6.RELEASE.jar
│ ├── spring-boot-autoconfigure-2.1.6.RELEASE.jar
│ ├── spring-boot-starter-2.1.6.RELEASE.jar
│ ├── spring-boot-starter-logging-2.1.6.RELEASE.jar
│ ├── spring-context-5.1.8.RELEASE.jar
│ ├── spring-core-5.1.8.RELEASE.jar
│ ├── spring-expression-5.1.8.RELEASE.jar
│ └── spring-jcl-5.1.8.RELEASE.jar
├── META-INF
│ ├── MANIFEST.MF
│ └── maven
│ └── com.example
│ └── demo
│ ├── pom.properties
│ └── pom.xml
└── org
└── springframework
└── boot
└── loader #存放的是 Spring boot loader 的 class 文件
├── ExecutableArchiveLauncher.class
├── JarLauncher.class
├── LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class
├── LaunchedURLClassLoader.class
├── Launcher.class
├── MainMethodRunner.class
├── PropertiesLauncher$1.class
├── PropertiesLauncher$ArchiveEntryFilter.class
├── PropertiesLauncher$PrefixMatchingArchiveFilter.class
├── PropertiesLauncher.class
├── WarLauncher.class
├── archive
│ ├── Archive$Entry.class
│ ├── ...
├── data
│ ├── RandomAccessData.class
│ ├── ...
├── jar
│ ├── AsciiBytes.class
│ ├── ...
└── util
└── SystemPropertyUtils.class
复制代码
大多数 JAR 文件包含一个 META-INF 目录,它用于存储包和扩展的配置数据,如安全性和版本信息。Java 2 平台(标准版【J2SE】)识别并解释 META-INF 目录中的下述文件和目录,以便配置应用程序、扩展和类装载器:
能够执行的 JAR 与 普通的 JAR 最直接的区别就是可否经过 java -jar 来执行。
一个 可执行的 jar文件是一个自包含的 Java 应用程序,它存储在特别配置的 JAR 文件中,能够由 JVM 直接执行它而无需事先提取文件或者设置类路径。要运行存储在非可执行的 JAR 中的应用程序,必须将它加入到您的类路径中,并用名字调用应用程序的主类。可是使用可执行的 JAR 文件,咱们能够不用提取它或者知道主要入口点就能够运行一个应用程序。可执行 JAR 有助于方便发布和执行 Java 应用程序
一个可执行的 JAR 必须经过 menifest 文件的头引用它所须要的全部其余从属 JAR。若是使用了 -jar选项,那么环境变量 CLASSPATH 和在命令行中指定的全部类路径都被 JVM 所忽略。
当咱们用 JAR 命令打完包后,会在根目录下面建立 META-INF 目录,该目录下面会有一些对该 JAR 包信息的描述,其中确定会有一个 MANIFEST.MF 文件,该文件包含了该 JAR 包的版本、建立人和类搜索路径等信息。
FASTJSON jar 中的 MANIFEST.MF 文件
Manifest-Version: 1.0 # 用来定义manifest文件的版本
Archiver-Version: Plexus Archiver # 详见 http://codehaus-plexus.github.io/plexus-archiver/
Built-By: wenshao # 构建者
Created-By: Apache Maven 3.5.0 # # 声明该文件的生成者,通常该属性是由 jar 命令行工具生成的
Build-Jdk: 1.8.0_162 # 基于构建的 JDK 版本
复制代码
SpringBoot demo 的 MANIFEST.MF 文件
Manifest-Version: 1.0
Implementation-Title: demo # 定义了扩展实现的标题
Implementation-Version: 0.0.1-SNAPSHOT # 定义扩展实现的版本
Start-Class: com.example.demo.DemoApplication # 启动类
Spring-Boot-Classes: BOOT-INF/classes/ # 编译以后的 class 文件目录
Spring-Boot-Lib: BOOT-INF/lib/ # 当前工程依赖的 jar 包目录
Build-Jdk-Spec: 1.8 # 指定的 JDK 版本
Spring-Boot-Version: 2.1.6.RELEASE # SpringBoot 版本
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher # Main 函数
复制代码
在 Java 平台中, MANIFEST 文件是 JAR 归档中所包含的特殊文件,MANIFEST 文件被用来定义扩展或文件打包相关数据。
MANIFEST 文件做为一个元数据文件,它包含了不一样部分中的 k-v 对数据。
若是一个 JAR 文件被看成可执行文件,则其中的 MANIFEST 文件须要指出该程序的主类文件,如上面案例中的 SpringBoot demo 的那个 jar 中的MANIFEST 文件所示
从 MANIFEST 文件中提供的信息大概能够了解到其基本做用
JDK 中提供了能够获取 jar 包中 MANIFEST.MF 文件信息的工具,能够经过 java.util.jar 这个类库来获取。
JarFile jar = new JarFile(new File("/Users/glmapper/Documents/test/demo/target/demo-0.0.1-SNAPSHOT.jar"));
Manifest manifest = jar.getManifest();
Attributes mainAttributes = manifest.getMainAttributes();
for(Map.Entry<Object, Object> attrEntry : mainAttributes.entrySet()){
System.out.println("main\t"+attrEntry.getKey()+":"+attrEntry.getValue());
}
Map<String, Attributes> entries = manifest.getEntries();
for(Map.Entry<String, Attributes> entry : entries.entrySet()) {
Attributes values = entry.getValue();
for (Map.Entry<Object, Object> attrEntry : values.entrySet()) {
System.out.println(attrEntry.getKey() + ":" + attrEntry.getValue());
}
}
复制代码
执行结果为:
main Implementation-Title:demo
main Implementation-Version:0.0.1-SNAPSHOT
main Start-Class:com.example.demo.DemoApplication
main Spring-Boot-Classes:BOOT-INF/classes/
main Spring-Boot-Lib:BOOT-INF/lib/
main Build-Jdk-Spec:1.8
main Spring-Boot-Version:2.1.6.RELEASE
main Created-By:Maven Archiver 3.4.0
main Manifest-Version:1.0
main Main-Class:org.springframework.boot.loader.JarLauncher
复制代码
下面为 JarFile 的定义,从代码就能够看出,前面咱们所介绍的 Jar 是以 ZIP 格式构建一种归档文件,由于它是 ZipFile 的子类。
public class JarFile extends ZipFile {
private SoftReference<Manifest> manRef;
private JarEntry manEntry;
private JarVerifier jv;
private boolean jvInitialized;
private boolean verify;
//指示是否存在Class-Path属性(仅当hasCheckedSpecialAttributes为true时才有效)
private boolean hasClassPathAttribute;
// 若是清单检查特殊属性,则为 true
private volatile boolean hasCheckedSpecialAttributes;
// 在SharedSecrets中设置JavaUtilJarAccess
static {
SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
}
/** * The JAR manifest file name.(JAR清单文件名) */
public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
// 省略其余
}
复制代码
下面是 Manifest 类的定义,用来描述 JAR 的 清单文件。从其属性中也很好的观察到,其存储的就是 K-V 键值对数据。
public class Manifest implements Cloneable {
// manifest main attributes
private Attributes attr = new Attributes();
// manifest entries
private Map<String, Attributes> entries = new HashMap<>();
// 省略其余
}
复制代码
JAR 格式远远超出了一种压缩格式,它有许多能够改进效率、安全性和组织 Java 应用程序的功能。由于这些功能已经创建在核心平台 -- 包括编译器和类装载器 -- 中了,因此开发人员能够利用 JAR 文件格式的能力简化和改进开发和部署过程。
功能 | 命令 |
---|---|
用一个单独的文件建立一个 JAR 文件 | jar cf jar-file input-file... |
用一个目录建立一个 JAR 文件 | jar cf jar-file dir-name |
建立一个未压缩的 JAR 文件 | jar cf0 jar-file dir-name |
更新一个 JAR 文件 | jar uf jar-file input-file... |
查看一个 JAR 文件的内容 | jar tf jar-file |
提取一个 JAR 文件的内容 | jar xf jar-file |
从一个 JAR 文件中提取特定的文件 | jar xf jar-file archived-file... |
运行一个打包为可执行 JAR 文件的应用程序 | java -jar app.jar |