JAVASpring

从源码角度3分钟理解SpringBoot的jar可以直接运行的原因

转载:从源码角度3分钟理解SpringBoot的jar可以直接运行的原因

在springboot项目中我们使用maven打包插件将项目打成一个jar之后,然后使用java -jar的命令就可以直接运行jar了,这是什么原理呢?今天来揭秘一下springboot的jar直接运行的原理。

1、制作jar

(1)添加依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-loader</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <!--  spring-boot 打包  -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

(2)编写基础的启动代码和测试类

------------------------启动类----------------------
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class SpringJarApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringJarApplication.class, args);
    }
}
------------------------测试类------------------------
@RestController
@RequestMapping("/test")
public class SpringJarController {

    @GetMapping("/test01")
    public String test01() {
        return "test01";
    }
}
------------------------配置文件------------------------
server:
  port: 8080
spring:
  application:
    name: spring-jar

(3)使用springboot的插件打包

打包成功之后的日志输出:

到这里就完成使用springboot插件打包了一个jar。使用java -jar的命令运行的效果如下所示:

2、解析jar的结构


 在本地的仓库中找到刚打的jar,然后通过工具打开springboot插件打包jar包,如下所示:

(1)BOOT-INF包

BOOT-INF包含应用程序的所有类文件和资源,在BOOT-INF包下的lib中将项目中需要依赖的jar都打包进来了,因此我们又称springboot通过插件打包的jar叫做fat jar。

(2)META-INF包

这个包下包含了一份很重要的清单文件,此文件包含了应用程序的元数据信息,如下是清单文件的详细信息:

Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: spring.jar
Implementation-Version: 1.0-SNAPSHOT
Start-Class: com.longxia.jar.SpringJarApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.3.2.RELEASE
Created-By: Maven Jar Plugin 3.2.0
Main-Class: org.springframework.boot.loader.JarLauncher

我们需要重点关注的两处,一处是Main-Class,因为它指定了应用的主类,也就是jar包的入口位置;一处是Start-Class,它标识着项目的启动类位置。

3、jar的运行原理


 在运行jar包的时候,首先会找到MANIFEST.MF下的JarLauncher类,此类的就是程序的入口,JarLauncher类的代码如下所示:

Fat jar在启动JarLauncher中的main函数的时候,Fat jar会负责创创建一个LunchedURLClassLoader来加载BOOT-INF包下的lib中的jar,如下所示:

接下来在launch方法中寻找我们项目中的真正的启动类,那么如何寻着启动类呢?其实是文件清单中的Start-class中找,如下所示:

找到项目启动类之后,通过反射的方式启动我们的项目,于是乎我们可以看到经典的启动图标:

总结:


(1)springboot提供的打包插件可以将应用程序打包成一个可执行的fat jar,在fat jar中包含了项目所需要的依赖和springboot loader相关的类信息。

(2)当执行java -jar命令的时候会先去MAINFEST.MF这个清单文件中寻找jar的启动入口(也就是Main-Class中指定的JarLauncher),然后在JarLauncher类下的launch方法中找到真正的应用启动类来执行。