基于spring-boot-maven-plugin插件打包lib文件外置,layout模式为ZIP模式

本篇文章为自己亲自实践所得,项目是基于 spring boot 的多模块 Maven 项目, 依赖错综复杂。参考网上千篇一律的复制文章躺了不少坑。
整体感觉下来,Maven就是一把利剑,理解的到位,能化腐朽为神奇,基础不牢,费心费神。为了减小本文篇幅,基础知识一定要参考apache maven 官网学习

为了少走弯路,基础插件知识请进入官网学习:https://maven.apache.org/plugins/index.html

正文

乍一看,Maven 可能看起来有很多东西,但简而言之,maven是将项目工程模式应用于项目构建的基础架构。
讲人话就是:Maven是一个插件执行框架;所有工作都由插件完成。同时 Maven 基于构建生命周期的核心概念,明确定义了构建和分发特定工件(项目)的过程。

本次用到的插件

非官方插件

插件 作用
spring-boot-maven-plugin spring boot官方打包插件,本次使用其 layout 为 ZIP 的模式进行打包

官方插件:

插件 作用
maven-resources-plugin 资源文件处理插件,配置文件中的 @…@ 包围的变量
maven-dependency-plugin 依赖操作(复制、解包)和分析。本次用来将依赖的 jar 包移到lib文件夹中
maven-assembly-plugin 构建源和/或二进制文件的程序包(分发)。比如打包成 zip 文件

maven插件的命名约定

maven插件有着自己的一套命名规范。官方插件命名的格式为 [maven-xxx-plugin],非官方的插件命名为 [xxx-maven-plugin] 。是不是觉得很眼熟,没错,spring boot starter 的命名也有类似的规范。

如下几个插件可以看其名字就能区分是maven官方的还是非官方的:spring-boot-maven-plugin、maven-surefire-plugin、maven-resources-plugin
官方插件可以详见官方地址:https://maven.apache.org/plugins/index.html

小步慢跑实现:jar包瘦身,lib包外置

千万不要想着一步实现lib包外置,我们的目标是将lib包外置,网上一堆文章直接贴整体配置文档,注释和当时什么地方有坑言之较少,所以我们还是得按自己的想法进行尝试,确保问题控制在自己的掌控范围之内。
目标: 实现lib外置

准备工作:

  1. 打包fast jar ,因为使用的是 spring boot项目,使用官方提供的插件进行打包。
    1. 使用 spring-boot-maven-plugin 插件,layout加载方式改为 ZIP 模式可以实现。(因为该插件是在maven-jar-plugin 插件之后执行)
    2. 使用官方的 maven-jar-plugin我尝试过但是依赖导入会有问题总是报错“java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy”类型的错误,因为MANIFEST.MF文件中Class-Path参数不能个性化排除业务jar。所以我调试无果之后仅使用的是spring boot的官方插件
    3. 另外一种是通过 spring 社区提供的一个试验性的插件 spring-boot-thin-launcher 这个插件用来将项目的依赖和配置从jar包中分离出去,打包成 thin jar,可以实现(有兴趣可以了解一下,应该比本篇文章简单),开源地址:https://github.com/spring-projects-experimental/spring-boot-thin-launcher
  2. 有哪些方法可以实现 fast jar中的lib文件中的jar包移到jar外部的文件夹中?
    需要使用 maven-dependency-plugin 插件
  3. fast jar的内部结构了解,了解打包插件最终修改的是哪些参数。MANIFEST.MF 文件及其参数了解, BOOT-INF文件夹作用
  4. pom文件中 build 配置的标签有哪些自己不知道的,比如 configuration 参数如何查询(官方文档)
  5. spring-boot-maven-plugin 插件能实现哪些功能,phase、goals
    1. 经了解spring-boot-maven-plugin 实现lib 外置需要,切换打包方式ZIP 启用 -Dloader.path=lib/
    2. 使用 includes 标签可以将匹配的 jar 保留在原来的BOOT-INF/lib中

实践的基本步骤

  1. 切换layout 为 ZIP模式,确保项目启动成功
  2. 先将lib包全部外置,确保项目再次启动成功。(如果直接执行jar文件,需要使用 -Dloader.path=lib/ 启动参数。最好编辑器中的启动和jar启动都测试完毕没问题再下一步)
  3. 过滤业务相关的jar,打包到 fast jar 中,启动肯定是要报错,错误来源与外部lib的jar包
  4. 配置maven-dependency-plugin 排除业务jar放入外置lib文件夹中。到此实现了lib外置
  5. (项目中可不用)进阶探究使用maven-assembly-plugin 对编译好的文件和启动脚本搬运到 zip|tar.gz 等文件中

先看成果最终信息

启动命令

启动的时候需要增加 -Dloader.path=lib/ 参数,线上环境还需要加上 -server,以下只是一个简单的启动命令:

java -Dloader.path=lib/ -Dfile.encoding=utf-8 -jar gd_mbs_zfxn.jar
如果是有配置文件的可以逗号分隔
java -Dloader.path=lib/,config/ -Dfile.encoding=utf-8 -jar gd_mbs_zfxn.jar --spring.profiles.active=system,test
后台运行启动,使用于Linux,信息输出到 nohup.out 文件
nohup java -Dloader.path=lib/,config/ -Dfile.encoding=utf-8 -jar gd_mbs_zfxn.jar --spring.profiles.active=system,test &

最终的jar文件结构

文件结构,jar包因为了便于展示结构,已解压成文件

. 本文件夹
├─gd_mbs_zfxn.jar ## jar 包
│ ├─BOOT-INF
│ │ ├─classes
│ │ │ ├─com
│ │ │ │ └─**** ## 省略详细信息,主要包含的启动类的class文件,对应MANIFEST.MF 文件的 Start-Class项
│ │ │ ├─config ## 存放配置文件 yml,xml 等等
│ │ │ ├─templates
│ │ │ │ └─license
│ │ │ └─xml
│ │ └─lib ## 还有项目依赖的业务 jar 包
│ ├─META-INF
│ │ └─maven
│ │ └─com.bdsoft.framework.gd
│ │ └─leea-app
│ └─org
│ └─springframework
│ └─boot
│ └─loader
│ ├─archive
│ ├─data
│ ├─jar
│ └─util
└─lib

  1. 所得jar包中: MANIFEST.MF 文件内容如下,请注意 Main-ClassPropertiesLauncher,这个是pom中 spring-boot-maven-plugin 插件配置 ZIP的结果。
Manifest-Version: 1.0
Implementation-Title: APP启动配置
Implementation-Version: 1.0.0.0
Start-Class: com.bdsoft.BDPWebApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.2.7.RELEASE
Created-By: Maven Jar Plugin 3.2.0
Main-Class: org.springframework.boot.loader.PropertiesLauncher
  1. 配置文件不外置,仍然放在jar的原来位置 BOOT-INF/classes 文件下面
    这个不需要配置,默认就是这样,如果想排除掉其他非必要的配置文件,在pom文件的 resource 中 排除即可。
    基于spring-boot-maven-plugin插件打包lib文件外置,layout模式为ZIP模式_第1张图片
  2. 业务jar仍然放在jar内部的 BOOT-INF/lib 文件下.基于spring-boot-maven-plugin插件打包lib文件外置,layout模式为ZIP模式_第2张图片
  3. 非业务的其他依赖jar包(不包含3中的三个业务jar)全部放置在启动jar(fast jar)同级的 lib文件夹下面。

pom 的 build 全部配置

maven的 build 的配置

复杂的,涉及配置文件中@xxxx@变量替换、zip|tar.gz文件打包的,可参考如下配置:

<build>
    <finalName>gd_mbs_zfxnfinalName>
    
    <resources>
        <resource>
            <directory>${basedir}/src/main/resourcesdirectory>
            
            <filtering>truefiltering>
        resource>
        
    resources>
    <plugins>
        
        <plugin>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-maven-pluginartifactId>
            <version>2.2.7.RELEASEversion>
            <configuration>
                
                <layout>ZIPlayout>
                <includeSystemScope>trueincludeSystemScope>
                <mainClass>com.bdsoft.BDPWebApplicationmainClass>
                <includes>
                    
                    <include>
                        <groupId>com.bdsoft.framework.gdgroupId>
                        <artifactId>leea-symartifactId>
                    include>
                    <include>
                        <groupId>com.bdsoft.framework.gdgroupId>
                        <artifactId>leea-efficiencyartifactId>
                    include>
                    <include>
                        <groupId>com.googlecode.aviatorgroupId>
                        <artifactId>leea-aviatorartifactId>
                    include>
                includes>
            configuration>
            <executions>
                <execution>
                    <goals>
                        
                        <goal>repackagegoal>
                    goals>
                execution>
            executions>
        plugin>
        
        <plugin>
            <groupId>org.apache.maven.pluginsgroupId>
            <artifactId>maven-surefire-pluginartifactId>
            <version>2.22.2version>
            <configuration>
                <skip>trueskip>
            configuration>
        plugin>
        
        <plugin>
            <groupId>org.apache.maven.pluginsgroupId>
            <artifactId>maven-resources-pluginartifactId>
            <version>3.1.0version>
            <configuration>
                <encoding>utf-8encoding>
                
                <useDefaultDelimiters>trueuseDefaultDelimiters>
            configuration>
        plugin>

        <plugin>
            <groupId>org.apache.maven.pluginsgroupId>
            <artifactId>maven-dependency-pluginartifactId>
            <version>3.1.0version>
            <executions>
                
                <execution>
                    <id>pra1id>
                    
                    <phase>packagephase>
                    <goals>
                        <goal>copy-dependenciesgoal>
                    goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}/liboutputDirectory>
                        
                        <stripVersion>falsestripVersion>
                        
                        <excludeGroupIds>
                            
                            com.bdsoft.framework.gd,
                            com.googlecode.aviator
                        excludeGroupIds>
                    configuration>
                execution>

            executions>
        plugin>


        
        <plugin>
            <artifactId>maven-assembly-pluginartifactId>
            <version>3.4.2version>
            <configuration>
                <descriptors>
                    <descriptor>src/main/assembly/assembly.xmldescriptor>
                descriptors>
            configuration>
            <executions>
                <execution>
                    <id>make-assemblyid>
                    <phase>packagephase>
                    <goals>
                        <goal>singlegoal>
                    goals>
                execution>
            executions>
        plugin>
    plugins>
build>

简单的,只实现 lib 外置,可参考如下配置,不需要关注本文 进阶配置章节的内容

<build>
    <finalName>gd_mbs_zfxnfinalName>
    
    <resources>
        <resource>
            <directory>${basedir}/src/main/resourcesdirectory>
        resource>
        
    resources>
    <plugins>
        
        <plugin>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-maven-pluginartifactId>
            <version>2.2.7.RELEASEversion>
            <configuration>
                
                <layout>ZIPlayout>
                <includeSystemScope>trueincludeSystemScope>
                <mainClass>com.bdsoft.BDPWebApplicationmainClass>
                <includes>
                    
                    <include>
                        <groupId>com.bdsoft.framework.gdgroupId>
                        <artifactId>leea-symartifactId>
                    include>
                    <include>
                        <groupId>com.bdsoft.framework.gdgroupId>
                        <artifactId>leea-efficiencyartifactId>
                    include>
                    <include>
                        <groupId>com.googlecode.aviatorgroupId>
                        <artifactId>leea-aviatorartifactId>
                    include>
                includes>
            configuration>
            <executions>
                <execution>
                    <goals>
                        
                        <goal>repackagegoal>
                    goals>
                execution>
            executions>
        plugin>
        
        <plugin>
            <groupId>org.apache.maven.pluginsgroupId>
            <artifactId>maven-surefire-pluginartifactId>
            <version>2.22.2version>
            <configuration>
                <skip>trueskip>
            configuration>
        plugin>

        <plugin>
            <groupId>org.apache.maven.pluginsgroupId>
            <artifactId>maven-dependency-pluginartifactId>
            <version>3.1.0version>
            <executions>
                
                <execution>
                    <id>pra1id>
                    
                    <phase>packagephase>
                    <goals>
                        <goal>copy-dependenciesgoal>
                    goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}/liboutputDirectory>
                        
                        <stripVersion>falsestripVersion>
                        
                        <excludeGroupIds>
                            
                            com.bdsoft.framework.gd,
                            com.googlecode.aviator
                        excludeGroupIds>
                    configuration>
                execution>

            executions>
        plugin>
    plugins>
build>

业务 jar 的位置操作

想要实现业务 jar 仍然放在jar内部的 BOOT-INF/lib 文件下,其他依赖 jar 放入 lib文件夹中,需要两个插件配合。maven-dependency-plugin 和 spring-boot-maven-plugin 插件在 package 阶段根据各自的匹配策略将 业务jar 排除和匹配放入
核心思路就是:

  1. 我们想实现的目的是什么,这个目的处在maven生命周期的什么阶段,对应使用什么插件(
  2. 我们的目的需要在什么触发事件中执行(
  3. 我们的目的需要什么组件去实现,是测试组件还是打包组件还是依赖组件。将我们的想法分解成符合maven的工程概念,并找到其中合适的组件,配置它提供的(对应Mojo)目标功能(dependency)实现我们的目的。让它操作基于我们提供的匹配原则(includes符合或excludes排除)符合的文件

核心注意点:

  1. jar 包是在package阶段,我们需要将依赖jar导出到lib包中,可以使用为了便于理解我都采用的是 package阶段
  2. spring-boot-maven-plugin 插件中仅放行 业务jar 打入fast jar,请看 标签,因为默认 goal 是repackage,对应的是mvn package,maven-dependency-plugin 插件也使用 package 阶段,该插件使用详见:https://docs.spring.io/spring-boot/docs/2.2.1.RELEASE/maven-plugin/
    <includes>
        
        <include>
            <groupId>com.bdsoft.framework.gdgroupId>
            <artifactId>leea-symartifactId>
        include>
        <include>
            <groupId>com.bdsoft.framework.gdgroupId>
            <artifactId>leea-efficiencyartifactId>
        include>
        <include>
            <groupId>com.googlecode.aviatorgroupId>
            <artifactId>leea-aviatorartifactId>
        include>
    includes>
    
  3. maven-dependency-plugin插件中不能再将 业务jar 输出到外置 lib 文件夹,所以需要排除相应的业务jar,不然会在启动的时候报创建bean失败的错误,详见 excludeGroupIds 标签,记得使用 英文逗号分割,该插件使用详见:https://maven.apache.org/plugins/maven-dependency-plugin/,排除依赖相关说明详见copy-dependencies的文档:https://maven.apache.org/plugins/maven-dependency-plugin/copy-dependencies-mojo.html
    <plugin>
        <groupId>org.apache.maven.pluginsgroupId>
         <artifactId>maven-dependency-pluginartifactId>
         <version>3.1.0version>
         <executions>
             
             <execution>
                 <id>pra1id>
                 
                 <phase>packagephase>
                 <goals>
                     <goal>copy-dependenciesgoal>
                 goals>
                 <configuration>
                     <outputDirectory>${project.build.directory}/liboutputDirectory>
                     <stripVersion>falsestripVersion>
                     
                     <excludeGroupIds>
                         
                         com.bdsoft.framework.gd,
                         com.googlecode.aviator
                     excludeGroupIds>
                 configuration>
             execution>
    
         executions>
     plugin>
    

进阶配置

maven-assembly-plugin 插件对应的 assembly.xml 内容如下

<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.1 https://maven.apache.org/xsd/assembly-2.1.1.xsd">

    
    
    <id>${profiles.active}-${project.version}id>

    
    <formats>
        <format>tar.gzformat>
        <format>zipformat>
    formats>

    
    <includeBaseDirectory>falseincludeBaseDirectory>

    
    <fileSets>
        

        
        <fileSet>
            <directory>${basedir}/src/bindirectory>
            <outputDirectory>binoutputDirectory>
            <fileMode>0755fileMode>
            <includes>
                <include>**.shinclude>
                <include>**.batinclude>
            includes>
        fileSet>

        
        <fileSet>
            <directory>${project.build.directory}/classesdirectory>
            <outputDirectory>configoutputDirectory>
            <fileMode>0644fileMode>
            <includes>
                
                <include>**/bootstrap.ymlinclude>
                
                <include>**/application.ymlinclude>
                <include>**/application-system.ymlinclude>
                <include>**/application-${profiles.active}.ymlinclude>
                
                <include>mapper/**/*.xmlinclude>
                
                <include>static/**include>
                
                <include>templates/**include>
                
                <include>**/*.xmlinclude>
                
                <include>**/*.propertiesinclude>
                <include>**/*.txtinclude>
            includes>
        fileSet>

        
        <fileSet>
            <directory>${basedir}/target/libdirectory>
            <outputDirectory>liboutputDirectory>
            <fileMode>0755fileMode>
        fileSet>

        
        <fileSet>
            <directory>${project.build.directory}directory>
            <outputDirectory>outputDirectory>
            <fileMode>0755fileMode>
            <includes>
                <include>${project.build.finalName}.jarinclude>
            includes>
        fileSet>

        
        <fileSet>
            <directory>${basedir}directory>
            <includes>
                <include>*.mdinclude>
            includes>
        fileSet>
    fileSets>
assembly>

上的${profiles.active} 变量来源于pom文件中下面的profiles设置,如果不需要动态配置文件,可以忽略 将上面相关变量${profiles.active} 删除。


<profiles>
    <profile>
        
        <id>prossid>
        <properties>
            
            <profiles.active>proprofiles.active>
            
            <testparam>xxxtestparam>
        properties>
    profile>
    <profile>
        
        <id>preproid>
        <properties>
            
            <profiles.active>preproprofiles.active>
            
            <testparam>xxxtestparam>
        properties>
    profile>
    <profile>
        
        <id>devid>
        <properties>
            
            <profiles.active>devprofiles.active>
            
            <testparam>xxxtestparam>
        properties>
        <activation>
            
            <activeByDefault>trueactiveByDefault>
        activation>
    profile>
    <profile>
        
        <id>testid>
        <properties>
            
            <profiles.active>testprofiles.active>
            
            <testparam>xxxtestparam>
        properties>
    profile>
profiles>

maven的 package 打包时的详细信息

插件的执行顺序

maven-resources-plugin resources (default-resources)
maven-compiler-plugin compile (default-compile)
maven-resources-plugin testResources (default-testResources)
maven-compiler-plugin testCompile (default-testCompile)
maven-surefire-plugin test (default-test)
maven-jar-plugin jar (default-jar)
spring-boot-maven-plugin repackage (repackage)
spring-boot-maven-plugin repackage (default)
maven-dependency-plugin copy-dependencies (pra1) 【pra1 是execution 定义的id
maven-assembly-plugin single (make-assembly)

详细日志如下:


[INFO] ------------------< com.bdsoft.framework.gd:leea-app >------------------
[INFO] Building APP启动配置 1.0.0.0                                           [5/6]
[INFO] --------------------------------[ jar ]---------------------------------
....
[INFO] 
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ leea-app ---
[INFO] Using 'utf-8' encoding to copy filtered resources.
[INFO] Copying 16 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ leea-app ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 13 source files to D:\work\workspaces\workspace-af\xxxxxxxxxxx\gd_mbs_zfxn\xxxx\leea-app\target\classes
[INFO] /D:/work/workspaces/workspace-af/xxxxxxxxxxx/gd_mbs_zfxn/xxxx/leea-app/src/main/java/com/bdsoft/config/system/GlobalCorsConfig.java: 某些输入文件使用或覆盖了已过时的 API。
[INFO] /D:/work/workspaces/workspace-af/xxxxxxxxxxx/gd_mbs_zfxn/xxxx/leea-app/src/main/java/com/bdsoft/config/system/GlobalCorsConfig.java: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。
[INFO] 
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ leea-app ---
[INFO] Using 'utf-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\work\workspaces\workspace-af\xxxxxxxxxxx\gd_mbs_zfxn\xxxx\leea-app\src\test\resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ leea-app ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ leea-app ---
[INFO] Tests are skipped.
[INFO] 
[INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ leea-app ---
[INFO] Building jar: D:\work\workspaces\workspace-af\xxxxxxxxxxx\gd_mbs_zfxn\xxxx\leea-app\target\gd_mbs_zfxn.jar
[INFO] 
[INFO] --- spring-boot-maven-plugin:2.2.7.RELEASE:repackage (repackage) @ leea-app ---
[INFO] Layout: ZIP
[INFO] Replacing main artifact with repackaged archive
[INFO] 
[INFO] --- spring-boot-maven-plugin:2.2.7.RELEASE:repackage (default) @ leea-app ---
[INFO] Layout: ZIP
[INFO] Replacing main artifact with repackaged archive
[INFO] 
[INFO] --- maven-dependency-plugin:3.1.0:copy-dependencies (pra1) @ leea-app ---
[INFO] Copying tomcat-embed-websocket-9.0.37.jar to D:\work\workspaces\workspace-af\xxxxxxxxxxx\gd_mbs_zfxn\xxxx\leea-app\target\lib\tomcat-embed-websocket-9.0.37.jar
[INFO] Copying lombok-1.18.12.jar to D:\work\workspaces\workspace-af\xxxxxxxxxxx\gd_mbs_zfxn\xxxx\leea-app\target\lib\lombok-1.18.12.jar
....
[INFO] 
[INFO] --- maven-assembly-plugin:3.4.2:single (make-assembly) @ leea-app ---
[INFO] Reading assembly descriptor: src/main/assembly/assembly.xml
[INFO] Building tar: D:\work\workspaces\workspace-af\xxxxxxxxxxx\gd_mbs_zfxn\xxxx\leea-app\target\gd_mbs_zfxn-dev-1.0.0.0.tar.gz
[INFO] Building zip: D:\work\workspaces\workspace-af\xxxxxxxxxxx\gd_mbs_zfxn\xxxx\leea-app\target\gd_mbs_zfxn-dev-1.0.0.0.zip
[INFO] 

总结

不要盲目相信网上 各种拷贝粘贴文,有问题第一时间应该去阅读一下官方的文档,毕竟官方才是理解最深的那位,文档也是最浅显易懂的。

其他:

  1. 如果报错创建class失败,可以看一下是不是依赖的jar包重复加载,如果是重复导致的,配置过滤排除重复导致报错的jar包。
  2. 读取不到yml或者其他配置文件,看是不是 ZIP 打包模式和 漏掉了”-Dloader.path“ 参数,或者未在 pom中指定
  3. 打包过程中报错,一定要仔细看一下Maven输出的详细信息。
  4. 上诉是项目生产使用的配置较为复杂,如果是只达到lib外置的实践,可以跳过 maven-assembly-plugin 插件的配置也就是上述的进阶配置。
  5. 对于有jar冲突或者是因为maven的jar解构导致jar信息丢失的,使用替换jar包包名解决也可详见我的另一篇文章《
    bcprov-jdk15to18和其他的bcprov版本jar包冲突(不同版本jar兼容)解决》。

最后,祝大家玩的开心~~

你可能感兴趣的:(笔记,maven,spring,spring,boot,团队开发)