SpringBoot - 用maven-dependency-plugin插件将项目代码与依赖分开打包

雨点打透心脏的1/2处 2023-09-30 15:22 75阅读 0赞

SpringBoot - 用maven-dependency-plugin插件将项目代码与依赖分开打包

写在前面

通常基于SpringBoot框架的项目,在发布打包时会将项目代码和所有依赖文件一起打成一个可执行的、一体化的JAR包,如果项目的依赖包很多,那么这个JAR包就会非常大,如果想把项目依赖的JAR从项目一体化的JAR包中分离出来,该怎么办呢?

具体步骤

①. 修改打包插件

将原来的打包插件:

  1. <plugin>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-maven-plugin</artifactId>
  4. <version>2.4.2</version>
  5. <configuration>
  6. <executable>true</executable>
  7. <jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
  8. </configuration>
  9. </plugin>

替换为下面的打包插件:

  1. <!-- 需要指定启动类,否则报错。-->
  2. <plugin>
  3. <groupId>org.apache.maven.plugins</groupId>
  4. <artifactId>maven-jar-plugin</artifactId>
  5. <configuration>
  6. <archive>
  7. <!-- 生成的项目JAR包中,不要包含pom.xml和pom.properties这两个文件 -->
  8. <addMavenDescriptor>false</addMavenDescriptor>
  9. <manifest>
  10. <!-- 是否要把第三方依赖的JAR包加入到类构建路径 -->
  11. <addClasspath>true</addClasspath>
  12. <!-- 外部依赖JAR包的存放路径,为了在MANIFEST.MF文件中指定外部依赖的路径 -->
  13. <classpathPrefix>lib/</classpathPrefix>
  14. <!-- 项目主启动类 -->
  15. <mainClass>cn.hadoopx.servicex.ServicexApplication</mainClass>
  16. </manifest>
  17. </archive>
  18. </configuration>
  19. </plugin>
②. 新增文件复制插件
  1. <!-- 复制项目依赖的JAR包到指定的lib目录-->
  2. <plugin>
  3. <groupId>org.apache.maven.plugins</groupId>
  4. <artifactId>maven-dependency-plugin</artifactId>
  5. <executions>
  6. <execution>
  7. <id>copy-lib</id>
  8. <phase>package</phase>
  9. <goals>
  10. <goal>copy-dependencies</goal>
  11. </goals>
  12. <configuration>
  13. <!-- 将外部依赖的JAR包复制到target/lib路径下 -->
  14. <outputDirectory>target/lib</outputDirectory>
  15. <excludeTransitive>false</excludeTransitive>
  16. <stripVersion>false</stripVersion>
  17. <includeScope>runtime</includeScope>
  18. </configuration>
  19. </execution>
  20. </executions>
  21. </plugin>
③. 按常规方式打包
④. 将项目JAR和lib目录复制到项目的部署路径下,执行启动命令

在这里插入图片描述

常见问题

①. servicex-test-1.0-SNAPSHOT.jar 中没有主清单属性

由于在打包插件中没有指定项目主启动类导致。

maven-dependency-plugin插件的使用

一、goal

maven-dependency-plugin插件最常用的goal有:copy、copy-dependencies、unpack、unpack-dependencies

copy:拷贝指定jar包到指定目录,与当前工程的依赖没有关系

copy-dependencies:拷贝依赖jar包到指定目录

unpack:解压指定jar包到指定目录,与当前工程的依赖没有关系

unpack-dependencies:解压依赖jar包到指定目录

二、copy

copy可以的配置的项比较多

是必须配置的,指定jar包

默认值${project.build.directory}/dependency

1.pom文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>org.example</groupId>
  7. <artifactId>maven-haha</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <build>
  10. <plugins>
  11. <plugin>
  12. <groupId>org.apache.maven.plugins</groupId>
  13. <artifactId>maven-dependency-plugin</artifactId>
  14. <version>2.8</version>
  15. <configuration>
  16. <artifactItems>
  17. <artifactItem>
  18. <groupId>junit</groupId>
  19. <artifactId>junit</artifactId>
  20. <version>4.11</version>
  21. </artifactItem>
  22. </artifactItems>
  23. </configuration>
  24. </plugin>
  25. </plugins>
  26. </build>
  27. </project>

2.使用dependency:copy

mvn clean dependency:copy

img

结果:

img

三、copy-dependencies

1.pom文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>org.example</groupId>
  7. <artifactId>maven-haha</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <dependencies>
  10. <dependency>
  11. <groupId>junit</groupId>
  12. <artifactId>junit</artifactId>
  13. <version>4.13</version>
  14. <scope>test</scope>
  15. </dependency>
  16. </dependencies>
  17. <build>
  18. <plugins>
  19. <plugin>
  20. <groupId>org.apache.maven.plugins</groupId>
  21. <artifactId>maven-dependency-plugin</artifactId>
  22. <version>2.8</version>
  23. </plugin>
  24. </plugins>
  25. </build>
  26. </project>

2.使用dependency:copy-dependencies

mvn clean dependency:copy-dependencies

img

结果:

img

四、unpack、unpack-dependencies

unpack、unpack-dependencies是解压到指定目录,与copy、copy-dependencies操作类似

spring-boot-maven-plugin 打包分离依赖lib

1. 问题: spring-boot项目,默认生成的pom使用spring-boot-maven-plugin打包,会把所有依赖项都打进jar文件中,在调试阶段需要频繁传输jar包时很痛苦。

先看spring-boot-maven-plugin官文 [https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle ],其中有一节

5.3.5. Dependency Exclusion

By default, both the repackage and the run goals will include any provided dependencies that are defined in the project. A Spring Boot project should consider provided dependencies as “container” dependencies that are required to run the application.

Some of these dependencies may not be required at all and should be excluded from the executable jar. For consistency, they should not be present either when running the application.

There are two ways one can exclude a dependency from being packaged/used at runtime:

  • Exclude a specific artifact identified by groupId and artifactId, optionally with a classifier if needed.
  • Exclude any artifact belonging to a given groupId.

简译: spring-boot-maven-plugin默认会把依赖都打进jar包中,如果要使用exclude排除某项/某些依赖有两种方式:1.指定groupId和artifactId, 2.指定排除groupId下的所有项。这证实了spring-boot-maven-plugin的确会把依赖和程序打在一起。但是如果要用exclude排除所有依赖项的话显然不太容易,枚举所所有groupId的工作量就不小,而且笨重,所以排除。

Collection of artifact definitions to include. The Include element defines mandatory groupId and artifactId properties and an optional mandatory groupId and artifactId properties and an optional classifier property.

要包含的集合。其中的Include属性定义了groupId和artifactId属性…

这里有点不清晰:includes是要包含没错,但它与“默认值”之间是什么关系?spring-boot-maven-plugin插件默认在打包的时候是将所有依赖一并打包到最终文件的(jar或war),那么如果定义了includes,是只采用includes还是所有依赖+includes还是怎样?

经过实验,含义应该是“以includes定义覆盖默认值”,即显式包含了哪些就只有哪些。如此,就可以利用这个属性实现分离依赖的效果:

  1. <!-- 原有自动生成的pom -->
  2. ...
  3. <plugin>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-maven-plugin</artifactId>
  6. <version>2.4.2</version>
  7. </plugin>
  8. ...
  9. <!-- 改成 -->
  10. ...
  11. <plugin>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-maven-plugin</artifactId>
  14. <version>2.4.2</version>
  15. <configuration>
  16. <includes>
  17. <!-- 这里只包含一个不存在的项nothing,即代表什么都不包含,当然名字可以随便写 -->
  18. <include>
  19. <groupId>nothing</groupId>
  20. <artifactId>nothing</artifactId>
  21. </include>
  22. </includes>
  23. </configuration>
  24. </plugin>
  25. ...

然后再使用maven-dependency-plugin插件把依赖项拷贝到lib文件夹中,最后的pom文件build标签如下:

  1. <build>
  2. <plugins>
  3. <plugin>
  4. <groupId>org.apache.maven.plugins</groupId>
  5. <artifactId>maven-compiler-plugin</artifactId>
  6. <configuration>
  7. <source>1.8</source>
  8. <target>1.8</target>
  9. </configuration>
  10. </plugin>
  11. <plugin>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-maven-plugin</artifactId>
  14. <version>2.4.2</version>
  15. <configuration>
  16. <includes>
  17. <include>
  18. <groupId>haha</groupId>
  19. <artifactId>haha</artifactId>
  20. </include>
  21. </includes>
  22. </configuration>
  23. </plugin>
  24. <!--拷贝依赖到lib目录-->
  25. <plugin>
  26. <groupId>org.apache.maven.plugins</groupId>
  27. <artifactId>maven-dependency-plugin</artifactId>
  28. <executions>
  29. <execution>
  30. <id>copy-lib</id>
  31. <phase>package</phase>
  32. <goals>
  33. <goal>copy-dependencies</goal>
  34. </goals>
  35. <configuration>
  36. <outputDirectory>target/lib</outputDirectory>
  37. <excludeTransitive>false</excludeTransitive>
  38. <stripVersion>false</stripVersion>
  39. <includeScope>compile</includeScope>
  40. </configuration>
  41. </execution>
  42. </executions>
  43. </plugin>
  44. </plugins>
  45. </build>

使用maven窗口的package指令打包,target结果如下:

img img

使用java.ext.dirs指定lib路径启动,(我这里使用-Dloader.path没有加载成功)

  1. java -jar -Djava.ext.dirs=lib xxx-0.0.1-SNAPSHOT.jar

注:这里使用java.ext.dirs有一个问题:可能有些java内在的库调用会失败,比如我这里调用到了jce.jar>javax>crypto>Mac>getInstance会失败。参考链接https://blog.csdn.net/w47\_csdn/article/details/80254459 中的一段话:

首先介绍下java.ext.dirs参数的使用和环境变量:java中系统属性java.ext.dirs指定的目录由ExtClassLoader加载器加载,如果您的程序没有指定该系统属性(-Djava.ext.dirs=sss/lib)那么该加载器默认加载 J A V A H O M E / l i b / e x t 目录下的所有 j a r 文件。但如果你手动指定系统属性且忘了把 JAVA_HOME/lib/ext目录下的所有jar文件。但如果你手动指定系统属性且忘了把 JAVAHOME/lib/ext目录下的所有jar文件。但如果你手动指定系统属性且忘了把JAVA_HOME/lib/ext路径给加上,那么ExtClassLoader不会去加载$JAVA_HOME/lib/ext下面的jar文件,这意味着你将失去一些功能,例如java自带的加解密算法实现。

修改上述java指令,加上系统默认的ext路径$JAVA_HOME/jre/lib/ext:

  1. java -jar -Djava.ext.dirs=lib:$JAVA_HOME/jre/lib/ext xxx-0.0.1-SNAPSHOT.jar注意冒号做分隔符是linux,可能不同系统不一样

至此,依赖项在不变动的情况下不用重复传输,修改代码重新编译后只需要传输较小的jar包即可。

发表评论

表情:
评论列表 (有 0 条评论,75人围观)

还没有评论,来说两句吧...

相关阅读