Maven進階學習指南

来源:https://www.cnblogs.com/Jcloud/archive/2023/06/26/17506002.html
-Advertisement-
Play Games

當我們在開發項目時,有時需要用到外部依賴組件,例如當我們需要Json序列化的時候需要用到FastJson組件,我們可以通過下載對應jar包載入到項目中。但當一個大的項目同時需要依賴各種各樣的外部服務,就存在著配置繁瑣、依賴衝突等問題,因此可以通過maven來完成對應的依賴管理功能。 ...


前言

當我們在開發項目時,有時需要用到外部依賴組件,例如當我們需要Json序列化的時候需要用到FastJson組件,我們可以通過下載對應jar包載入到項目中。但當一個大的項目同時需要依賴各種各樣的外部服務,就存在著配置繁瑣、依賴衝突等問題,因此可以通過maven來完成對應的依賴管理功能。

一、Settings配置

settings.xml用來配置maven項目中的各種參數文件,包括本地倉庫、遠程倉庫、私服、認證等信息。

1.1 配置概述

1.1.1 全局settings、用戶setting、pom的區別

  • 全局 settings.xml 是 maven 的全局配置文件,一般位於${maven.home}/conf/settings.xml,即maven文件夾下的conf中。
  • 用戶 setting是maven的用戶配置文件,一般位於${user.home}/.m2/settings.xml,即每位用戶都有一份配置文件。
  • pom.xml 文件是項目配置文件,一般位於項目根目錄下或子目錄下。

配置優先順序從高到低:pom.xml > 本地 settings > 全局 settings

如果這些文件同時存在,在應用配置時,會合併它們的內容,如果有重覆的配置,優先順序高的配置會覆蓋優先順序低的。

1.1.2 倉庫【重要】

如前言所述,我們依賴的外部服務是需要有地方進行存儲的,而存儲的地方就稱之為倉庫。其中倉庫又分為本地倉庫、中央倉庫、鏡像倉庫、私服。

其對應關係為:

image.png

(1)本地倉庫

當項目在本地編譯或運行時,直接載入本地的依賴服務無疑是最快的。預設情況下,不管在Window還是Linux下,每個用戶在自己用戶目錄下都有一個路徑名為.m2/repository/的倉庫目錄。

而原始的本地倉庫是為空的,因此maven需要知道一個網路上的倉庫,在本地倉庫不存在時前往下載網路上的倉庫,也就是遠程倉庫。

(2)中央倉庫

當maven未配置時,會預設請求maven的中央倉庫,中央倉庫包含了這個世界上絕大多數流行的開源java構件,以及源碼、作者信息、SCM,信息、許可證信息等,每個月這裡都會接受全世界java程式員大概1億次的訪問,它對全世界java開發者的貢獻由此可見一斑。

但是由於最常見的例如網路原因等,國外的中央倉庫使用起來並不順利,因此就有了下載地址為國內的中央倉庫,也就是鏡像倉庫。

(3)鏡像倉庫

總結來說,鏡像倉庫就是將國外的中心倉庫複製一份到國內,這樣一來下載速度以及訪問速度都將很快。

(4)私服

一般來說中央倉庫或者鏡像倉庫都能滿足我們的需求,但是當我們在公司內合作開發代碼時,可能因為系統保密性原因,有一些其他同事開發的外部依賴只希望能夠被本公司的人使用,而如果上傳到鏡像倉庫則保密性就不復存在了。因此私服最主要的功能時存儲一些公司內部不希望被公開的依賴服務。

1.2 settings配置詳解

settings.xml配置了本地全局maven的相關配置。

以下是一份seetings.xml的文件配置頂級元素。

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                          https://maven.apache.org/xsd/settings-1.0.0.xsd">
      <localRepository>
      <interactiveMode>
      <usePluginRegistry>
      <offline>
      <pluginGroups>
      <servers>
      <mirrors>
      <proxies>
      <profiles>
      <activeProfiles>
</settings>

1.2.1 localRepository

用來標識本地倉庫的位置

D:/Maven/Repository

1.2.2 interactiveMode

maven 是否需要和用戶交互以獲得輸入。預設為true。

true

1.2.3 usePluginRegistry

maven 是否需要使用 plugin-registry.xml 文件來管理插件版本。

false

1.2.4 offline

用來標識是否以離線模式運營maven。

當系統不能聯網時,可以通過次配置來離線運行。

false

1.2.5 servers

當使用maven私服時,某些私服需要配置認證信息,需要在此處填寫相應的配置。之所以不寫在pom.xml中是因為一般項目在上傳至代碼倉庫時同樣會將pom.xml上傳,而setting.xml一般位於用戶本地,因此相對比較安全。

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      https://maven.apache.org/xsd/settings-1.0.0.xsd">
  ...
  <!--配置服務端的一些設置。一些設置如安全證書不應該和pom.xml一起分發。這種類型的信息應該存在於構建伺服器上的settings.xml文件中。 -->
  <servers>
    <!--伺服器元素包含配置伺服器時需要的信息 -->
    <server>
      <!--這是server的id(註意不是用戶登陸的id),該id與distributionManagement中repository元素的id相匹配。 -->
      <id>server001</id>
      <!--鑒權用戶名。鑒權用戶名和鑒權密碼表示伺服器認證所需要的登錄名和密碼。 -->
      <username>my_login</username>
      <!--鑒權密碼 。鑒權用戶名和鑒權密碼表示伺服器認證所需要的登錄名和密碼。密碼加密功能已被添加到2.1.0 +。詳情請訪問密碼加密頁面 -->
      <password>my_password</password>
      <!--鑒權時使用的私鑰位置。和前兩個元素類似,私鑰位置和私鑰密碼指定了一個私鑰的路徑(預設是${user.home}/.ssh/id_dsa)以及如果需要的話,一個密語。將來passphrase和password元素可能會被提取到外部,但目前它們必須在settings.xml文件以純文本的形式聲明。 -->
      <privateKey>${usr.home}/.ssh/id_dsa</privateKey>
      <!--鑒權時使用的私鑰密碼。 -->
      <passphrase>some_passphrase</passphrase>
      <!--文件被創建時的許可權。如果在部署的時候會創建一個倉庫文件或者目錄,這時候就可以使用許可權(permission)。這兩個元素合法的值是一個三位數字,其對應了unix文件系統的許可權,如664,或者775。 -->
      <filePermissions>664</filePermissions>
      <!--目錄被創建時的許可權。 -->
      <directoryPermissions>775</directoryPermissions>
    </server>
  </servers>
  ...
</settings>

1.2.6 mirrors【重要】

用來配置相應的鏡像倉庫。

如果倉庫X可以提供倉庫Y存儲的所有內容,那麼就可以認為X是Y的一個鏡像。用過Maven的都知道,國外的中央倉庫因為網路原因用起來太慢了,所以選擇一個國內的鏡像就很有必要,我推薦國內的阿裡雲鏡像。 阿裡雲鏡像:配置很簡單,修改conf文件夾下的settings.xml文件,添加如下鏡像配置:

<mirrors>
    <mirror>
      <id>alimaven</id>
      <name>aliyun maven</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <mirrorOf>central</mirrorOf>        
    </mirror> 
    <mirror>
      <id>alimaven1</id>
      <name>aliyun maven1</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <mirrorOf>*</mirrorOf>        
    </mirror>
</mirrors>

其中id與name用來標識唯一的倉庫,url為鏡像倉庫地址,mirrorOf用來匹配當請求什麼倉庫依賴時使用該鏡像。

這裡介紹下<mirrorOf>配置的各種選項

  • <mirrorOf>*<mirrorOf>:匹配所有遠程倉庫。
  • <mirrorOf>external:*<mirrorOf>:匹配所有遠程倉庫,使用localhost的除外,使用file://協議的除外。也就是說,匹配所有不在本機上的遠程倉庫。
  • <mirrorOf>repo1,repo2<mirrorOf>:匹配倉庫repo1h和repo2,使用逗號分隔多個遠程倉庫。
  • <mirrorOf>*,!repo1<mirrorOf>:匹配所有遠程倉庫,repo1除外,使用感嘆號將倉庫從匹配中排除。

需要註意的是,由於鏡像倉庫完全屏蔽了被鏡像倉庫,當鏡像倉庫不穩定或者停止服務的時候,Maven仍將無法訪問被鏡像倉庫,因而將無法下載構件。

此外, maven 讀取mirror 配置是 從上往下讀取的,因此謹慎配置<mirrorOf>*<mirrorOf>,因為如果第一個鏡像倉庫配置瞭如此標誌,那麼如果該倉庫即使不存在對應依賴也不會向下游查詢。

1.2.7 proxies

用來配置代理。

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      https://maven.apache.org/xsd/settings-1.0.0.xsd">
  ...
  <proxies>
    <!--代理元素包含配置代理時需要的信息 -->
    <proxy>
      <!--代理的唯一定義符,用來區分不同的代理元素。 -->
      <id>myproxy</id>
      <!--該代理是否是激活的那個。true則激活代理。當我們聲明瞭一組代理,而某個時候只需要激活一個代理的時候,該元素就可以派上用處。 -->
      <active>true</active>
      <!--代理的協議。 協議://主機名:埠,分隔成離散的元素以方便配置。 -->
      <protocol>http</protocol>
      <!--代理的主機名。協議://主機名:埠,分隔成離散的元素以方便配置。 -->
      <host>proxy.somewhere.com</host>
      <!--代理的埠。協議://主機名:埠,分隔成離散的元素以方便配置。 -->
      <port>8080</port>
      <!--代理的用戶名,用戶名和密碼表示代理伺服器認證的登錄名和密碼。 -->
      <username>proxyuser</username>
      <!--代理的密碼,用戶名和密碼表示代理伺服器認證的登錄名和密碼。 -->
      <password>somepassword</password>
      <!--不該被代理的主機名列表。該列表的分隔符由代理伺服器指定;例子中使用了豎線分隔符,使用逗號分隔也很常見。 -->
      <nonProxyHosts>*.google.com|ibiblio.org</nonProxyHosts>
    </proxy>
  </proxies>
  ...
</settings>

1.2.8 profiles【重要】

根據環境參數來調整構建配置的列表。用於定義一組profile

seetings中的profile是 pom.xml 中 profile 元素的裁剪版本

它包含了idactivationrepositoriespluginRepositories 和 properties 元素。這裡的 profile 元素只包含這五個子元素是因為這裡只關心構建系統這個整體(這正是 settings.xml 文件的角色定位),而非單獨的項目對象模型設置。如果一個 settings.xml 中的 profile 被激活,它的值會覆蓋任何其它定義在 pom.xml 中帶有相同 id 的 profile

(1)repositories

定義了一組遠程倉庫的列表,當該屬性對應的profile被激活時,會使用該遠程倉庫。

<repositories>
  <!--包含需要連接到遠程倉庫的信息 -->
  <repository>
    <!--遠程倉庫唯一標識 -->
    <id>codehausSnapshots</id>
    <!--遠程倉庫名稱 -->
    <name>Codehaus Snapshots</name>
    <!--如何處理遠程倉庫里發佈版本的下載 -->
    <releases>
      <!--true或者false表示該倉庫是否為下載某種類型構件(發佈版,快照版)開啟。 -->
      <enabled>false</enabled>
      <!--該元素指定更新發生的頻率。Maven會比較本地POM和遠程POM的時間戳。這裡的選項是:always(一直),daily(預設,每日),interval:X(這裡X是以分鐘為單位的時間間隔),或者never(從不)。 -->
      <updatePolicy>always</updatePolicy>
      <!--當Maven驗證構件校驗文件失敗時該怎麼做-ignore(忽略),fail(失敗),或者warn(警告)。 -->
      <checksumPolicy>warn</checksumPolicy>
    </releases>
    <!--如何處理遠程倉庫里快照版本的下載。有了releases和snapshots這兩組配置,POM就可以在每個單獨的倉庫中,為每種類型的構件採取不同的策略。例如,可能有人會決定只為開發目的開啟對快照版本下載的支持。參見repositories/repository/releases元素 -->
    <snapshots>
      <enabled />
      <updatePolicy />
      <checksumPolicy />
    </snapshots>
    <!--遠程倉庫URL,按protocol://hostname/path形式 -->
    <url>http://snapshots.maven.codehaus.org/maven2</url>
    <!--用於定位和排序構件的倉庫佈局類型-可以是default(預設)或者legacy(遺留)。Maven 2為其倉庫提供了一個預設的佈局;然而,Maven 1.x有一種不同的佈局。我們可以使用該元素指定佈局是default(預設)還是legacy(遺留)。 -->
    <layout>default</layout>
  </repository>
</repositories>
(2)properties

定義了一組拓展屬性,當對應的profile被激活時該屬性有效。

<!--
  1. env.X: 在一個變數前加上"env."的首碼,會返回一個shell環境變數。例如,"env.PATH"指代了$path環境變數(在Windows上是%PATH%)。
  2. project.x:指代了POM中對應的元素值。例如: <project><version>1.0</version></project>通過${project.version}獲得version的值。
  3. settings.x: 指代了settings.xml中對應元素的值。例如:<settings><offline>false</offline></settings>通過 ${settings.offline}獲得offline的值。
  4. java System Properties: 所有可通過java.lang.System.getProperties()訪問的屬性都能在POM中使用該形式訪問,例如 ${java.home}。
  5. x: 在<properties/>元素中,或者外部文件中設置,以${someVar}的形式使用。
 -->
<properties>
  <user.install>${user.home}/our-project</user.install>
</properties>
(3)id

全局唯一標識,如果一個 settings.xml 中的 profile 被激活,它的值會覆蓋任何其它定義在 pom.xml 中帶有相同 id 的 profile

(4)pluginRepositories

同repositories差不多,不過該標簽定義的是插件的遠程倉庫。

(5)activation

觸發激活該profile的條件。

<activation>
  <!--profile預設是否激活的標識 -->
  <activeByDefault>false</activeByDefault>
  <!--當匹配的jdk被檢測到,profile被激活。例如,1.4激活JDK1.4,1.4.0_2,而!1.4激活所有版本不是以1.4開頭的JDK。 -->
  <jdk>1.5</jdk>
  <!--當匹配的操作系統屬性被檢測到,profile被激活。os元素可以定義一些操作系統相關的屬性。 -->
  <os>
    <!--激活profile的操作系統的名字 -->
    <name>Windows XP</name>
    <!--激活profile的操作系統所屬家族(如 'windows') -->
    <family>Windows</family>
    <!--激活profile的操作系統體繫結構 -->
    <arch>x86</arch>
    <!--激活profile的操作系統版本 -->
    <version>5.1.2600</version>
  </os>
  <!--如果Maven檢測到某一個屬性(其值可以在POM中通過${name}引用),其擁有對應的name = 值,Profile就會被激活。如果值欄位是空的,那麼存在屬性名稱欄位就會激活profile,否則按區分大小寫方式匹配屬性值欄位 -->
  <property>
    <!--激活profile的屬性的名稱 -->
    <name>mavenVersion</name>
    <!--激活profile的屬性的值 -->
    <value>2.0.3</value>
  </property>
  <!--提供一個文件名,通過檢測該文件的存在或不存在來激活profile。missing檢查文件是否存在,如果不存在則激活profile。另一方面,exists則會檢查文件是否存在,如果存在則激活profile。 -->
  <file>
    <!--如果指定的文件存在,則激活profile。 -->
    <exists>${basedir}/file2.properties</exists>
    <!--如果指定的文件不存在,則激活profile。 -->
    <missing>${basedir}/file1.properties</missing>
  </file>
</activation>

1.2.9 ActiveProfiles

在運行時手工激活的profile。

該元素包含了一組 activeProfile 元素,每個 activeProfile 都含有一個 profile id。任何在 activeProfile 中定義的 profile id,不論環境設置如何,其對應的 profile 都會被激活。如果沒有匹配的 profile,則什麼都不會發生。

<activeProfiles>
    <!-- 要激活的profile id -->
    <activeProfile>env-test</activeProfile>
</activeProfiles>

1.2.10 激活profile的三種方式【重要】

上面有提到了兩種激活的profile的方式,還有一種可以通過命令行激活profile。

(1)通過ActiveProfiles激活

如題1.2.9 可以同時激活多個profile。

(2)通過activation結果

如題1.2.8 (5)

(3)通過命令行的方式激活

也是我們經常使用的方式,例如:

mvn -P 

我們可以通過在pom.xml或setting.xml中指定不同環境的profile,在編譯構建不同的項目時,通過上述的命令行方式激活對應的profIle。例如在開發環境下:

mvn package -P dev 

可以打包開環環境下的項目。

1.3 Q&A

1.3.1 mirrors與repositories的關係【重要】

從上文可以看到,repository標簽與mirror標簽都定義了一個遠程倉庫的位置,那麼當一個依賴同時存在於兩個倉庫時,會先載入那個依賴呢?

這裡需要闡述一下maven載入真正起作用的repository的步驟,

  1. 首先獲取pom.xml中repository的集合,然後獲取setting.xml中mirror中元素。
  2. 如果repository的id和mirror的mirrorOf的值相同,則該mirror替代該repository。
  3. 如果該repository找不到對應的mirror,則使用其本身。
  4. 依此可以得到最終起作用的repository集合。可以理解mirror是覆寫了對應id的repository。

mirror相當於一個攔截器,會攔截被mirrorOf匹配到的repository,匹配原則參照 1.2.6 ,在匹配到後,會用mirror里定義的url替換到repository。

沒有配置mirror

img

配置了mirror

img

二、Pom.xml詳解

上章中setting.xml定義了某個環境下全局項目的相關依賴配置,而pom.xml則具體定義了某一個項目中的依賴配置。

2.1 pom元素

2.1.1 基本信息

<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
    http://maven.apache.org/xsd/maven-4.0.0.xsd">
 
    <!-- 模型版本 一般不用更改 -->
    <modelVersion>4.0.0</modelVersion>
    <!-- 公司或者組織的唯一標誌,也是打包成jar包路徑的依據 -->
    <!-- 例如com.companyname.project-group,maven打包jar包的路徑:/com/companyname/project-group -->
    <groupId>com.companyname.project-group</groupId>
 
    <!-- 項目的唯一ID,一個groupId下麵可能多個項目,就是靠artifactId來區分的 -->
    <artifactId>project</artifactId>
 
    <!-- 項目當前版本,格式為:主版本.次版本.增量版本-限定版本號 -->
    <version>1.0</version>
 
    <!--項目產生的構件類型,
    jar、war主要用來標識項目打包出的服務是jar包還是war包 
    pom一般用作多moudle的項目中 頂層的pom用來指定子moudle中需要依賴的版本 詳見2.1.3 -->
    <packaging>jar</packaging>
    
    <!--定義了本項目的名稱與example的網址 -->
    <name>itoken dependencies</name>
    <url>www.funtl.com</url>
</project>

基本信息都比較易懂,主要定義了本項目的相關配置說明,例如唯一坐標、版本、項目介紹等。

2.1.2 dependencies【重要】

(1)dependencies

本元素定義了項目中所需要的相關依賴信息,也是最重要的元素之一。

<!--該元素描述了項目相關的所有依賴。 這些依賴自動從項目定義的倉庫中下載 -->
<dependencies>
    <dependency>
        <!------------------- 依賴坐標 ------------------->
        <!--依賴項目的坐標三元素:groupId + artifactId + version -->
        <!--其中三要素的來源就是2.1.1中別人定義好的相關信息 -->
        <groupId>org.apache.maven</groupId>
        <artifactId>maven-artifact</artifactId>
        <version>3.8.1</version>
 
        <!------------------- 依賴傳遞 ------------------->
        <!--依賴排除,即告訴maven只依賴指定的項目,不依賴該項目的這些依賴。此元素主要用於解決版本衝突問題 -->
        <exclusions>
            <exclusion>
                <artifactId>spring-core</artifactId>
                <groupId>org.springframework</groupId>
            </exclusion>
        </exclusions>
        <!-- 可選依賴,用於阻斷依賴的傳遞性。如果在項目B中把C依賴聲明為可選,那麼依賴B的項目中無法使用C依賴 -->
        <optional>true</optional>
        
        <!------------------- 依賴範圍 ------------------->
        <!--依賴範圍。在項目發佈過程中,幫助決定哪些構件被包括進來
            - compile:預設範圍,適用於所有階段,會隨著項目一起發佈;  
            - runtime: 在執行時需要使用,如JDBC驅動,適用運行和測試階段,不同於例如fastjson,需要在編譯時使用;   
            - test: 只在測試時使用,用於編譯和運行測試代碼,例如junit,不同於junit,在發佈時並不需要;    
            - optional: 當項目自身被依賴時,標註依賴是否傳遞。用於連續依賴時使用 -->
        <scope>test</scope>
    </dependency>
</dependencies>

(2)如何解決依賴傳遞問題或jar包版本衝突問題

解決上述問題一般有兩種方式:

  • 當我們作為依賴服務提供者時,可以通過<optional>標簽排除掉不想被傳遞的服務。
<!-- Project A -->
<project>
  ...
  <dependencies>
    <!-- declare the dependency to be set as optional -->
    <dependency>
      <groupId>sample.ProjectB</groupId>
      <artifactId>Project-B</artifactId>
      <version>1.0</version>
      <optional>true</optional> 
    </dependency>
  </dependencies>
</project>

我們的A服務中引用了外部的依賴B服務,此時有A -> B,在別人引用我們時有C -> A ->B,若此時我們A在提供對外服務時不想讓別人依賴B服務,可以在引用B時添加<optional>標簽為true,這樣帶C依賴於A時並不會引入B。如果C在此時想要使用B的相關服務,需要在C的pom中顯示的調用B的相關服務。

  • 當我們作為依賴服務使用者時,可以通過<exclusions>來去除掉我們依賴包中所不想依賴的其他服務。

如果此時我們的A服務依賴於B服務,B服務依賴於C服務,則有A ->B ->C,因為某種原因例如jar包衝突,我們在A中並不想依賴於C服務的版本,可以在調用B服務時去除掉C的相關依賴,然後自己再在A中使用C的相關版本。

<project>
  ...
  <dependencies>
      
    <dependency>
      <groupId>sample.ProjectB</groupId>
      <artifactId>Project-B</artifactId>
      <version>1.0</version>
      <exclusions>
        <exclusion>
          <!-- 排除掉B中C的相關依賴 -->
          <groupId>sample.ProjectC</groupId>
          <artifactId>Project-C</artifactId>
        </exclusion>
      </exclusions> 
    </dependency>
      
    <!-- 自己引用C的相關版本 -->
    <dependency>
      <groupId>sample.ProjectC</groupId>
      <artifactId>Project-C</artifactId>
      <version>2.0</version>
    </dependency>
      
  </dependencies>
</project>

2.1.3 <dependencyManagement>

當一個服務中存在有多個moudle時,每個子module都可能引用了相同的jar包,但是如果將版本維護到子module的pom中,當需要升級時需要修改所有的pom文件版本。maven提供了繼承的方式來解決此問題。

<!--在父pom中定義子pom需要的相關依賴 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
</dependencyManagement>

在父pom的 <dependencyManagement> 中定義子pom需要的依賴及版本。

  <!--在子pom中  如下定義了父pom中相關依賴信息 -->
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.RELEASE</version>
        <relativePath/>
  </parent>

  <dependencies>
        <dependency>
            <!--因為引用了父pom 所以可以不指定版本 maven會自動去父pom中查找指定版本 此處為1.0.0 -->
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
  </dependencies>

當我們的pom中有定義父pom的元素後,可以在指定需要的依賴時不指定其版本,maven會幫我們去父pom中查找相關的版本信息。

2.1.4 properties

properties主要用來定義常量,通過${value}來使用。

 <!--配置依賴版本-->
 <properties>
     <!-- Environment Settings -->
     <java.version>1.8</java.version>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

     <!-- Spring cloud Settings   -->
     <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
     <spring-boot-admin.version>2.0.1</spring-boot-admin.version>
     <zipkin.version>2.10.1</zipkin.version>
 </properties>

<dependencies>
        <!--spring cloud-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--zipkin-->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin</artifactId>
            <version>${zipkin.version}</version>
        </dependency>
    <dependencies>

此外,maven還通過約定大於配置的方式定義了一些常用的屬性。

屬性 定義
${basedir} 存放pom.xml和所有的子目錄
${basedir}/src/main/java 項目的java源代碼
${basedir}/src/main/resources 項目的資源,比如說property文件,springmvc.xml
${basedir}/src/main/webapp/WEB-INF web應用文件目錄,web項目的信息,比如存放web.xml、本地圖片、jsp視圖頁面
${basedir}/target 打包輸出目錄
${project.version} 項目版本
${project.groupId} 項目groupid

2.1.5 resources

resources標簽用來標識項目在編譯運行時需要額外編譯的文件。例如手工引入jar包、不同運行環境對應不同的profile。

<build>
     <resources>
         <!--首先將預設resources目錄下的所有文件包括 -->
         <resource>
             <directory>src/main/resources</directory>
             <filtering>true</filtering>
             <!--只編譯所有以.fxml結尾的文件 -->
             <includes>
                 <include>**/*.fxml</include>
             </includes>
             <!--排除掉所有的yaml文件 -->
             <excludes>  
                    <exclude>**/*.yaml</exclude>  
             </excludes>
         </resource>
         <!--將不同環境下對應的不同yaml或properties文件編譯運行 -->
         <resource>
             <!--
             <directory>src/main/profiles/dev</directory>
             <directory>src/main/profiles/beta<directory>
             <directory>src/main/profiles/pre</directory>
             -->
             <directory>src/main/profiles/product</directory>
             <filtering>true</filtering>
             <includes>
                 <include>**/*.fxml</include>
             </includes>
         </resource>
         <!--將手工引入的jar包編譯運行 -->
         <resource>
                <directory>lib</directory>
                <targetPath>BOOT-INF/lib/</targetPath>
                <includes>
                    <include>**/*.jar</include>
                </includes>
            </resource>
     </resources>
</build>

2.1.6 profile

與setting.xml中profile所不同的是(參照1.2.8),pom中的profile有著更多的標簽來描述一組環境。從作用上來區分的話,一般setting.xml用來標識不同的遠程倉庫,而pom中的profile一般用來標識當前項目屬於什麼環境,如下是一組常見的pom中的profiles。

<profiles>
        <profile>
            <id>dev</id>
            <!--激活條件 其中預設為該profile 更多激活條件可以參考1.2.8 -->
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <!--當此profile被激活時,會將 project.active 的屬性賦值為dev -->
            <properties>
                <project.active>dev</project.active>
            </properties>
        </profile>
        <profile>
            <id>test</id>
            <!--當此profile被激活時,會將 project.active 的屬性賦值為test -->
            <properties>
                <project.active>test</project.active>
            </properties>
        </profile>
</profiles>

<resources>
         <resource>
             <!--根據不同的環境 project.active 取得不同的值 從而會將不同的環境下的yaml或properties文件編譯進項目中 達到只需要在編譯時指定環境變數即可 不用每次都要修改配置文件 -->
             <directory>src/main/${project.active}</directory>
             <filtering>true</filtering>
             <includes>
                 <include>**/*.fxml</include>
             </includes>
         </resource>
</resources>

此外,一般通過 mvn -P dev/beta/pre/product 命令來激活不同的環境,也可以通過其他的方式來激活profile,詳見1.2.10。

當然,pom中的profile不止有指定編譯環境的功能,同樣也可以指定遠程倉庫等其他功能。

2.1.6 modules

當我們項目中有多個模塊時,如果我們要單獨打包的話需要在每一個模塊都執行對應的maven命令,但是通過<modules>標簽可以將子服務或平級服務進行聚合,只需要打包該服務,也就會將其對應的子模塊同時打包。

<modules>
    <!-- 引入子模塊所在的相對目錄 -->
    <module>springmybatis</module>
    <!-- 引入同級模塊所在的相對目錄 -->
    <module>../utils</module>
 </modules>

三、null:jrdp-common:null:jar問題解決

3.1包找不到問題

我們某次在開發功能的時候,在我們的項目中引用了伏羲另外一個系統的jar包,但是預發環境下編譯的時候卻發現構建失敗,原因是

img

因為當時項目有用到京東自己的倉庫,所以我們當時第一反應是去倉庫中查找,結果發現倉庫中是有這個jar包的。

在發現並不是最常見的jar包不存在的問題後,我們開始分析報錯原因,發現報錯的 jrdp-common這個包並不是我們直接引用的,而是在我們引用的jar包中引用的,並且 null:jrdp-common:null:jar可以推測前面應該是 groupID與 version

假設我們的項目是A項目,引用的項目是B項目,也就是 A->B->jrdp-common

於是我們打開B項目,查看B的pom結構。

image.png

發現B項目的pom中確實引用了jrdp-common這個包,但是並沒有指定版本,是因為繼承了 xx-parent這個包,我們在這個包中確實也找到了指定的版本號,因此就排除了項目中沒有指定groupid與版本號的問題。

這個時候好像就問題就陷入了思路,但是我們註意到,我們之前另一個私服上也是有這個包的,那麼之前的在另一個私服上引用好像是沒有問題的,我們查看了私服了上的pom文件,發現也是跟項目一樣的。

然後我們就突然想到,會不會是倉庫中的包有問題,果不其然

image.png

沒有指定parent也沒有指定版本號,於是我們修改了這個版本的pom,至此問題解決。

總結:因為我們的系統已經被好多個團隊中轉接手過,因此可能在某些地方踩了不少坑,像這種問題應該就是之前的團隊上傳了一些有問題的jar包,導致依賴不可用,但是因為我們之前一直用的私服是沒有什麼問題的,只到這次用到了倉庫問題才浮現。

另外,此問題並不具有普適性,但是當遇到了groupid不能未空的時候可以按照此方法進行排查。

作者:京東科技 南韓凱

內容來源:京東雲開發者社區


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 博客推行版本更新,成果積累制度,已經寫過的博客還會再次更新,不斷地琢磨,高質量高數量都是要追求的,工匠精神是學習必不可少的精神。因此,大家有何建議歡迎在評論區踴躍發言,你們的支持是我最大的動力,你們敢投,我就敢肝 ...
  • ### 序 前面介紹了k8s組件和對象的一些基本概念,瞭解了k8s具體是做什麼的以及架構,那麼接下來我們開始介紹怎麼去安裝k8s,這裡我們以windows為例,其他平臺可以參考Kubernetes官方文檔,其實安裝方式都是類似的。 ### 先決條件 要在系統中安裝 Kubernetes,以下是一些需 ...
  • 某日二師兄參加XXX科技公司的C++工程師開發崗位第26面: > 面試官:`deque`用過嗎? > > 二師兄:說實話,很少用,基本沒用過。 > > 面試官:為什麼? > > 二師兄:因為使用它的場景很少,大部分需要性能、且需要自動擴容的時候使用`vector`,需要隨機插入和刪除的時候可以使用` ...
  • ## java獲取cpu核心數目 >```java >int processors = Runtime.getRuntime().availableProcessors(); >``` ...
  • > 本文首發於公眾號:Hunter後端 > 原文鏈接:[celery筆記八之資料庫操作定時任務](https://mp.weixin.qq.com/s/iM0VxVMagmRNeG2VIc01pg) 前面我們介紹定時任務是在 celery.py 中的 `app.conf.beat_schedule` ...
  • > 本文節選自筆者博客:[https://www.blog.zeeland.cn/archives/019hasaa](https://www.blog.zeeland.cn/archives/019hasaa) # 前言 如果你經常閱讀論文,那麼你肯定會遇到以下幾個問題: - 論文晦澀難懂看不明白 ...
  • 本文將通過閱讀AnnotationConfigApplicationContext源碼,分析Spring啟動流程。 # 創建AnnotationConfigApplicationContext ```java AnnotationConfigApplicationContext applicatio ...
  • # 概念 在 Java 中,`char` 和 `byte` 類型占用的存儲空間是不同的。 1. `char` 類型:`char` 是 16 位無符號的 Unicode 字元類型,用於表示單個字元。在 Java 中,`char` 類型占用 2 個位元組(16 位)的存儲空間。 2. `byte` 類型: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...