本文用的spring boot版本:
2.3.1.RELEASE

1、spring boot可执行jar的内容

1.1、怎么打包成可执行jar

spring boot提供了一个spring-boot-maven-plugin的插件,用于将spring boot程序打包成可执行的jar包(fat jar),在pom.xml里加入这个插件

<build>
     <plugins>
        <plugin>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

 

1.2、可执行jar包的目录结构和内容

myspringboot-0.0.1-SNAPSHOT
   --BOOT-INF
         --classes  >里面是自己的springboot程序的class
         --lib  >pom.xml里的依赖jar
         --classpath.idx   >lib里jar包的清单
   --META-INF
         --maven  >maven的相关文件,pom.xml、pom.properties(gav)
         --MANIFEST.MF  >jar包的标准清单
   --org  >spring boot loader相关类
         --springframework
             --boot
                  --loader
                      --JarLauncher.class
                      --WarLauncher.class
                      --...

1.2.1、META-INF

这个文件夹是fat jar包的标准要求。JAR原文Jar Archive File,是java的一种文档档式。MANIFEST.MF是生成fat jar时自动创建的。
MAINIFEST.MF的内容如下:
 
Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: myspringboot
Implementation-Version: 0.0.1-SNAPSHOT
Start-Class: com.lionman.boot.MainApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.3.1.RELEASE
Created-By: Maven Jar Plugin 3.2.0
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.JarLauncher

  

Main-Class是fat jar可执行主类名,即:org.springframework.boot.loader.JarLauncher,这个才是Spring Boot应用的入口。

如果打成war包,Main-Class就是org.springframework.boot.loader.WarLauncher。

1.2.2、spring boot lader相关类

org:里面是spring boot loader相关类的字节码文件,即:spring boot程序启动需要的classes,这些classes所在的依赖包在我们写的spring boot程序里是并没有,因为只是启动使用。想看loader的源代码,需要手动加入到pom.xml
 
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-loader</artifactId>
</dependency>

 

这样就可以看到spring boot loader了

 打包时就是把这个jar里的内容拷贝到fat jar里。

2、JarLauncher

JarLauncher是spring boot loader进fat jar的入口,用Idea的JarApplication跟进调试。
 

 

 

JarLauncher的main方法两步:new JarLauncher()、launch(args)

2.2.1、new JarLauncher()

JarLauncher是构造方法是一个空实现,所以会调用它的父类ExecutableArchiveLauncher的构造方法。
附:JarLauncher、WarLauncher、ExecutableArchiveLauncher、Launcher关系图
 

 new JarLauncher()完成了构造Archive,这里即JarArchive。

 2.2.2、launch(args)

launch(args)调用的是基类Launcher里的方法,源码如下:

 

 

(1)getClassPathArchivesIterator()
会获取应用的fat jar,返回类型是Iterator<Archive>
 
(2)createClassLoader(getClassPathArchivesIterator())
会创建一个新的类加载器LaunchedURLClassLoader(它的父类加载器是AppClassLoader),用于加载BOOT-INF下classese文件夹的所有class和lib文件夹里的所有jar。
 

 

(3)getMainClass()
获取MANIFEST.MF里Start-Class,即被@SpringBootAppliction注解修饰带有main方法的spring boot启动类。
 
(4)launch(args, launchClass, classLoader)
 

 创建MainMethodRunner,反射调用spring boot启动类里的main方法。

main()执行了,spring boot应用就正式启动了。

3、spring boot为什么要自定义类加载器

从上面的源码分析,可以看出,spring boot创建了自己的类加载器,叫LaunchedURLClassloader。
spring boot想完成一个jar包完成整个程序的运行。jar in jar,无法进行加载。所以spring boot引入自定义类加载器去解决那些不符合jar规格的类加载问题。
 
spring boot这种优雅的方式,将我们自己的类和第三方jar包全部分开放置,AppClassLoader加载符合规范的Spring Boot ClassLoader(LaunchedURLClassloader)后,整个后续的类加载操作都会由自定义类加载器去加载,完美得实现了jar包的嵌套,带来了太多的便利。
 

4、附:JarLauncher流程图

 

 

 

 

posted on 2020-06-25 16:27  机甲龙  阅读(516)  评论(0编辑  收藏  举报