Maven打包SpringBoot项目,排除第三方依赖包

一、缘起

image.png

看了朱老大的微博,这不说的正是我嘛!日常开发部署的过程中,我也会自己探索引入一些maven的自动化部署,但是,到了关键性的上传服务器这一步,确实没有再精进一步。在项目依赖越多越多的情况下,打包上传确实是一件费时的事情。所以,按照朱老大的思路,我也尝试给我的项目包瘦瘦身。

二、运行环境

操作系统:Windows 10 ;
开发工具:IDEA-2019.3;
Web服务器:Tomcat 9.0.24;
JDK版本: jdk 1.8.0_221;
Maven版本:apache-maven-3.6.1
Spring boot 版本:2.0.9.RELEASE

三、原理分析

工欲善其事,必先利其器。想要把功能做好,就要先明白背后的原理。原理理解清楚了,实际操作可谓是手到擒来。

1. maven打包spring boot项目,配置pom.xml,将第三方的依赖包排除在项目包之外。
2. 首次打包之后,把项目包上传到服务器,同时也要把第三方依赖包上传到服务器。

以后部署的时候,第三方依赖包没有发生变化得话,就不用再次上传了。要想实现这样一个目的,就像调整pom.xml中相关配置<skip>false</skip>

3. 在服务器运行项目包,要配置项目包引用到第三方依赖包。
  1. 打成jar包的话,这里也可以由两种方式。第一种,就是在pom.xml中配置了第三方包的依赖的位置,在部署运行的时候,项目包自己会找到第三方依赖包的位置,这和以前的部署方式没有两样;第二种,是利用如下命令参数java -Dloader.path=lib/ -Dfile.encoding=utf-8 -jar abc-api-1.0.0-SNAPSHOT-exec.jar。其中,loader.path参看官方文档:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#executable-jar-launching。其中,有解释是这样的:loader.path can contain directories (which are scanned recursively for jar and zip files), archive paths, a directory within an archive that is scanned for jar files (for example, dependencies.jar!/lib), or wildcard patterns (for the default JVM behavior). Archive paths can be relative to loader.home or anywhere in the file system with a jar:file: prefix.。大概的意思就是:配置了这个参数的目录,运行项目包的时候,就会去扫描该目录下的第三方依赖包。
    看了这么多,我的感触就是,知识太过庞杂了。就拿这个参数来说,这个知识点可能是jdk里的,也可以说spring boot里的。但是,在此之前,我还是不确定的。再说,中文互联网的内容太过杂乱,搜索寻找起来就格外的费力。
  2. 打成war的话,我们可以把第三方依赖包放到tomcat的/lib目录中,这样,项目就可以自动找到了。但是,/lib目录下面本来就有许多自带的jar包了,为了防止混淆,可以在/lib目录下建一个ext目录,来将项目要用到的第三方依赖包放在此处。
    但是tomcat是无法识别这个ext目录里面的jar包的,此时需要修改tomcat配置文件${catalina.home}/conf/catalina.properties中的 common.loader值,加上${catalina.home}/lib/ext/*.jar,完成此步骤后,项目启动便可以使用到lib/ext里面的jar包了。

四、实际操作

1.jar包方式打包

  1. pom.xml 中的配置如下,里面详细解释了配置的含义。
<?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.0.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.keqing</groupId>
    <artifactId>kafka3</artifactId>
    <version>0.0.1</version>
    <name>kafka3</name>
    <packaging>jar</packaging>
  
    <build>
        <plugins>
            
            <!--
                官方解释:These are miscellaneous tools available through Maven by default.
                Dependency manipulation (copy, unpack) and analysis.
                这个插件的作用:把第三方依赖包复制到target/lib/目录下,达到分离的目的。
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <!-- 复制第三方 jar 到项目目录下的 target/lib/ 下 -->
                    <execution>
                        <goals>
                            <!--
                             takes the list of project direct dependencies and optionally transitive
                             dependencies and copies them to a specified location, stripping the version
                             if desired.
                             This goal can also be run from the command line.
                            -->
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <excludeScope>provided</excludeScope>
                            <!-- 配置的作用:跳过复制第三方依赖这一步。这是在首次上传
                                  第三方依赖到服务器之后,启用这个选项,可以不用在打包时
                                  重复复制,节省时间。-->
                            <skip>false</skip>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!--
                官方解释:These plugins relate to packaging respective artifact types.
                Build a JAR from the current project.
                这个插件的作用:把项目打成jar包,插件配置的意思是:把第三方依赖的路径,
                写入到MANIFEST.MF 文件中,格式是./lib/xxx.jar。这样做,就是 让项目包在
                运行的时候,能够像以前一样找到第三方依赖包。
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <!-- 指定 Spring Boot 启动类,实际测试中必须 -->
<!--                            <mainClass>com.keqing.Kafka3Application</mainClass>-->
                            <!-- 将所有第三方 jar 添加到项目 jar 的 MANIFEST.MF 文件中,这样运行 jar 时依赖包才能 
                                   被加载 。此为关键步骤,有了这一步,我们在把第三方依赖包与项目包分离的情况 
                                   下,在服务器运行,就和没有分离时, 是一摸一样的了。-->
                            <addClasspath>true</addClasspath>
                            <!-- 指定第三方 jar 的目标目录为 ./lib/-->
                            <classpathPrefix>./lib/</classpathPrefix>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <!--
                上面的插件,只是把项目包打成符合maven的标准格式,还有利用spring boot的插件,
                把包打成符合spring boot的格式才行。
                官方文档:https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/
                为了让用户方便使用 Maven,少进行配置甚至不用配置,就需要用 Maven 构建项目。Maven 在安      
                装好后,自动为生命周期的主要阶段绑定很多插件的目标。
            -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <!-- repackage 时排除掉 第三方依赖 jar 文件,我们的可运行 Spring Boot 的 jar 文件瞬间变小 ^_^
                下面的配置给出了怎样将生命周期的阶段与插件的目标相互绑定。这样,在执行mvn命令时,会自 
                动执行 这个插件的目标。
                目标可以有一个默认的阶段绑定,我们将在下面讨论。
                目标有一个默认的阶段绑定,然后它将在该阶段执行。但是,如果目标没有绑定到任何生命周期阶 
                段, 那么它就不会在构建生命周期中执行。
                官方文档:https://maven.apache.org/guides/mini/guide-configuring-plugins.html
                -->
<!--                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>-->
                <!--配置重新打包时,要包含的第三方依赖包,配置为nothing,那么就会排除掉所有的第三方依赖 
                    包-->
                <configuration>
                    <includes>
                        <include>
                            <groupId>nothing</groupId>
                            <artifactId>nothing</artifactId>
                        </include>
                    </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.war包方式打包

1.pom.xml配置如下,里面一样又详细的说明

<?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.0.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.keqing</groupId>
    <artifactId>kafka3</artifactId>
    <version>0.0.1</version>
    <name>kafka3</name>
    <!--要按照war包格式打包,就要修改这里的配置-->
    <packaging>war</packaging>
    <build>
        <finalName>kafka</finalName>
        <plugins>
            <!--此插件的配置没有改变。唯一需要变得是,在首次打包,并上传了第三方依赖包之后,
            就可以将<skip>设置为true了。这样可以跳过复制,节省时间。-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <excludeScope>provided</excludeScope>
                            <skip>false</skip>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!--这里修改为war包插件。war包的配置,要在此处排除掉第三方依赖包。而打jar包时,标准流程 
                里,打jar包,并不会包含第三方依赖的包,第三方依赖包是在spring-boot-maven-plugin运行时,          
                被引入到项目jar包中的。-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <packagingExcludes>
                        WEB-INF/lib/*.jar
                    </packagingExcludes>
                </configuration>
            </plugin>
           <!--此插件配置依然相同,依旧要把第三方依赖排除出去。-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <includes>
                        <include>
                            <groupId>nothing</groupId>
                            <artifactId>nothing</artifactId>
                        </include>
                    </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

五、总结

写这篇小文,真是煞费苦心。难在哪里呢?难在涉及的知识太过庞杂。比如,有关maven的占位符问题,这个本来就是我的一个知识盲点,在看到很多地方用到这方面的配置,并且又和spring boot交织在一块的时候,我更是一脑袋的浆糊了。所以,关于这个知识点,我还要另起一文,专门来阐述。
比如,有关java命令行的使用。有关maven插件的使用和配置,maven插件运行的原理,有关spring boot打包的原理,spring boot打成的包的结构特点,springboot打成的包的运行原理,tocmat的目录结构的含义。
其实,看起来很多,但是,都是基础性知识的复杂运用。也说明自身基础知识掌握的不够牢靠。
尤其,对于maven 还是一知半解。然后,spring boot 又是基于maven的。在maven的基础上,又对maven做了扩展配置。让问题一下子变得更加复杂了。我也感受到spring boot 知识的庞杂。仅仅是官方文档,里面就有我需要的很多很多的答案。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容