一、引言
Java开发中我们使用最多的便是spring框架,比如springboot应用。微服务模式下,每个服务都是一个springboot应用,都会被打包成一个可执行jar包。那么我们有多少人尝试去了解过这个可执行jar到底是什么?它的结构是什么样的,我认为大部分人是没有关注过这个的,今天笔者就通过探索的方式,揭开可执行jar的真面目。
二、打包项目
首先使用IDEA随便创建一个spring web项目,打包一下看下target目录
classes
展开看到如下结构,主要是把我们自己写的.java文件编译成的.class文件和一些配置文件.yaml或者.properties,我们常说的classpath路径,就是这个classes文件下的路径。
generated-sources
这个一般是用来存放框架或者工具自动生成的一些源码或者文件的地方,比如注解Lombok等。
generated-test-sources
这个没什么好说的,用来存放生成一些测试代码源或者文件的地方
maven-archiver
aven-archiver是Maven内部使用的一个组件,它属于org.apache.maven.archiver包,主要职责在于帮助Maven插件(如maven-jar-plugin, maven-war-plugin等)创建和管理归档文件(例如JAR, WAR, EAR等)。它处理诸如设置归档的元数据(如Manifest文件内容)、收集和归档项目资源和编译后的类文件等工作。
maven-status
maven-status是Maven在构建过程中创建的一个临时目录,用于存储构建期间的中间状态信息。这个目录的内容主要是由Maven的maven-build-number-plugin或maven-checkstyle-plugin等插件生成的,特别是那些需要跟踪文件状态的插件。
surefire-reports
surefire-reports是Maven项目在执行单元测试时生成的测试报告目录
test-classes
这个就是测试类的生成目录
以上就是maven打包后的目录解析说明,接下来的这两个单独的文件是重点
三、可执行jar包
首先看这个.jar.original后缀的文件,
进入target目录下,用360解压缩解压下original文件解压到自己新建的original文件夹下
使用IDEA查看,看到文件结构如下
这个文件很小只有7kb,主要是应用的本地资源,并不包含第三方依赖。
再看.jar结尾的文件,这个就是我们打包好的可执行jar包,同样的解压到创建的jarExcutable文件中
在IDEA中查看结构如下
使用terminal控制台,输入tree
这个文件结构相对来说就比较复杂了,解释下关键部分
- BOOT-INF/classes:存放的是应用编译后的class文件
- BOOT-INF/lib:这个存放的是我们引用的所有第三方jar包依赖
- META-INF/:存放应用相关的元信息
- org/:存放的是springboot相关的class文件
这个和标准的java EE web应用很相似,在java EE web应用中class文件放在WEB-INF/classes下,依赖的jar包就放在WEB-INF/lib下,二者很相似,其实spring很多东西是在java EE web基础上发展而来。
java -jar yourapplicationName.jar命令可以启动我们的jar包,那么为什么能启动呢?打开这个MANIFEST.MF文件内容
内容如下
这个JarLauncher
就是可执行JAR文件启动器,是由以下插件spring-boot-maven-plugin
追加进去的,JarLauncher是专门装载引导类(启动类)的
pom文件中引入如下依赖(实际开发不需要引入,这里是为了看源码)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-loader</artifactId>
</dependency>
JarLauncher源码如下
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.loader.launch;
/**
* {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are
* included inside a {@code /BOOT-INF/lib} directory and that application classes are
* included inside a {@code /BOOT-INF/classes} directory.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @author Madhura Bhave
* @author Scott Frederick
* @since 3.2.0
*/
public class JarLauncher extends ExecutableArchiveLauncher {
public JarLauncher() throws Exception {
}
protected JarLauncher(Archive archive) throws Exception {
super(archive);
}
@Override
protected boolean isIncludedOnClassPath(Archive.Entry entry) {
return isLibraryFileOrClassesDirectory(entry);
}
@Override
protected String getEntryPathPrefix() {
return "BOOT-INF/";
}
static boolean isLibraryFileOrClassesDirectory(Archive.Entry entry) {
String name = entry.name();
if (entry.isDirectory()) {
return name.equals("BOOT-INF/classes/");
}
return name.startsWith("BOOT-INF/lib/");
}
public static void main(String[] args) throws Exception {
new JarLauncher().launch(args);
}
}
其中的BOOT-INF/lib/和BOOT-INF/classes/对应我们解压jar包后的相关文件目录,说明JarLauncher 会从中加载文件