一、缘起
看了朱老大的微博,这不说的正是我嘛!日常开发部署的过程中,我也会自己探索引入一些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. 在服务器运行项目包,要配置项目包引用到第三方依赖包。
- 打成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里的。但是,在此之前,我还是不确定的。再说,中文互联网的内容太过杂乱,搜索寻找起来就格外的费力。 - 打成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包方式打包
- 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 知识的庞杂。仅仅是官方文档,里面就有我需要的很多很多的答案。