Maven的Java外掛開發指南
原文地址 譯者:劉小劉
介紹
本指南的目標是幫助使用者使用Java開發Maven外掛。
重要宣告:外掛命名規範和Apache Maven商標
通常將你的外掛命名為<yourplugin>-maven-plugin。
強烈反對命名為maven-<yourplugin>-plugin(maven在外掛名開頭),因為這是Apache Maven組織為官方Maven外掛保留的命名格式,用org.apache.maven.plugins作為組id。使用此命名是對Apache Maven商標的侵權。
你的第一個外掛
這一部分我們將構建一個不帶引數、執行時輸出一行資訊的簡單外掛,會覆蓋建立外掛專案的基本操作、Java Mojo的核心內容,以及執行Mojo的兩種方式。
你的第一個Mojo
最簡單地,一個Java Mojo只包含一個類。不需要像EJB有多個類,儘管包含一組相似Mojo的外掛很可能提取一個抽象基類以合併公共程式碼。
在程式碼樹中搜索Mojo時,plugin-tools尋找帶有@Mojo形式的Java5註解的類或帶有goal的javadoc型註解。所有帶有這樣註解的類包含在外掛配置檔案中。
一個簡單的Mojo
下面是一個簡單的無引數Mojo類,它可能是最簡單的Mojo了。程式碼示例後面是各部分的說明。
package sample.plugin; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Mojo; /** * Says "Hi" to the user. * */ @Mojo( name = "sayhi") public class GreetingMojo extends AbstractMojo { public void execute() throws MojoExecutionException { getLog().info( "Hello, world." ); } }
- 類org.apache.maven.plugin.AbstractMojo提供了實現一個Mojo需要的大部分基礎功能,除了execute方法。
- “@Mojo”註解是必須的,它控制了Mojo何時和怎樣被執行
- execute方法可以丟擲兩種異常:
- org.apache.Maven.plugin.MojoExecutionException 如果發生了非預期的錯誤丟擲此異常,顯示”BUILD ERROR”資訊。
- org.apache.Maven.plugin.MojoFailureException 如果發生了預期內的錯誤(例如編譯錯誤)丟擲此異常,顯示“BUILD FAILURE”資訊。
- getLog方法(在AbstractMojo中定義)返回一個類似log4j的日誌物件,外掛可以用它輸出debug, info, warn, error級別的日誌。這個日誌是使用者可接受的顯示資訊的方式。檢視
專案定義
外掛的Mojo寫完後,就可以構建外掛了。外掛描述中需要設定以下幾項:
groupId | 外掛的組id,應當與Mojo包名的共同字首一致 |
artifactId | 外掛名 |
version | 外掛版本 |
packaging | 應設為”maven-plugin” |
dependencies | 必須宣告對Maven Plugin Tools API的依賴以解析”AbstractMojo”和相關類 |
下面是示例Mojo的pom示例,引數按上表要求設定:
<project> <modelVersion>4.0.0</modelVersion> <groupId>sample.plugin</groupId> <artifactId>hello-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <packaging>maven-plugin</packaging> <name>Sample Parameter-less Maven Plugin</name> <dependencies> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>3.0</version> </dependency> <!-- dependencies to annotations --> <dependency> <groupId>org.apache.maven.plugin-tools</groupId> <artifactId>maven-plugin-annotations</artifactId> <version>3.4</version> <scope>provided</scope> </dependency> </dependencies> </project>
構建目標
在maven-plugin中定義了很少幾個目標作為標準構建生命週期的一部分:
compile | 編譯外掛的Java程式碼,構建外掛描述 |
test | 執行外掛的單元測試 |
package | 構建外掛jar包 |
install | 將外掛jar安裝到本地倉庫 |
deploy | 將外掛jar部署到遠端倉庫 |
執行你的第一個Mojo
執行新外掛的最直接方式是在命令列直接指定外掛目標。要這樣做,你需要在專案中這樣配置hello-Maven-plugin:
... <build> <plugins> <plugin> <groupId>sample.plugin</groupId> <artifactId>hello-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> </plugin> </plugins> </build> ...
然後,指定目標的全名:
mvn groupId:artifactId:version:goal
例如,要執行示例外掛的Mojo,在命令列輸入“mvn sample.plugin:hello-Maven-plugin:1.0-SNAPSHOT:sayhi”。
注:執行單一目標不需要指定版本。
縮短命令列
有幾種方式縮短輸入:
- 如果執行本地倉庫安裝的最新版外掛,可以省略版本號。使用”mvn sample.plugin:hello-Maven-plugin:sayhi”執行。
- 賦予外掛一個短字首,如mvn hello:sayhi。如果按照${prefix}-maven-plugin的命名方式(如果外掛是Apache Maven的官方外掛用maven-${prefix}-plugin)這是自動完成的。也可以通過額外的配置設定字首,更多資訊參見Introduction to Plugin Prefix Mapping。
- 最後,可以把外掛的組id加入預設搜尋的組id列表。這種方式需要在${user.home}/.m2/settings.xml中增加如下配置:
<pluginGroups> <pluginGroup>sample.plugin</pluginGroup> </pluginGroups>
此時,可以用”mvn hello:sayhi”執行Mojo了。
將Mojo關聯到構建生命週期
你也可以配置外掛關聯到構建生命週期某個特定階段的指定目標,示例如下:
<build> <plugins> <plugin> <groupId>sample.plugin</groupId> <artifactId>hello-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <executions> <execution> <phase>compile</phase> <goals> <goal>sayhi</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
這樣Java程式碼編譯時這個簡單Mojo就會執行。更多繫結Mojo到生命週期階段的資訊,參考 Build Lifecycle。
Mojo原型
利用Mojo原型建立新外掛專案:
mvn archetype:generate \ -DgroupId=sample.plugin \ -DartifactId=hello-maven-plugin \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DarchetypeArtifactId=maven-archetype-plugin
引數
不帶引數的Mojo一般沒太大用處,引數提供了以下重要功能:
- 提供了讓使用者調整外掛操作以適應應用的鉤子
- 提供一種不需要導航到物件就能提取POM元素值的簡單方式
定義Mojo引數
定義一個引數很簡單,只需要在Mojo中建立一個例項變數並增加恰當的註解。以下是帶引數的簡單Mojo示例:
/** * The greeting to display. */ @Parameter( property = "sayhi.greeting", defaultValue = "Hello World!" ) private String greeting;
註解之前的部分是引數說明。引數註解將變數宣告為Mojo引數。註解的defaultValue引數定義了變數的預設值。這個值可以包含引用專案變數的表示式,如”{project.version}”(在 “Parameter Expressions” document能看到更多)。property引數支援引用使用者在命令列中通過-D指定的系統屬性。
在專案中配置引數
作為外掛定義的一部分,外掛的引數值配置在Maven專案的pom.xml中完成,示例:
<plugin> <groupId>sample.plugin</groupId> <artifactId>hello-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <configuration> <greeting>Welcome</greeting> </configuration> </plugin>
在configuration部分,名為”greeting”的元素名是引數的名稱,元素的值”Welcome”是賦予引數的值。
注意:更多細節參見 Guide to Configuring Plugins。
單值引數型別
下面是可作為Mojo引數的簡單變數型別及其值在pom中的解釋規則。
Boolean
包括boolean和Boolean。讀配置時,讀到true字串時將引數設為true,讀到其它字串將引數設為false。例如:
/** * My boolean. */ @Parameter private boolean myBoolean;
<myBoolean>true</myBoolean>
定點數
包括byte, Byte, int, Integer, long, Long, short和Short。讀配置時,xml中的文字通過Integer.parseInt()或對應型別的valueOf()方法轉為整型數。字串必須是有效的十進位制整數,只包含數字0到9和可選的前置“-”表示負數。例如:
/** * My Integer. */ @Parameter private Integer myInteger;
<myInteger>10</myInteger>
浮點數
包含double, Double, float和Float。讀配置時,使用對應型別的valueOf()將xml中的文字轉為二進位制。文字可以是Java語言規範3.10.2節規定的任意格式。有效值示例如1.0和6.02E+23。
/** * My Double. */ @Parameter private Double myDouble;
<myDouble>1.0</myDouble>
日期
包括Date型別。讀配置時,xml中的文字按”yyyy-MM-dd HH:mm:ss.S a”(如”2005-10-06 2:22:55.1 PM”)或”yyyy-MM-dd HH:mm:ssa”(如”2005-10-06 2:22:55PM”)格式轉換。注意解析通過DateFormat.parse()完成,允許格式的少許偏差。這個方法盡力解析出日期和時間,即使格式不嚴格地匹配上述格式,例如:
/** * My Date. */ @Parameter private Date myDate;
<myDate>2005-10-06 2:22:55.1 PM</myDate>
檔案和目錄
包括File類。讀配置時,xml檔案中的文字被當做需要的檔案或目錄路徑。如果是相對路徑(不以/或驅動器字母如C:開頭),路徑是相對於POM所在目錄。例如:
/** * My File. */ @Parameter private File myFile;
<myFile>c:\temp</myFile>
URL
包含URL類。讀配置時,xml中的文字被當做URL。格式必須遵循RFC2396,看起來像web瀏覽器的URL(scheme://host:port/path/to/file)。轉換URL時對它的任何部分沒有限制。
/** * My URL. */ @Parameter private URL myURL;
<myURL>http://maven.apache.org</myURL>
普通文字
包含char, Character, StringBuffer和String。讀配置時,xml檔案中的文字被當做賦予引數的值。對char和Character引數,只使用文字的第一個字元。
列舉
也可以使用列舉型別變數。首先需要定義列舉型別,然後就可以在引數定義中使用了。
public enum Color { GREEN, RED, BLUE } /** * My Enum */ @Parameter private Color myColor;
在pom配置中可以使用這樣的列舉:
<myColor>GREEN</myColor>
也可以使用列舉型別的元素作為預設值,像下面這樣:
public enum Color { GREEN, RED, BLUE } /** * My Enum */ @Parameter(defaultValue = "GREEN") private Color myColor;
多值引數型別
下面介紹可以在Mojo中作為引數使用的各種複合物件,及它們在POM中的解釋規則。一般來說,儲存引數值(和引數值中的元素)的物件的型別是按以下步驟決定的(確定有效型別的第一步):
1. 如果XML元素包含implementation屬性,就使用它
2. 如果XML標籤包含”.”,嘗試解析為類的全名
3. 嘗試將XML標籤(首字母大寫)解析為Mojo所在包中的類
4. 對陣列,使用陣列的元素型別(例如,對String[]引數使用String型別);對集合和對映,使用Mojo配置中指定的類;使用String作為集合型別的entry和map的值的型別。
一旦元素的型別確定了,xml中的文字將被轉換為恰當的物件。
陣列
陣列引數通過多次指定來配置,例如:
/** * My Array. */ @Parameter private String[] myArray;
<myArray> <param>value1</param> <param>value2</param> </myArray>
集合
這一類包含所有實現java.util.Collection的類,如ArrayList或HashSet。這些引數也像陣列一樣通過多次指定來配置,例如:
/** * My List. */ @Parameter private List myList;
<myList> <param>value1</param> <param>value2</param> </myList>
關於獨立集合元素的對映細節,檢視 Mapping Lists.。
對映
這一類包含所有實現java.util.Map而未實現java.util.Properties的類,如HashMap。這些引數通過包含XML標籤來配置,形如<key>value</key>,例如:
/** * My Map. */ @Parameter private Map myMap;
<myMap> <key1>value1</key1> <key2>value2</key2> </myMap>
Properties
這一類包含所有實現java.util.Properties的對映。這些引數通過包含XML標籤配置,形如<property><name>myName</name> <value>myValue</value> </property>,例如:
/** * My Properties. */ @Parameter private Properties myProperties;
<myProperties> <property> <name>propertyName1</name> <value>propertyValue1</value> <property> <property> <name>propertyName2</name> <value>propertyValue2</value> <property> </myProperties>
其它物件型別
這一類包含所有未實現java.util.Map、java.util.Collection和java.util.Dictionary的類。例如:
/** * My Object. */ @Parameter private MyObject myObject;
<myObject> <myField>test</myField> </myObject>
使用setters
如果你想在Maven上下文之外重用Mojo,私有屬性對映是好選擇,對它的使用沒有限制。按上面的示例我們可以使用下劃線風格命名私有屬性,並提供setter供配置對映機制使用,Mojo看起來是這樣的:
public class MyQueryMojo extends AbstractMojo { @Parameter(property="url") private String _url; @Parameter(property="timeout") private int _timeout; @Parameter(property="options") private String[] _options; public void setUrl( String url ) { _url = url; } public void setTimeout( int timeout ) { _timeout = timeout; } public void setOptions( String[] options ) { _options = options; } public void execute() throws MojoExecutionException { ... } }
注意當屬性名和外掛配置中實際的引數名不一致時,對引數對應的屬性名的標記,這是用來告知Maven要使用的getter和setter。