当前位置: 移动技术网 > IT编程>开发语言>Java > Spring Boot 的java -jar命令启动原理详解

Spring Boot 的java -jar命令启动原理详解

2020年03月09日  | 移动技术网IT编程  | 我要评论

导语

在运用spring boot 后,我们基本上摆脱之前项目每次上线的时候把项目打成war包。当然也不排除一些奇葩的规定,必须要用war包上线,不过很多时候,我们对一些东西只是处在使用的阶段,并不会去深入的研究使用的原理是什么,这貌似也是大多数人的固定思维。

或许正是如此,总会有些没有固定思维的人会去积极的探索原理,当然这话不是说我是积极的,我其实也是只原理的搬运工。今天和大家来简单的说下spring boot 的项目在运行java -jar的原理。

jar包目录和jar命令启动入口

在正式开始之前,我们先来看看把jar包进行解压。然后用tree /f命令查看目录结构(由于笔者写博文时用的是window,所以用的是tree /f命令),由于目录结构太长,这里做了相应省略,如下:

├─boot-inf
│ ├─classes
│ │ │ application.properties
│ │ │
│ │ └─com
│ │   └─spring
│ │     └─boot
│ │       └─test
│ │           springboottestapplication.class
│ │
│ └─lib
│     classmate-1.5.1.jar
│     hibernate-validator-6.0.18.final.jar
│     …………此处省略…………
│
├─meta-inf
│ │ manifest.mf
│ │
│ └─maven
│   └─com.spring.boot.test
│     └─spring-boot-test
│         pom.properties
│         pom.xml
│
└─org
  └─springframework
    └─boot
      └─loader
        │ 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
        │   bytes.class
        │   …………此处省略…………
        │
        └─util
            systempropertyutils.class

先简单说下上面目录结构,大体目录分三层:boot-inf、meta-inf、org,boot-inf是存放对应的应用服务的.class文件和maven依赖的jar包,包括启动类springboottestapplication,meta-inf下存放的是maven相关的pom信息和manifest.mf文件,org文件夹下存放的是spring boot loader模块编译的.class文件,也就是jar启动的关键代码所在。

在执行java -jar命令的时候,它的启动类配置实在jar包目录下meta-inf文件夹下的名manifest.mf文件中,在这个文件中有一个名为main-class的属性,我们来看下这个文件的具体内容:

manifest-version: 1.0
implementation-title: spring-boot-test
implementation-version: 0.0.1-snapshot
start-class: com.spring.boot.test.springboottestapplication
spring-boot-classes: boot-inf/classes/
spring-boot-lib: boot-inf/lib/
build-jdk-spec: 1.8
spring-boot-version: 2.2.3.release
created-by: maven archiver 3.4.0
main-class: org.springframework.boot.loader.jarlauncher

从上面的配置文件中,可以看到main-class属性指向的class为org.springframework.boot.loader.jarlauncher,而jarlauncher是jar的启动器,这个类是在org/springframework/boot/loader/,然后可以看到项目所定义的启动类是指向start-class这个属性的。

jar文件启动器——jarlauncher

在上面我们说了jarlauncher是jar可执行的启动器,那么它和项目的启动类springboottestapplication有什么关联呢?先给大家来个示例,先来到解压目录下执行命令:java org.springframework.boot.loader.jarlauncher ,然后便是如下界面:

c:\users\elisha\desktop\spring-boot-test-0.0.1-snapshot>java org.springframework.boot.loader.jarlauncher
 
 .  ____     _      __ _ _
 /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/ ___)| |_)| | | | | || (_| | ) ) ) )
 ' |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: spring boot ::    (v2.2.3.release)
2020-01-18 14:28:19.866 info 3644 --- [      main] c.s.boot.test.springboottestapplication : starting springboottestapplication on laptop-r2nni9cm with pid 3644 (c:\users\elisha\desktop\spring-boot-test-0.0.1-snapshot\boot-inf\classes started by elisha in c:\users\elisha\desktop\spring-boot-test-0.0.1-snapshot)

从上面的执行接口可以看到项目引导类springboottestapplication会被jarlauncher类进行引导,但是如果我们到boot-inf/class目录下,然后也执行java  com.spring.boot.test.springboottestapplication,会报springapplication的classnotfoundexception这个错误,由此可以得知这是因为java命令未指定class path。不过当前spring boot依赖的jar文件都是存放在boot-inf/lib下,而org.springframework.boot.loader.jarlauncher会将jar作为springboottestapplication类库的依赖,这也就是为什么jarlauncher能引导springboottestapplication,反之则是不可以的,那么对于springboottestapplication是jarlauncher的子进程,还是处于同一层级呢?接下来我们来看看jarlauncher的原理。

jarlauncher实现引导原理

因为org.springframework.boot.loader.jarlauncher的类是在spring-boot-loader中,但是若想在idea中来看源码,需要在pom文件中引入如下配置:

<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-loader</artifactid>
  <scope>provided</scope>
</dependency>

在引入上面的配置文件后,便可以在idea中查看源码了,使用ctrl+n命令来搜索jarlauncher类,那就来看下源码,如下:

public class jarlauncher extends executablearchivelauncher {
 
 static final string boot_inf_classes = "boot-inf/classes/";
 
 static final string boot_inf_lib = "boot-inf/lib/";
 
 public jarlauncher() {
 }
 
 protected jarlauncher(archive archive) {
 super(archive);
 }
 
 @override
 protected boolean isnestedarchive(archive.entry entry) {
 if (entry.isdirectory()) {
  return entry.getname().equals(boot_inf_classes);
 }
 return entry.getname().startswith(boot_inf_lib);
 }
 
 public static void main(string[] args) throws exception {
 new jarlauncher().launch(args);
 }
 
}

从上面的jarlauncher类中,可以看到两个常量:boot_inf_classes、boot_inf_lib,而它们又分别指向如下路径:boot-inf/classes/、boot-inf/lib/,并用isnestedarchive(archive.entry entry)方法进行判断(在spring boot中archive,抽象出了archive的概念,一个archive可以是一个jar(jarfilearchive)、也可以是一个目录(explodedarchive),在这里可以理解为spring  boot抽象出来的同一访问资源层。),从isnestedarchive方法的参数archive.entry对象貌似为一个jar文件中的资源,譬如application.properties,同时这个对象和jarentry是类似的,其name属性(archive.entry#getname())便是jar资源的相对路径。当application.properties资源在fat jar目录下时,其实archive.entry#getname()就是/boot-inf/classes/application.properties,此时便符合startswith方法的判断,所以isnestedarchive(archive.entry entry)便返回为true。当返回为false时,便说明fat jar被解压到文件目录了,由此也说明了spring boot应用可以通过java org.springframework.boot.loader.jarlauncher 命令启动的原因了。

archive.entry的实现

上面说了在spring boot中archive,抽象出了archive的概念,一个archive可以是一个jar(jarfilearchive)、也可以是一个目录(explodedarchive),这里所说的jarfilearchive、explodedarchive便是archive的两种是想方式,对于这两个类的实现代码感兴趣额同学可以自己去看看。

不过由此也说明了jarlauncher  既支持jar启动,又支持文件系统启动。同时jarlauncher 在作为引导类的时候,当执行java -jar 命令式,/meta-inf/ 下manifest.mf文件中的main-class属性将调用它的,main(string [])方法,其实它还是调用jarlauncher #launch(args)方法,这个方法是实现基类launcher,这里简单看下这个方法的实现:

protected void launch(string[] args) throws exception {
 jarfile.registerurlprotocolhandler();
 classloader classloader = createclassloader(getclasspatharchives());
 launch(args, getmainclass(), classloader);
}

总结

本篇文章简单的讲解了一下,java -jar命令的一个执行的原理,首先说了下jar包目录和jar命令启动入口,然后说了下jar文件启动器——jarlauncher和jarlauncher实现引导原理,最后说了下archive.entry的实现,这个实现的原理也是比较复杂,后面如果有机会,会再写篇文章来进行说明。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网