SpringBoot如何通过java-jar启动
⽬录
Pre
引导
新建⼯程打包启动
java -jar ⼲啥的
打包插件
spring-boot-maven-plugin简介
包结构
Archive的概念
JarFile
JarLauncher⼯作流程
⼩结
Pre
⼤家开发的基于Spring Boot 的应⽤,jar形式,发布的时候,绝⼤部分都是使⽤java -jar 启动。得益于Spring Boot 的封装,再也不⽤操⼼搭建tomcat等相关web容器le , ⼀切变得⾮常美好,那SpringBoot是怎么做到的呢?
引导
新建⼯程打包启动
我们新创建⼀个Spring Boot的⼯程
其中打包的配置为
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>springboot是啥
</plugins>
</build>
先打包⼀下
查看target⽬录
然后启动
java -jar ⼲啥的
我们先看看 java -jar ⼲了啥?
在oracle官⽹到了该命令的描述:
If the -jar option is specified, its argument is the name of the JAR file containing class and resource files for the application. The startup class must be indicated by the Main-Class manifest header in its source code.
使⽤-jar参数时,后⾯的参数是的jar 【spring-0.0.1-SNAPSHOT.jar】,该jar⽂件中包含的是class和资源⽂件;在manifest⽂件中有Main-Class的定义;Main-Class的源码中指定了整个应⽤的启动类;简单来说: java -jar会去jar中的manifest⽂件,去到Main-Class对应的真正的启动类;
那看看去吧
咦,这个Main-Class 是Spring Boot 的。
我们还看到有个Start Class
官⽅⽂档中,只提到过Main-Class ,并没有提到Start-Class;
Start-Class的值是com.artisan.spring.Application,这是我们的java代码中的唯⼀类,包含main⽅法,是能够真正的应⽤启动类
所以问题就来了:理论上看,执⾏java -jar命令时JarLauncher类会被执⾏,但实际上是com.artisan.spring.Application被执⾏了,这其中发⽣了什么呢?why?
打包插件
事实上,Java没有提供任何标准的⽅式来加载嵌套的jar⽂件(jar中包含jar ,即Spring Boot 中的fat jar)
Spring Boot 默认的打包插件如下:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
执⾏maven clean package之后,会⽣成两个⽂件,刚才我们也看到了
spring-boot-maven-plugin简介
spring-boot-maven-plugin项⽬存在于spring-boot-tools⽬录中。
spring-boot-maven-plugin默认有5个goals:repackage、run、start、stop、build-info。在打包的时候默认使⽤的是repackage。
spring-boot-maven-plugin的repackage能够将mvn package⽣成的软件包,再次打包为可执⾏的软件包,并将mvn package⽣成的软件包重命名为.original* spring-boot-maven-plugin的repackage在代码层⾯调⽤了RepackageMojo的execute⽅法,⽽在该⽅法中⼜调⽤了repackage⽅法。
private void repackage() throws MojoExecutionException {
// maven⽣成的jar,最终的命名将加上.original后缀
Artifact source = getSourceArtifact();
/
/ 最终为可执⾏jar,即fat jar
File target = getTargetFile();
// 获取重新打包器,将maven⽣成的jar重新打包成可执⾏jar
Repackager repackager = File());
// 查并过滤项⽬运⾏时依赖的jar
Set<Artifact> artifacts = filterDependencies(Artifacts(),
getFilters(getAdditionalFilters()));
// 将artifacts转换成libraries
Libraries libraries = new ArtifactsLibraries(artifacts, quiresUnpack,
getLog());
try {
/
/ 获得Spring Boot启动脚本
LaunchScript launchScript = getLaunchScript();
// 执⾏重新打包,⽣成fat jar
}catch (IOException ex) {
throw new Message(), ex);
}
// 将maven⽣成的jar更新成.original⽂件
updateArtifact(source, target, BackupFile());
}
执⾏以上命令之后,便⽣成了打包结果对应的两个⽂件。
包结构
下⾯针对⽂件的内容和结构进⾏⼀探究竟。
spring-0.0.1-SNAPSHOT.jar
├── META-INF
│└── maven(主要是pom⽂件)
│└── MANIFEST.MF
├── BOOT-INF
│├── classes
││└──应⽤程序类
│└── lib
│└──第三⽅依赖jar
└── org
└── springframework
└── boot
└── loader
└── springboot启动程序
META-INF内容
Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: spring
Implementation-Version: 0.0.1-SNAPSHOT
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: com.artisan.spring.Application
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.4.1
Created-By: Maven Jar Plugin 3.2.0
Main-Class: org.springframework.boot.loader.JarLauncher
Main-Class:org.springframework.boot.loader.JarLauncher ,这个是jar启动的Main函数Start-Class: com.artisan.spring.Application,这个是我们应⽤⾃⼰的Main函数
Archive的概念
在继续了解底层概念和原理之前,我们先来了解⼀下Archive的概念:
archive即归档⽂件,这个概念在linux下⽐较常见
通常就是⼀个tar/zip格式的压缩包
jar是zip格式
SpringBoot抽象了Archive的概念,⼀个Archive可以是jar(JarFileArchive),可以是⼀个⽂件⽬录(ExplodedArchive),可以抽象为统⼀访问资源的逻辑层
关于Spring Boot中Archive的源码如下:
public interface Archive extends Iterable<Archive.Entry> {
// 获取该归档的url
URL getUrl() throws MalformedURLException;
// 获取jar!/META-INF/MANIFEST.MF或[ArchiveDir]/META-INF/MANIFEST.MF
Manifest getManifest() throws IOException;
// 获取jar!/BOOT-INF/lib/*.jar或[ArchiveDir]/BOOT-INF/lib/*.jar
List<Archive> getNestedArchives(EntryFilter filter) throws IOException;
}
SpringBoot定义了⼀个接⼝⽤于描述资源,也就是org.springframework.boot.loader.archive.Archive。
该接⼝有两个实现,分别是
org.springframework.boot.loader.archive.ExplodedArchive
org.springframework.boot.loader.archive.JarFileArchive。
前者⽤于在⽂件夹⽬录下寻资源,后者⽤于在jar包环境下寻资源。⽽在SpringBoot打包的fatJar中,则是使⽤后者JarFileArchive
JarFile
JarFile:对jar包的封装,每个JarFileArchive都会对应⼀个JarFile。
JarFile被构造的时候会解析内部结构,去获取jar包⾥的各个⽂件或⽂件夹,这些⽂件或⽂件夹会被封装到Entry中,也存储在JarFileArchive中。如果Entry是个jar,会解析成JarFileArchive。
⽐如⼀个JarFileArchive对应的URL为:
jar:file:/Users/format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/
它对应的JarFile为:
/Users/format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar
这个JarFile有很多Entry,⽐如:
META-INF/
META-INF/MANIFEST.MF
spring/
spring/study/
....
spring/study/executablejar/ExecutableJarApplication.class
lib/spring-boot-starter-1.3.5.RELEASE.jar
lib/spring-boot-1.3.5.RELEASE.jar
...
JarFileArchive内部的⼀些依赖jar对应的URL(SpringBoot使⽤org.springframework.boot.loader.jar.Handler处理器来处理这些URL):
jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-starter-web-1.3.5.RELEASE.jar!/
jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-loader-1.3.5.RELEASE.jar!/org/springframework/boot/loader/JarLauncher.class
我们看到如果有jar包中包含jar,或者jar包中包含jar包⾥⾯的class⽂件,那么会使⽤ !/ 分隔开,这种⽅式只有org.springframework.boot.loader.jar.Handler能处理,它是SpringBoot内部扩展出来的⼀种URL协议。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论