# spring-boot
**Repository Path**: luoluoba/spring-boot
## Basic Information
- **Project Name**: spring-boot
- **Description**: spring-boot源码
- **Primary Language**: Unknown
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2021-05-14
- **Last Updated**: 2021-05-24
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
springboot2.3.7.BUILD-SNAPSHOT源码注释
Spring Boot源码解析的重点包括:
spring boot自动配置原理
spring boot启动原理
spring boot启动Spring容器源码解析
spring boot启动内置源码解析
外置tomcat启动外置Tomcat启动SpringBoot源码详解
##1.spring boot启动原理
###java -jar做了什么
根据oracle官网对该命令的描述:
使用-jar参数时,后面的参数是的jar文件名,该jar文件中包含的是class和资源文件。在manifest文件中有Main-Class,它指定了应用的启动类。说白了就是java -jar会去找jar中的manifest文件,在那里面找到Main-Class指定的启动类,然后运行其Main方法;
###SpringBoot的Jar包的打包插件
Spring Boot项目的pom.xml文件中默认使用如下插件(配置在)进行打包:
```xml
org.springframework.boot
spring-boot-maven-plugin
```
spring-boot-maven-plugin项目存在于spring-boot-tools目录中。spring-boot-maven-plugin默认有5个goals:repackage、run、start、stop、build-info。在打包的时候默认使用的是repackage
repackage能够将mvn package生成的软件包再次打包为可执行的软件包,并将mvn package生成的软件包重命名为*.original,所以执行以上命令之后,便生成了打包结果对应的两个文件。
###SpringBoot的jar包中META-INF内容如下
```
Manifest-Version: 1.0
Implementation-Title: spring-learn
Implementation-Version: 0.0.1-SNAPSHOT
Start-Class: com.tulingxueyuan.Application
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.1.5.RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher
```
理论上看,执行java -jar命令时JarLauncher类会被执行,但实际上是com.tulingxueyuan.Application被执行了,另外一个类要被使用必须先被加载,Java没有提供任何标准的方式来加载嵌套的jar文件。这其中发生了什么呢?为什么要这么做呢?在这之前先讲讲以下内容
###SpringBoot的Archive
Archive的概念:archive即归档文件,通常就是一个tar/zip格式的压缩包,而jar是zip格式
SpringBoot抽象了Archive的概念,一个Archive可以是jar(JarFileArchive),可以是一个文件目录(ExplodedArchive),可以抽象为统一访问资源的逻辑层。
SpringBoot定义了一个Archive接口用于描述资源,该接口有两个实现ExplodedArchive和JarFileArchive,分别用于在文件夹目录下寻找资源和在jar包环境下寻找资源。而在SpringBoot打包的fatJar中,使用的时JarFileArchive。
JarFileArchive的数据主要结构为:
```java
public class JarFileArchive implements Archive {
private final JarFile jarFile;
private URL url;
}
```
JarFile是对jar包的封装,每个JarFileArchive都会对应一个JarFile。JarFile被构造的时候会解析内部结构,去获取jar包里的各个文件或文件夹,这些文件或文件夹会被封装到Entry中,也存储在JarFileArchive中。注意:如果Entry是个jar,会解析成JarFileArchive。
比如springBoot的可执行jar包:
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,jar包中包含jar、jar包中包含jar包里面的class文件,会使用 !/ 分隔开如
```
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
```
这种方式只有org.springframework.boot.loader.jar.Handler能处理,它是SpringBoot内部扩展出来的一种URL协议。
###关于JarLauncher
从MANIFEST.MF可以看到Main函数是JarLauncher,作用是加载内部/BOOT-INF/lib下的jar及/BOOT-INF/classes下的应用class,JarLauncher实现很简单:
```
public class JarLauncher extends ExecutableArchiveLauncher {
public JarLauncher() {}
public static void main(String[] args) throws Exception {
new JarLauncher().launch(args);
}
}
```
主入口新建了JarLauncher并调用父类Launcher中的launch方法启动程序.
JarLauncher():JarLauncher继承 ExecutableArchiveLauncher,在创建JarLauncher时,父类无参构造方法构建了当前main方法所在的FatJar的JarFileArchive对象。
launch(args):
(1)以FatJar为file作为入参,构造JarFileArchive对象。获取其中所有的资源目标,取得其Url,将这些URL作为参数,构建了一个URLClassLoader。
(2)以第一步构建的ClassLoader加载MANIFEST.MF文件中Start-Class指向的业务类,并且执行静态方法main。进而启动整个程序。
另外ExecutableArchiveLauncher 继承了Launcher,在 Launcher 的launch方法中,通过以上archive的getNestedArchives方法找到/BOOT-INF/lib下所有jar及/BOOT-INF/classes目录所对应的archive,通过这些archives的url生成LaunchedURLClassLoader,并将其设置为线程上下文类加载器,启动应用。
###关于URLStreamHandler
java中描述资源常使用URL,而URL有一个方法用于打开链接java.net.URL#openConnection()。由于URL用于表达各种各样的资源,打开资源的具体动作由java.net.URLStreamHandler这个类的子类来完成。根据不同的协议,会有不同的handler实现。
SpringBoot对支持从jar in jar中内容读取做了定制,即支持多个!/分隔符的url路径。SpringBoot定制了以下两个方面:
定制java.net.URLStreamHandler的子类Handler,实现识别多个!/分隔符。
定制java.net.JarURLConnection的子类JarURLConnection,打开springboot链接,并获取InputStream的方法。
同时SpringBoot自定义了一套读取ZipFile的工具类和方法实现在定制的JarURLConnection正确获取输入流。
###总结
1.Spring Boot应用通过spring-boot-plugin 打包之后,生成一个Fat jar,包含了应用依赖的jar包和Spring Boot loader相关的类,并 生成了MANIFEST.MF 文件,文件中的main-class 指定运行java -jar的主程序。
2.springbootd的jar包启动时执行的为JarLauncher,它通过加载BOOT-INF/classes目录及BOOT-INF/lib目录下jar文件,实现了fat jar的启动。
其中通过SpringBoot扩展的JarFile、JarURLConnection及URLStreamHandler,实现了jar in jar中资源的加载。
通过SpringBoot扩展的URLClassLoader–LauncherURLClassLoader,实现了jar in jar中class文件的加载。
WarLauncher通过加载WEB-INF/classes目录及WEB-INF/lib和WEB-INF/lib-provided目录下的jar文件,实现了war文件的直接启动及web容器中的启动。
###如何调试JarLauncher
1.引入依赖
```
org.springframework.boot
spring-boot-loader
```
2.idea中配置JAR APPLICATION
idea -> Run/Debug Configurations -> Search sources using mgdule's classpath:指定调试的模块 -> Path to JAR:指定运行的jar包 -> OK -> Debug