一、緣起

看了朱老大的微博,這不說的正是我嘛!日常開發(fā)部署的過程中,我也會(huì)自己探索引入一些maven的自動(dòng)化部署,但是,到了關(guān)鍵性的上傳服務(wù)器這一步,確實(shí)沒有再精進(jìn)一步。在項(xiàng)目依賴越多越多的情況下,打包上傳確實(shí)是一件費(fèi)時(shí)的事情。所以,按照朱老大的思路,我也嘗試給我的項(xiàng)目包瘦瘦身。
二、運(yùn)行環(huán)境
操作系統(tǒng):Windows 10 ;
開發(fā)工具:IDEA-2019.3;
Web服務(wù)器:Tomcat 9.0.24;
JDK版本: jdk 1.8.0_221;
Maven版本:apache-maven-3.6.1
Spring boot 版本:2.0.9.RELEASE
三、原理分析
工欲善其事,必先利其器。想要把功能做好,就要先明白背后的原理。原理理解清楚了,實(shí)際操作可謂是手到擒來。
1. maven打包spring boot項(xiàng)目,配置pom.xml,將第三方的依賴包排除在項(xiàng)目包之外。
2. 首次打包之后,把項(xiàng)目包上傳到服務(wù)器,同時(shí)也要把第三方依賴包上傳到服務(wù)器。
以后部署的時(shí)候,第三方依賴包沒有發(fā)生變化得話,就不用再次上傳了。要想實(shí)現(xiàn)這樣一個(gè)目的,就像調(diào)整pom.xml中相關(guān)配置<skip>false</skip>。
3. 在服務(wù)器運(yùn)行項(xiàng)目包,要配置項(xiàng)目包引用到第三方依賴包。
- 打成jar包的話,這里也可以由兩種方式。第一種,就是在pom.xml中配置了第三方包的依賴的位置,在部署運(yùn)行的時(shí)候,項(xiàng)目包自己會(huì)找到第三方依賴包的位置,這和以前的部署方式?jīng)]有兩樣;第二種,是利用如下命令參數(shù)
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.。大概的意思就是:配置了這個(gè)參數(shù)的目錄,運(yùn)行項(xiàng)目包的時(shí)候,就會(huì)去掃描該目錄下的第三方依賴包。
看了這么多,我的感觸就是,知識(shí)太過龐雜了。就拿這個(gè)參數(shù)來說,這個(gè)知識(shí)點(diǎn)可能是jdk里的,也可以說spring boot里的。但是,在此之前,我還是不確定的。再說,中文互聯(lián)網(wǎng)的內(nèi)容太過雜亂,搜索尋找起來就格外的費(fèi)力。 - 打成war的話,我們可以把第三方依賴包放到tomcat的/lib目錄中,這樣,項(xiàng)目就可以自動(dòng)找到了。但是,/lib目錄下面本來就有許多自帶的jar包了,為了防止混淆,可以在/lib目錄下建一個(gè)ext目錄,來將項(xiàng)目要用到的第三方依賴包放在此處。
但是tomcat是無法識(shí)別這個(gè)ext目錄里面的jar包的,此時(shí)需要修改tomcat配置文件${catalina.home}/conf/catalina.properties中的common.loader值,加上${catalina.home}/lib/ext/*.jar,完成此步驟后,項(xiàng)目啟動(dòng)便可以使用到lib/ext里面的jar包了。
四、實(shí)際操作
1.jar包方式打包
- pom.xml 中的配置如下,里面詳細(xì)解釋了配置的含義。
<?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.
這個(gè)插件的作用:把第三方依賴包復(fù)制到target/lib/目錄下,達(dá)到分離的目的。
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<!-- 復(fù)制第三方 jar 到項(xiàng)目目錄下的 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>
<!-- 配置的作用:跳過復(fù)制第三方依賴這一步。這是在首次上傳
第三方依賴到服務(wù)器之后,啟用這個(gè)選項(xiàng),可以不用在打包時(shí)
重復(fù)復(fù)制,節(jié)省時(shí)間。-->
<skip>false</skip>
</configuration>
</execution>
</executions>
</plugin>
<!--
官方解釋:These plugins relate to packaging respective artifact types.
Build a JAR from the current project.
這個(gè)插件的作用:把項(xiàng)目打成jar包,插件配置的意思是:把第三方依賴的路徑,
寫入到MANIFEST.MF 文件中,格式是./lib/xxx.jar。這樣做,就是 讓項(xiàng)目包在
運(yùn)行的時(shí)候,能夠像以前一樣找到第三方依賴包。
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<!-- 指定 Spring Boot 啟動(dòng)類,實(shí)際測(cè)試中必須 -->
<!-- <mainClass>com.keqing.Kafka3Application</mainClass>-->
<!-- 將所有第三方 jar 添加到項(xiàng)目 jar 的 MANIFEST.MF 文件中,這樣運(yùn)行 jar 時(shí)依賴包才能
被加載 。此為關(guān)鍵步驟,有了這一步,我們?cè)诎训谌揭蕾嚢c項(xiàng)目包分離的情況
下,在服務(wù)器運(yùn)行,就和沒有分離時(shí), 是一摸一樣的了。-->
<addClasspath>true</addClasspath>
<!-- 指定第三方 jar 的目標(biāo)目錄為 ./lib/-->
<classpathPrefix>./lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<!--
上面的插件,只是把項(xiàng)目包打成符合maven的標(biāo)準(zhǔn)格式,還有利用spring boot的插件,
把包打成符合spring boot的格式才行。
官方文檔:https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/
為了讓用戶方便使用 Maven,少進(jìn)行配置甚至不用配置,就需要用 Maven 構(gòu)建項(xiàng)目。Maven 在安
裝好后,自動(dòng)為生命周期的主要階段綁定很多插件的目標(biāo)。
-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!-- repackage 時(shí)排除掉 第三方依賴 jar 文件,我們的可運(yùn)行 Spring Boot 的 jar 文件瞬間變小 ^_^
下面的配置給出了怎樣將生命周期的階段與插件的目標(biāo)相互綁定。這樣,在執(zhí)行mvn命令時(shí),會(huì)自
動(dòng)執(zhí)行 這個(gè)插件的目標(biāo)。
目標(biāo)可以有一個(gè)默認(rèn)的階段綁定,我們將在下面討論。
目標(biāo)有一個(gè)默認(rèn)的階段綁定,然后它將在該階段執(zhí)行。但是,如果目標(biāo)沒有綁定到任何生命周期階
段, 那么它就不會(huì)在構(gòu)建生命周期中執(zhí)行。
官方文檔:https://maven.apache.org/guides/mini/guide-configuring-plugins.html
-->
<!-- <executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>-->
<!--配置重新打包時(shí),要包含的第三方依賴包,配置為nothing,那么就會(huì)排除掉所有的第三方依賴
包-->
<configuration>
<includes>
<include>
<groupId>nothing</groupId>
<artifactId>nothing</artifactId>
</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.war包方式打包
1.pom.xml配置如下,里面一樣又詳細(xì)的說明
<?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>設(shè)置為true了。這樣可以跳過復(fù)制,節(jié)省時(shí)間。-->
<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包時(shí),標(biāo)準(zhǔn)流程
里,打jar包,并不會(huì)包含第三方依賴的包,第三方依賴包是在spring-boot-maven-plugin運(yùn)行時(shí),
被引入到項(xiàng)目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>
五、總結(jié)
寫這篇小文,真是煞費(fèi)苦心。難在哪里呢?難在涉及的知識(shí)太過龐雜。比如,有關(guān)maven的占位符問題,這個(gè)本來就是我的一個(gè)知識(shí)盲點(diǎn),在看到很多地方用到這方面的配置,并且又和spring boot交織在一塊的時(shí)候,我更是一腦袋的漿糊了。所以,關(guān)于這個(gè)知識(shí)點(diǎn),我還要另起一文,專門來闡述。
比如,有關(guān)java命令行的使用。有關(guān)maven插件的使用和配置,maven插件運(yùn)行的原理,有關(guān)spring boot打包的原理,spring boot打成的包的結(jié)構(gòu)特點(diǎn),springboot打成的包的運(yùn)行原理,tocmat的目錄結(jié)構(gòu)的含義。
其實(shí),看起來很多,但是,都是基礎(chǔ)性知識(shí)的復(fù)雜運(yùn)用。也說明自身基礎(chǔ)知識(shí)掌握的不夠牢靠。
尤其,對(duì)于maven 還是一知半解。然后,spring boot 又是基于maven的。在maven的基礎(chǔ)上,又對(duì)maven做了擴(kuò)展配置。讓問題一下子變得更加復(fù)雜了。我也感受到spring boot 知識(shí)的龐雜。僅僅是官方文檔,里面就有我需要的很多很多的答案。