SpringBoot可执行jar文件原理分析
Spring
官网对SpringBoot
的介绍中有这么一句话 Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".
。其大概意思是说SpringBoot
可以让我们更容易的创建一个独立的生产级别的Spring
应用,只需要运行它就可以了。Spring
官网阐述的SpringBoot
六大特征中的第一个就是Create stand-alone Spring applications
。
SpringBoot应用可执行JAR资源结构
有过SpringBoot
使用经验的人都知道,SpringBoot
应用可以打成一个jar
包,执行java -jar
命令即可运行。下面探究一下通过java -jar
命令启动SpringBoot
打包的jar
文件设计的内部细节和原理。
通过maven
打包后,在项目的target
目录下可以找到两个jar
文件:first-spring-boot-application-1.0.0-SNAPSHOT.jar和first-spring-boot-application-1.0.0-SNAPSHOT.jar.original。通过文件名和这两个jar文件大小可以分析出first-spring-boot-application-1.0.0-SNAPSHOT.jar.original为Maven打包的原始jar文件,而first-spring-boot-application-1.0.0-SNAPSHOT.jar为引入了相关第三方依赖资源后的jar
包。
解压这两个jar
文件,分析其内部的文件结构
$ unzip first-spring-boot-application-1.0.0-SNAPSHOT.jar -d temp
$ tree temp/
temp/
├── BOOT-INF
│ ├── classes
│ │ └── thinking
│ │ └── in
│ │ └── spring
│ │ └── boot
│ │ └── App.class
│ └── lib
│ ├── classmate-1.3.4.jar
│ ├── hibernate-validator-6.0.9.Final.jar
│ ├── .....
├── META-INF
│ ├── MANIFEST.MF
│ └── maven
│ └── thinking-in-spring-boot
│ └── first-spring-boot-application
│ ├── pom.properties
│ └── pom.xml
└── org
└── springframework
└── boot
└── loader
├── ExecutableArchiveLauncher.class
├── JarLauncher.class
├── ......
└── util
└── SystemPropertyUtils.class
19 directories, 90 files
$ unzip first-spring-boot-application-1.0.0-SNAPSHOT.jar.original -d original
$ tree original/
original/
├── META-INF
│ ├── MANIFEST.MF
│ └── maven
│ └── thinking-in-spring-boot
│ └── first-spring-boot-application
│ ├── pom.properties
│ └── pom.xml
└── thinking
└── in
└── spring
└── boot
└── App.class
8 directories, 4 files
发现first-spring-boot-application-1.0.0-SNAPSHOT.jar解压后的文件目录中:
- BOOT-INF/class目录存放应用编译后的class文件
- BOOT-INF/lib目录存放应用依赖的JAR包。
- META-INF/目录存放应用相关的元信息,如MANIFEST.MF文件;
- org/目录存放SpringBoot相关的class文件。
比较两者解压后的文件资源组成,除了META-INF
目录,其他资源均发生了变化,“repackage
”后的jar
,将应用class
文件和application.properties
文件从根目录放置到了BOOT-INF/classes
,所依赖的jar均存放到了BOOT-INF/lib
目录。
标准的Java EE Web
应用中,class
文件存放在WEB-INF/classes
目录,依赖的jar
包存放在WEB-INF/lib
目录。所以可以看出springboot
打包出来的jar
文件中BOOT-INF/class
和BOOT-INF/lib
是仿照Java web应用的目录格式的。那么java -jar
命令为何能执行jar
文件呢。
FAT JAR和WAR执行模块——spring-boot-loader
Java官方文档规定java -jar
命令引导的具体启动类必须配置在MANIFEST.MF
资源的Main-Class
属性中。同时JAR文件规范,MANIFEST.MF
资源必须存放在/META-INF/
目录下。
$ cat META-INF/MANIFEST.MF
Manifest-Version: 1.0
Implementation-Title: 《Spring Boot 编程思想》第一个 Spring B
oot 应用
Implementation-Version: 1.0.0-SNAPSHOT
Built-By: fengyajun
Implementation-Vendor-Id: thinking-in-spring-boot
Spring-Boot-Version: 2.0.2.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: thinking.in.spring.boot.App
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.6.0
Build-Jdk: 1.8.0_144
Implementation-URL: http://maven.apache.org
发现Main-Class
属性指向的class
为org.springframework.boot.loader.JarLauncher
,存放在org/springframework/boot/loader/目录下,而项目的引导类则定义在Start-Class
属性中。其中org.springframework.boot.loader.JarLauncher
类是org.springframe.boot:spring-boot-loader:2.0.2.RELEASE
包中的。因此运行 Java org.springframework.boot.loader.JarLauncher
即可启动该应用。
$ java org.springframework.boot.loader.JarLauncher
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.2.RELEASE)
.......
INFO 72527 --- [main] thinking.in.spring.boot.App : Started App in 5.406 seconds (JVM running for 6.148)