本文用的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流程图
如果您觉得本文有用,不妨在右下角帮忙点个推荐,或者在评论里给我一句赞美,小小的成就都是我今后继续为大家编写优质文章的动力,机甲龙拜谢!
欢迎您持续关注我的博客:)