Spring Boot 的java

网友投稿 215 2022-12-16


Spring Boot 的java

导语

在运用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文件中引入如下配置:

org.springframework.boot

spring-boot-loader

provided

在引入上面的配置文件后,便可以在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抽象出来的同一访问资源层。),从isNestedArhttp://chive方法的参数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被解压到文件目录了,由此也说明了SRNCwiWHWcpring 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的实现,这个实现的原理也是比较复杂,后面如果有机会,会再写篇文章来进行说明。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:如何通过java获取文件名和扩展名
下一篇:Java8 新特性之日期时间对象及一些其他特性
相关文章

 发表评论

暂时没有评论,来抢沙发吧~