Spring Boot - 瘦身大作战:优雅应对Spring Boot Fat Jar

2023-11-26 12:17:48 浏览数 (1)


Fat Jar

【pom.xml】

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.16</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.artisan</groupId>
    <artifactId>thinBootJar</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>thinBootJar</name>
    <description>thinBootJar</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <image>
                        <builder>paketobuildpacks/builder-jammy-base:latest</builder>
                    </image>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

打包后观察目录

重点关注下Main-Class


瘦身

pom修改

代码语言:javascript复制
 <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!-- 用来配置可执行jar包中Main-Class的类型,这里一定要设置为 ZIP,使打的jar包中的Main-Class为PropertiesLauncher
 -->
                    <layout>ZIP</layout>
                    <includes>
                        <!-- 不打依赖包 -->
                        <include>
                            <!-- nothing 代表不存在的依赖包,意思就是什么依赖包都不引入-->
                            <groupId>nothing</groupId>
                            <artifactId>nothing</artifactId>
                        </include>
                        <!--                        <include>-->
                        <!-- common是引入的公共服务模块 -->
                        <!--                            <groupId>com.xxx.xx.rpc</groupId>-->
                        <!--                            <artifactId>common</artifactId>-->
                        <!--                        </include>-->
                    </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>

JAR,即通常的可执行jar Main-Class: org.springframework.boot.loader.JarLauncher

WAR,即通常的可执行war,需要的servlet容器依赖位于WEB-INF/lib-provided Main-Class: org.springframework.boot.loader.warLauncher

ZIP,即DIR,类似于JAR Main-Class: org.springframework.boot.loader.PropertiesLauncher

MODULE,将所有的依赖库打包(scope为provided的除外),但是不打包Spring Boot的任何Launcher

NONE,将所有的依赖库打包,但是不打包Spring Boot的任何Launcher


打包后观察下 jar包 的大小 和 里面的内容

重点关注下Main-Class


copy lib

代码语言:javascript复制
 <!-- 将依赖的jar包copy到lib目录下  -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <!-- 指定依赖的路径 -->
                            <outputDirectory>
                                ${project.build.directory}/lib
                            </outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

打包后观察下


启动 -Dloader.path

为了让名字好看一点

代码语言:javascript复制
java -Dloader.path=./lib  -jar  artisan-test-thin-boot-jar.jar

验证

通过启动参数loader.path配置外置依赖包的加载路径。 项目成功启动,说明我们配置的外包依赖包加载生效了

spring-boot-loader 依赖

为了方便查看源码,我们引入如下依赖

代码语言:javascript复制
  <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-loader -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-loader</artifactId>
        </dependency>

类继承关系

org.springframework.boot.loader.Launcher类是特殊的引导程序类,用作可执行jar的主要入口点。它是jar文件中的实际Main-Class,用于设置适当的URLClassLoader并最终调用main()方法

  • 有三个启动器子类(JarLauncherWarLauncherPropertiesLauncher)。它们的目的是从目录中的嵌套jar文件或war文件(而不是在类路径中显式的文件)加载资源(.class文件等)。
  • 对于JarLauncherWarLauncher,嵌套路径是固定的。 JarLauncher位于BOOT-INF / lib /中,而WarLauncher位于WEB-INF / lib /WEB-INF / lib-provided /中。如果需要,可以在这些位置添加额外的罐子。
  • 默认情况下,PropertiesLauncher在应用程序存档中的BOOT-INF / lib /中查找。可以通过在loader.properties(这是目录,归档文件或归档文件中的目录的逗号分隔列表)中设置一个称为LOADER_PATHloader.path的环境变量来添加其他位置

概括一下: 启动器Launcher是为了项目启动加载依赖资源的,共有3个启动器 JarLauncher,WarLauncher和PropertiesLauncher ,

  • JarLauncher和WarLauncher加载资源的路径是固定的,
  • PropertiesLauncher可以通过环境变量loader.path来指定加载资源的位置

layout属性值说明:

  • JAR,即通常的可执行jar Main-Class: org.springframework.boot.loader.JarLauncher
  • WAR,即通常的可执行war,需要的servlet容器依赖位于 Main-Class: org.springframework.boot.loader.warLauncher
  • ZIP,即DIR,类似于JAR Main-Class: org.springframework.boot.loader.PropertiesLauncher

PropertiesLauncher属性配置

  1. loader.path: 用于定义lib包加载路径。
  2. loader.home: 解析loader.path中相对路径的基础路径,通常是类路径位置。
  3. loader.args: 设置main方法的默认参数,以空格分隔。
  4. loader.main: 指定要启动的主类的名称,例如com.xxx.Application。
  5. loader.config.name: 指定属性文件的路径,例如classpath:loader.properties。
  6. loader.system: 一个布尔值标志,指示是否将所有属性添加到系统属性,默认为false。

附 pom.xml

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.16</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.artisan</groupId>
    <artifactId>thinBootJar</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>thinBootJar</name>
    <description>thinBootJar</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>artisan-test-thin-boot-jar</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!-- 用来配置可执行jar包中Main-Class的类型,这里一定要设置为 ZIP,使打的jar包中的Main-Class为PropertiesLauncher
 -->
                    <layout>ZIP</layout>
                    <includes>
                        <!-- 不打依赖包 -->
                        <include>
                            <!-- nothing 代表不存在的依赖包,意思就是什么依赖包都不引入-->
                            <groupId>nothing</groupId>
                            <artifactId>nothing</artifactId>
                        </include>
                        <!--                        <include>-->
                        <!-- common是引入的公共服务模块 -->
                        <!--                            <groupId>com.xxx.xx.rpc</groupId>-->
                        <!--                            <artifactId>common</artifactId>-->
                        <!--                        </include>-->
                    </includes>
                </configuration>
            </plugin>

            <!-- 将依赖的jar包copy到lib目录下  -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <!-- 指定依赖的路径 -->
                            <outputDirectory>
                                ${project.build.directory}/lib
                            </outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

</project>

0 人点赞