Maven學習筆記四【Profiles】
Apache Maven 2.0竭盡全力確保構建是可移植的。除其他之外,這意味著允許在POM中進行構建配置,避免所有檔案系統的引用。並且更加依賴本地儲存庫來儲存實現這一點所需的元資料。
然而,有時可移植性並不是完全可能的。某些條件下,外掛可能需要配置本地檔案系統路徑。在其他情況下,需要一個稍微不同的依賴集,並且專案的artifact名稱可能需要稍微調整一下。在另一種情況下,甚至可能需要在構建生命週期中包含一個完整的外掛,這取決於所檢測到的構建環境。
為了解決這些情況,Maven 2.0引入了構建概要檔案的概念。配置檔案使用POM本身可用元素的子集(外加一個額外的部分)來指定,並以各種方式觸發。他們在構建時修改POM,並用於互補的集合中,以便為一組目標環境提供等效但不同的引數(例如,在開發、測試和生產環境中提供appserver根的路徑)。因此,概要檔案很容易導致來自團隊不同成員的構建結果不同。但是,如果使用得當,可以在使用概要檔案的同時仍然保持專案的可移植性。這也將最小化maven -f選項的使用,該選項允許使用者使用不同的引數或配置建立另一個POM,從而使其更易於維護,因為它只在一個POM上執行。
配置檔案的不同型別以及定義在哪裡
-
Per Project - 定義在pom檔案中
(pom.xml)
-
Per User- 定義在Maven-settings
檔案中
(%USER_HOME%/.m2/settings.xml)
-
Global - 定義在全域性 Maven-settings
(${maven.home}/conf/settings.xml)
-
Profile descriptor - 位於
project basedir
(profiles.xml)
檔案的描述符(不支援Maven 3.0)
如何觸發Profile
配置檔案可以通過以下幾種方式觸發/啟用:
- Explicitly - 顯示地
- Through Maven settings - 通過Maven設定
- Based on environment variables - 基於環境變數
- OS settings - 作業系統設定
- Present or missing files - 提交或丟失檔案
配置檔案啟用的詳細資訊
可以使用-P CLI選項顯式指定概要檔案。此選項接受一個引數,該引數是要使用的概要檔案id的逗號分隔列表。當指定此選項時,除了啟用已配置的概要檔案外,還將啟用選項引數中指定的概要檔案。
mvn groupId:artifactId:goal -P profile-1,profile-2
可以通過<activeProfiles>
標籤在maven設定中啟用概要檔案。如下,接受<activeProfiles>
元素地列表,每個元素都包含一個概要檔案id:
<settings> ... <activeProfiles> <activeProfile>profile-1</activeProfile> </activeProfiles> ... </settings>
在<activeProfiles>
標籤中列出的配置檔案將在每次專案使用時被預設啟用。
可以根據構建環境的檢測狀態自動觸發概要檔案。這些觸發器是通過概要檔案中<activation>
部分指定的。 目前,這種檢測僅限於JDK版本的字首匹配、系統屬性的存在與否或系統屬性的值。下面有些例子:
當JDK的版本以”1.4”開頭時,以下配置將觸發配置檔案:
<profiles> <profile> <activation> <jdk>1.4</jdk> </activation> ... </profile> </profiles>
範圍匹配也可以在Maven 2.1中使用:
<profiles> <profile> <activation> <jdk>[1.3,1.6)</jdk> </activation> ... </profile> </profiles>
下面將基於作業系統設定啟用。有關作業系統值的更多細節,請參見Maven execucer 外掛。
<profiles> <profile> <activation> <os> <name>Windows XP</name> <family>Windows</family> <arch>x86</arch> <version>5.1.2600</version> </os> </activation> ... </profile> </profiles>
當系統屬性”debug”指定為任意值時,將啟用以下配置檔案:
<profiles> <profile> <activation> <property> <name>debug</name> </property> </activation> ... </profile> </profiles>
當系統屬性”debug”沒有被定義時,將啟用以下配置檔案:
<profiles> <profile> <activation> <property> <name>!debug</name> </property> </activation> ... </profile> </profiles>
如果沒有定義系統屬性”debug”,或者定義的值不為”true”,則會啟用以下概要檔案;
<profiles> <profile> <activation> <property> <name>debug</name> <value>!true</value> </property> </activation> ... </profile> </profiles>
要啟用它,你需要在命令列中輸入其中一個:
mvn groupId:artifactId:goal mvn groupId:artifactId:goal -Ddebug=false
下個示例,當環境變數”environment”被指定到”test”時,出發概要檔案:
<profiles> <profile> <activation> <property> <name>environment</name> <value>test</value> </property> </activation> ... </profile> </profiles>
要啟用它,可以在命令列輸入:
mvn groupId:artifactId:goal -Denvironment=test
從Maven 3.0開始,POM中的配置檔案也可以基於settings.xml中配置的active profiles屬性被啟用。
注意:像FOO這樣的環境變數可以作為表單env.FOO的屬性使用。進一步注意,環境變數名在Windows上被規範化為所有大寫。
當生成檔案target/generated-sources/axistools/wsdl2java/org/apache/maven
丟失時,下面示例將觸發概要檔案:
<profiles> <profile> <activation> <file> <missing>target/generated-sources/axistools/wsdl2java/org/apache/maven</missing> </file> </activation> ... </profile> </profiles>
在Maven 2.0.9中,可以插入標記<exists>
和<missing>
。支援的變數是系統屬性,如${user.home}
和${env.HOME}
。請注意,POM本身定義的屬性和值在這裡不能用於插值。例如上面的示例觸發器,不能使用${project.build.directory}
,只能硬編碼路徑。
配置檔案也可以預設啟用,使用如下配置:
<profiles> <profile> <id>profile-1</id> <activation> <activeByDefault>true</activeByDefault> </activation> ... </profile> </profiles>
這個概要檔案將作用於所有構建,除非使用上面列舉的方法之一啟用同意POM中的另一個概要檔案。當POM中的配置檔案在命令列上或通過其啟用配置檔案被啟用時,預設啟用的所有配置檔案將自動停用。
停用配置檔案
從Maven 2.0.10開始,可以使用命令列停用一個或多個概要檔案,方法是在它們的識別符號前加上字元’!’或’-‘:
mvn groupId:artifactId:goal -P !profile-1,!profile-2
這可以用於停用標記為activeByDefault的配置檔案,或者通過其啟用配置啟用的配置檔案。
POM的哪些區域可以根據每種型別的概要檔案進行定製
既然我們已經討論了在哪裡指定概要檔案,以及如何啟用它們,那麼我們可以在概要檔案中指定什麼將是非常有用的。與配置檔案的其他方面一樣,這個答案並不簡單。
根據選擇配置概要檔案的位置,可以訪問不同的POM配置選項。
外部檔案中的配置檔案
在外部檔案中指定配置檔案(也就是說在settings.xml或 profiles.xml檔案中)是不可移植的。任何看起來很有可能更改構建結果的內容都僅限於POM中的內聯概要檔案。像儲存庫列表這樣的東西,可能僅僅是已批准專案的專有儲存庫,並且不會改變構建的結果。因此,將只能修改< repository >
和< pluginrepository >
部分,外加一個額外的<properties>
部分。
<properties>
部分允許指定自由形式的鍵-值對,這些鍵-值對將包含在POM的插值過程中。這允許以${profile. provied .path}
的形式指定外掛配置。
POM中的配置檔案
另一方面,如果概要檔案可以在POM中合理地指定,那麼將有更多的選擇空間。當然,代價是隻能修改當前專案以及子模組。由於這些概要檔案是內聯指定的,因此有更好的機會保持可移植性。因此,有理由說可以向它們新增更多的資訊,而不會有其他使用者無法獲得這些資訊的風險。
POM中指定的配置檔案可以修改以下POM元素:
-
<repositories>
-
<pluginRepositories>
-
<dependencies>
-
<plugins>
-
<properties>
(實際上在主POM中不可用,但是在後臺使用) -
<modules>
-
<reporting>
-
<dependencyManagement>
-
<distributionManagement>
-
<build>
元素的子集,包含如下:<defaultGoal> <resources> <testResources> <finalName>
<profiles>
之外的POM元素
不允許在POM-profiles之外修改某些POM元素,因為當POM部署到儲存庫系統時,這些執行時修改將不會被分發,從而使該人員對該專案的構建與其他人完全不同。雖然可以在一定程度上通過為外部概要檔案提供的選項做到這一點,但是風險是有限的。另一個原因是這個POM資訊有時會從父POM中重用。
外部檔案,如settings.xml
andprofiles.xml
,也不支援POM-profiles之外的元素。讓我們以這個場景為詳細說明。當有效的POM部署到遠端儲存庫時,任何人都可以從儲存庫中提取它的資訊,並使用它直接構建Maven專案。現在,想象一下,如果我們可以在依賴項中設定對構建非常重要的概要檔案,或者在settings.xml檔案中的POM-profiles之外的任何其他元素設定概要檔案。那麼,很可能我們不能期望其他人使用儲存庫中的POM並能夠構建它。我們還必須考慮如何與其他人共享settings.xml檔案。注意,太多的檔案配置是非常令人困惑的,並且難以維護。底線是,既然它是構建資料,所以就應該在POM中。Maven 2的目標之一是將執行構建所需的所有資訊合併到單個檔案中,或POM的檔案層次結構中。
配置檔案陷阱
我們已經提到,向構建中新增概要檔案可能會破壞專案的可移植性。我們甚至強調了概要檔案可能破壞專案可移植性的情況。但是,有必要重申這些觀點,以便更連貫地討論在使用概要檔案時要避免的一些缺陷。
在使用概要檔案時,有兩個主要問題需要記住。首先是外部屬性,通常用於外掛配置。這些可能會破壞專案的可移植性。另一個更微妙的方面是對一組自然概要檔案的不完整規範。
外部屬性
外部屬性定義涉及pom.xml檔案之外,但沒有在其內部相應的配置檔案中定義的任何屬性值。POM中屬性最明顯的用法是在外掛配置中。雖然沒有屬性也有可能破壞專案的可移植性,但是這些東西可能具有導致構建失敗的微妙影響。例如,在settings.xml的概要檔案中指定appserver路徑。當團隊中另一個使用者檢視在沒有類似的settings.xml的情況下構建時,可呢個導致整合測試失敗。考慮如下web專案的pom.xml片段:
<project> ... <build> <plugins> <plugin> <groupId>org.myco.plugins</groupId> <artifactId>spiffy-integrationTest-plugin</artifactId> <version>1.0</version> <configuration> <appserverHome>${appserver.home}</appserverHome> </configuration> </plugin> ... </plugins> </build> ... </project>
現在,在本地${user.home}/.m2/settings.xml
中,將有如下配置:
<settings> ... <profiles> <profile> <id>appserverConfig</id> <properties> <appserver.home>/path/to/appserver</appserver.home> </properties> </profile> </profiles> <activeProfiles> <activeProfile>appserverConfig</activeProfile> </activeProfiles> ... </settings>
構建整合測試生命週期階段時,因為您提供的路徑允許測試外掛安裝和測試這個web應用程式,整合測試通過。
但是,當其他同事試圖構建整合測試時,他的構建非常失敗,抱怨它不能解決外掛配置引數<appserverHome>
,或者更糟糕的是,該引數值-字面意思${appserver.home}
無效。
恭喜,現在的專案是不可移植的。將這個配置內聯到你的pom.xml中,可以幫助緩解這一問題,但它有一個明顯的缺點:現在每個專案層次結構必須指定此資訊。由於Maven為專案繼承提供了良好的支援,所以可以將這種配置保留在團隊級POM的<pluginManagement>
部分或類似的部分中,並簡單地繼承路徑。
另一個不太吸引人的答案可能是開發環境的標準化。然而,這往往會損害Maven能夠提供的生產力增益。
自然輪廓集的不完整規範
除了上述可移植性破壞因素之外,概要檔案很容易無法涵蓋所有情況。當你這樣做的時候,你通常會離開你的目標環境。讓我們以pom.xml為例:
<project> ... <build> <plugins> <plugin> <groupId>org.myco.plugins</groupId> <artifactId>spiffy-integrationTest-plugin</artifactId> <version>1.0</version> <configuration> <appserverHome>${appserver.home}</appserverHome> </configuration> </plugin> ... </plugins> </build> ... </project>
現在,考慮以下概要檔案,它將在pom.xml中內聯指定:
<project> ... <profiles> <profile> <id>appserverConfig-dev</id> <activation> <property> <name>env</name> <value>dev</value> </property> </activation> <properties> <appserver.home>/path/to/dev/appserver</appserver.home> </properties> </profile> <profile> <id>appserverConfig-dev-2</id> <activation> <property> <name>env</name> <value>dev-2</value> </property> </activation> <properties> <appserver.home>/path/to/another/dev/appserver2</appserver.home> </properties> </profile> </profiles> .. </project>
執行如下命令:
mvn -Denv=dev-2 integration-test
構建成功,並且應用ID為appserverConfig-dev-2概要檔案給出的屬性。
執行如下命令:
mvn -Denv=dev integration-test
構建成功,並且應用ID為appserverConfig-dev概要檔案給出的屬性。
但是,執行如下命令:
mvn -Denv=production integration-test
為什麼不會構建成功呢。因為所得到的${appserver.home}
的非插值字面值不是部署和測試web應用的有效路徑。在編寫配置檔案時,我們沒有考慮生產環境的情況。生產環境、以及測試甚至是本地環境構成了一組自然的目標環境,我們可能希望為其構建整合測試生命週期階段。這個自然集的不完整規範意味著我們將有效的目標環境有效地限制到開發環境。團隊的其他成員可能看不到其中的奧妙。所以當構建概要檔案來處理諸如此類的情況時,請確保處理整個目標集。
另外,使用者特定的配置檔案也可以以類似的方式工作。這意味著,當團隊新增新開發人員時,以使用者為主鍵配置的不同環境的概要檔案就會生效。猜想這對新手來說是個有用的訓練,但如果用這種方式把它們扔給狼群就不好了。同樣,一定要考慮整個配置檔案集。
如何知道在構建過程中哪些概要檔案是有用的
確定活動概要檔案將幫助使用者瞭解在構建期間執行了哪些特定概要檔案。我們可以使用Maven Help外掛來判斷在構建過程中哪些配置檔案是有效的。
mvn help:active-profiles
我們弄個小例子來消化下,沿用之前的my-app專案,修改pom.xml檔案,新增如下內容:
~❯ vim ~/my-app/pom.xml <project> ... <profiles> <profile> <id>appserverConfig-dev</id> <activation> <property> <name>env</name> <value>dev</value> </property> </activation> <properties> <appserver.home>/path/to/dev/appserver</appserver.home> </properties> </profile> <profile> <id>appserverConfig-dev-2</id> <activation> <property> <name>env</name> <value>dev-2</value> </property> </activation> <properties> <appserver.home>/path/to/another/dev/appserver2</appserver.home> </properties> </profile> </profiles> .. </project>
執行如下命令:
~❯ cd my-app/ ~❯ mvn help:active-profiles -Denv=dev 輸出將會包含如下內容: The following profiles are active: - appserverConfig-dev (source: com.mycompany.app:my-app:1.0-SNAPSHOT)
從上面的結果可以看出profile的ID,以及其來源。
下面我們在settings.xml檔案中聲明瞭一個概要檔案:
~❯ vim ~/.m2/settings.xml <settings> ... <profiles> <profile> <id>appserverConfig</id> <properties> <appserver.home>/path/to/appserver</appserver.home> </properties> </profile> </profiles> <activeProfiles> <activeProfile>appserverConfig</activeProfile> </activeProfiles> ... </settings>
測試下:
~❯ cd my-app/ ~/my-app❯ mvn help:active-profiles 輸出將會包含如下內容: The following profiles are active: - appserverConfig (source: external)
如上所示,輸出正式settings.xml中配置的profile ID,而且source的值變成external,表示是外部配置的。
如果在setting.xml中設定了預設啟用的配置檔案,但是pom檔案中也激活了配置檔案,哪個配置檔案會對構建產生影響呢:
~❯ cd my-app/ ~/my-app❯ mvn help:active-profiles -P appserverConfig-dev 輸出將包含如下內容: The following profiles are active: - appserverConfig (source: external) - appserverConfig-dev (source: com.mycompany.app:my-app:1.0-SNAPSHOT)
儘管它列出了兩個活動概要檔案,但我們不確定其中哪個已經應用。要檢視對構建執行的影響:
~❯ cd my-app/ ~/my-app❯mvn help:effective-pom -P appserverConfig-dev 輸出包含如下內容: ... <properties> <appserver.home>/path/to/appserver</appserver.home> </properties>
這會將此構建配置的有效POM列印到控制檯。 從輸出可以看出<appserver.home>
的值是settings.xml中設定的。所以settings.xml比pom.xml中配置的概要檔案擁有更高的優先順序。
通過命令列引數-Doutput=effective-pom.xml,可以將上述輸出內容重定向到指定的檔案中。
命名約定
到目前為止,我們已經注意到概要檔案是解決針對不同目標環境的不同構建配置需求問題的一種自然的方法。上面,我們討論瞭解決這種情況的“自然配置檔案集”的概念,以及考慮需要的整個配置檔案集的重要性。
然而,如何組織和管理該集合的演化問題也很重要。正如優秀的開發人員努力編寫自我文件化的程式碼一樣,重要的是概要ID能夠表示它們的預期用途。這樣做的一個好方法是使用公共系統屬性觸發器作為概要檔名稱的一部分。這可能導致諸如env-dev、env-test和env-prod之類的名稱用於由系統屬性env觸發的概要檔案。這樣的系統在如何啟用針對特定環境的構建方面留下了非常直觀的提示。因此,要啟用測試環境的構建,只需要通過發出以下命令來啟用env-test:
mvn -Denv=test <phase>