小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

徹底理解maven

 甘甘灰 2019-06-17

前言

最近解決jar包沖突問題時,很頭疼,發(fā)現(xiàn)自己對maven的理解太膚淺了,很多細節(jié)都一知半解,于是最近又學習了一把maven,總結如下:

基本概念

maven有兩個最基本的概念: pom和lifecycle, 這里的pom不是maven構建過程中使用的pom文件,但他們之間有聯(lián)系。 pom全稱為Project Object Model, 簡單說就是要對構建的項目進行建模,將要構建的項目看成是一個對象Object,既然是一個對象,這個對象有哪些屬性呢? 在maven中一個項目使用唯一的坐標來表示,它包括groupId, artifactId, version, classifier, type(也叫packaging)這五部分,另外一個方面,一個項目肯定不是孤立存在的,可能會依賴其他項目,也就是說這個對象應該還有dependencies屬性,用PO表示構建對象,使用java代碼描述這個對象的話:

class PO{ private String groupId; private String artifactId; private String version; private String classifier; private String type; private Set dependencies;}

xml具有很強的表達能力,一個java對象可以用xml來描述,用xml表達上面這個java對象可以為:

PO>    groupId>groupId>    artifactId>artifactId>    version>version>    classifier>classifier>    type>type>    dependencies>        PO>PO>        PO>PO>        ...    dependencies>PO>

這個是不是和pom.xml很類似? 其實pom.xml就是PO對象的xml描述,上面這個PO定義還不完整,我們知道在java中類是可以繼承的,PO也有繼承關系,PO對象存在父類父類對象,用parent表示,它會繼承父類對象的所有屬性。 另一方面,一個項目可能根據不同職責分為多個模塊(module),所有模塊其實也就是一個單獨的項目,只不過這些項目會使用其父對象的一些屬性來進行構建。我們將這些新的屬性加到PO的定義中去:

class PO{ private String groupId; private String artifactId; private String version; private String classifier; private String type; private Set dependencies; private PO parent; private Set modules;}

再將這個定義用XML語言表示一下:

PO>    parent>parent>    groupId>groupId>    artifactId>artifactId>    version>version>    classifier>classifier>    type>type>    dependencies>        PO>PO>        PO>PO>        ...    dependencies>    modules>        ...    modules>PO>

是不是更像pom.xml了? pom.xml其實就是對PO對象的xml描述?。?/strong>

構建

項目的構建過程對應的是PO對象的build屬性,對應pom.xml中也就是元素中的內容,這里就有引入maven中第二個核心概念:Lifecycle。Lifecycle直譯過來就是生命周期。我們平常會接觸到哪些周期呢?一年中春夏秋冬就是一個周期。一個周期中可能分為多個階段,比如這里的春夏秋冬。在maven中一個構建過程就對應一個Lifecycle,這個Lifecycle也分為多個階段,每個階段叫做phase。你可能會問,那這個Lifecycle中包含多少個phase呢?一個標準的構建Lifecycle包含了如下的phase:

validate: 用于驗證項目的有效性和其項目所需要的內容是否具備initialize:初始化操作,比如創(chuàng)建一些構建所需要的目錄等。generate-sources:用于生成一些源代碼,這些源代碼在compile phase中需要使用到process-sources:對源代碼進行一些操作,例如過濾一些源代碼generate-resources:生成資源文件(這些文件將被包含在最后的輸入文件中)process-resources:對資源文件進行處理compile:對源代碼進行編譯process-classes:對編譯生成的文件進行處理generate-test-sources:生成測試用的源代碼process-test-sources:對生成的測試源代碼進行處理generate-test-resources:生成測試用的資源文件process-test-resources:對測試用的資源文件進行處理test-compile:對測試用的源代碼進行編譯process-test-classes:對測試源代碼編譯后的文件進行處理test:進行單元測試prepare-package:打包前置操作package:打包pre-integration-test:集成測試前置操作 integration-test:集成測試post-integration-test:集成測試后置操作install:將打包產物安裝到本地maven倉庫deploy:將打包產物安裝到遠程倉庫

在maven中,你執(zhí)行任何一個phase時,maven會將其之前的phase都執(zhí)行。例如 mvn install,那么maven會將deploy之外的所有phase按照他們出現(xiàn)的順序一次執(zhí)行。

Lifecycle還牽涉到另外一個非常重要的概念:goal。注意上面Lifecycle的定義,也就是說maven為程序的構建定義了一套規(guī)范流程:第一步需要validate,第二步需要initialize... ... compile,test,package,... ... install,deploy,但是并沒有定義每一個phase具體應該如何操作。這里的phase的作用有點類似于Java語言中的接口,只協(xié)商了一個契約,但并沒有定義具體的動作。比如說compile這個phase定義了在構建流程中需要經過編譯這個階段,但沒有定義應該怎么編譯(編譯的輸入是什么?用什么編譯javac/gcc?)。這里具體的動作就是由goal來定義,一個goal在maven中就是一個Mojo(Maven old java object)。Mojo抽象類中定義了一個execute()方法,一個goal的具體動作就是在execute()方法中實現(xiàn)。實現(xiàn)的Mojo類應該放在哪里呢?答案是maven plugin里,所謂的plugin其實也就是一個maven項目,只不過這個項目會引用maven的一些API,plugin項目也具備maven坐標。

在執(zhí)行具體的構建時,我們需要為lifecycle的每個phase都綁定一個goal,這樣才能夠在每個步驟執(zhí)行一些具體的動作。比如在lifecycle中有個compile phase規(guī)定了構建的流程需要經過編譯這個步驟,而maven-compile-plugin這個plugin有個compile goal就是用javac來將源文件編譯為class文件的,我們需要做的就是將compile這個phase和maven-compile-plugin中的compile這個goal進行綁定,這樣就可以實現(xiàn)Java源代碼的編譯了。那么有人就會問,在哪里綁定呢?答案是在pom.xml元素中配置即可。例如:

build>plugins>  plugin>    artifactId>maven-myquery-pluginartifactId>    version>1.0version>    executions>      execution>        id>execution1id>        phase>testphase>        configuration>          url>http://www./queryurl>          timeout>10timeout>          options>            option>oneoption>            option>twooption>            option>threeoption>          options>        configuration>        goals>          goal>querygoal>        goals>      execution>    executions>  plugin>plugins>build>

就將maven-myquery-plugin中的query這個goal綁定到了test這個phase,后續(xù)在maven執(zhí)行到test phase時就會執(zhí)行query goal。還有有人可能會問,我都沒有指定Java源文件的位置,編譯啥?這就引出了maven的design principle。在maven中,有一個非常著名的principle就是convention over configuration(約定優(yōu)于配置)。這一點和ant有非常大的區(qū)別,例如使用ant來進行編譯時,我們需要指定源文件的位置,輸出文件的位置,javac的位置,classpath... ...在maven中這些都是不需要,若沒有手動配置,maven默認從<項目根目錄>/src/main/java這個目錄去查找Java源文件,編譯后的class文件會保存在<項目根目錄>/target/classes目錄。在maven中,所有的PO都有一個根對象,就是Super POM。Super POM中定義了所有的默認的配置項,Super POM對應的pom.xml文件可以在maven安裝目錄下lib/maven-model-builder-3.0.3.jar:org/apache/maven/model/pom-4.0.0.xml中找到。用一張圖來表示maven Lifecycle,phase,goal之間的關系:

image.png

插件

上面我們提到,Maven 將所有項目的構建過程統(tǒng)一抽象成一套生命周期: 項目的清理、初始化、編譯、測試、打包、集成測試、驗證、部署和站點生成 … 幾乎所有項目的構建,都能映射到這一組生命周期上. 但生命周期是抽象的(Maven的生命周期本身是不做任何實際工作), 任務執(zhí)行(如編譯源代碼)均交由插件完成. 其中每個構建步驟都可以綁定一個或多個插件的目標,而且Maven為大多數構建步驟都編寫并綁定了默認插件.當用戶有特殊需要的時候, 也可以配置插件定制構建行為, 甚至自己編寫插件.

再說生命周期

Maven 擁有三套相互獨立的生命周期: clean、default 和 site, 而每個生命周期包含一些phase階段, 階段是有順序的, 并且后面的階段依賴于前面的階段. 而三套生命周期相互之間卻并沒有前后依賴關系, 即調用site周期內的某個phase階段并不會對clean產生任何影響.

clean
clean生命周期的目的是清理項目:

執(zhí)行如$ mvn clean;
default
default生命周期定義了真正構建時所需要執(zhí)行的所有步驟:

執(zhí)行如$ mvn clean install;

site
site生命周期的目的是建立和發(fā)布項目站點: Maven能夠基于POM所包含的信息,自動生成一個友好的站點,方便團隊交流和發(fā)布項目信息

執(zhí)行命令如$ mvn clean deploy site-deploy;

這三個lifecycle定義了其包含的phase。maven會在這三個lifecycle中匹配對應的phase。當執(zhí)行某個phase時,maven會依次執(zhí)行在這個phase之前的phase。

插件
生命周期的階段phase與插件的目標goal相互綁定, 用以完成實際的構建任務. 而對于插件本身, 為了能夠復用代碼,它往往能夠完成多個任務, 這些功能聚集在一個插件里,每個功能就是一個目標.
如:$ mvn compiler:compile: 冒號前是插件前綴, 后面是該插件目標(即: maven-compiler-plugin的compile目標).
而該目標綁定了default生命周期的compile階段: 他們的綁定能夠實現(xiàn)項目編譯的目的.

內置綁定

為了能讓用戶幾乎不用任何配置就能使用Maven構建項目, Maven 默認為一些核心的生命周期綁定了插件目標, 當用戶通過命令調用生命周期階段時, 對應的插件目標就會執(zhí)行相應的邏輯.

image.png
image.png

上圖只列出了打包方式為jar且擁有插件綁定關系的階段(packaging 定義了Maven項目打包方式, 通常打包方式與所生成構件擴展名對應,有jar(默認)、war、pom、maven-plugin等., 其他打包類型生命周期的默認綁定關系可參考: Built-in Lifecycle Bindings、Plugin Bindings for default Lifecycle Reference.

image.png

自定義綁定

除了內置綁定以外, 用戶還能夠自定義將某個插件目標綁定到生命周期的某個階段上. 如創(chuàng)建項目的源碼包, maven-source-plugin插件的jar-no-fork目標能夠將項目的主代碼打包成jar文件, 可以將其綁定到verify階段上:

build> plugins> plugin> groupId>org.apache.maven.pluginsgroupId> artifactId>maven-source-pluginartifactId> version>3.0.0version> executions> execution> id>attach-sourcesid> phase>verifyphase> goals> goal>jar-no-forkgoal> goals> execution> executions> plugin> plugins>build>

executions下每個execution子元素可以用來配置執(zhí)行一個任務.

聚合與繼承

Maven的聚合特性(aggregation)能夠使項目的多個模塊聚合在一起構建, 而繼承特性(inheritance)能夠幫助抽取各模塊相同的依賴、插件等配置,在簡化模塊配置的同時, 保持各模塊一致.

模塊聚合
隨著項目越來越復雜(需要解決的問題越來越多、功能越來越重), 我們更傾向于將一個項目劃分幾個模塊并行開發(fā), 如: 將feedcenter-push項目劃分為client、core和web三個模塊, 而我們又想一次構建所有模塊, 而不是針對各模塊分別執(zhí)行$ mvn命令. 于是就有了Maven的模塊聚合 -> 將feedcenter-push作為聚合模塊將其他模塊聚集到一起構建:

聚合POM
聚合模塊POM僅僅是幫助聚合其他模塊構建的工具, 本身并無實質內容:

project xmlns='http://maven./POM/4.0.0'         xmlns:xsi='http://www./2001/XMLSchema-instance'         xsi:schemaLocation='http://maven./POM/4.0.0         http://maven./xsd/maven-4.0.0.xsd'>    modelVersion>4.0.0modelVersion>    groupId>com.vdian.feedcentergroupId>    artifactId>feedcenter-pushartifactId>    packaging>pompackaging>    version>1.0.0.SNAPSHOTversion>    modules>        module>feedcenter-push-clientmodule>        module>feedcenter-push-coremodule>        module>feedcenter-push-webmodule>    modules>project>

通過在一個打包方式為pom的Maven項目中聲明任意數量的module以實現(xiàn)模塊聚合:

  • packaging: pom, 否則無法聚合構建.
  • modules: 實現(xiàn)聚合的核心,module值為被聚合模塊相對于聚合POM的相對路徑, 每個被聚合模塊下還各自包含有pom.xml、src/main/java、src/test/java等內容, 離開聚合POM也能夠獨立構建

元素的內容是jar,那么我們很好理解,也就是說這個項目最終會被打包成一個jar包,那元素為pom又是什么意思呢?從字面上的意思來看,這個項目將打包成一個pom。我們不妨去maven倉庫里去瞧瞧(前提是已經在項目下運行了mvn install命令)??梢园l(fā)現(xiàn)這個文件其實和項目中的pom.xml是同一個文件,這樣做的目的是什么呢?上面我們說過PO對象也是有繼承關系的,pom的作用就在這里,這就是maven中project inheritance的概念。當實際執(zhí)行maven命令的時候,會根據project inheritance關系對項目的pom.xml進行轉化,得到真正執(zhí)行時所用到的pom.xml,即所謂的effective pom,因此可以得到一個結論:所有元素為pom的項目其實并不會輸出一個可供外部使用,類似于jar包的東西。這類項目的作用有兩個:

管理子項目

  • 例如這里的api和biz就是echo項目的兩個module。若沒有echo這個父項目,我們需要到api和biz兩個項目下分別執(zhí)行mvn install命令才能完成整個構建過程,而有了echo這個父項目之后,我們只需在echo項目中執(zhí)行mvn install即可,maven會解析pom.xml,發(fā)現(xiàn)該項目有api和biz兩個module,它會分別到這兩個項目下去執(zhí)行mvn install命令。當module數量比較多的時候,能大大提高構建的效率。
    管理繼承屬性
  • 比如A和B都需要某個依賴,那么在父類項目的pom.xml中聲明即可,因為根據PO對象的繼承關系,A和B項目會繼承父類項目的依賴,這樣就可以減少一些重復的輸入。

effective pom包含了當前項目的PO對象,直到Super POM對應的PO對象中的信息。要看一個項目的effective pom,只需在項目中執(zhí)行

mvn help:effective-pom

命令即可查看。這里順帶說一句,有的同學可能不理解上面這個命令是什么意思。maven命令的語法為

mvn [options] [goal(s)] [phase(s)]

goal和phase。maven允許你執(zhí)行一個或者多個goals/phases。很明顯這面的命令help:effective-pom并不是一個phase,那么也就是說它是一個goal。對這個goal只不過是采用了縮寫的形式,其全稱是這樣的:

org.apache.maven.plugins:maven-help-plugin:2.2:effective-pom

以分號為分隔符,包含了groupId,artifactId,version,goal四部分。若groupId為org.apache.maven.plugins則可以使用上述的簡寫形式。也就是說

mvn help:effective-pommvn org.apache.maven.plugins:maven-help-plugin:2.2:effective-pom

是等價的,都是執(zhí)行了maven-help-plugin這個plugin中的effective-pom這個goal。

我們知道一個plugin中可以包含多個goal,goal可以綁定到lifecycle中的某一個phase,這樣在執(zhí)行這個phase的時候就會調用該goal。那那些沒有綁定到phase上的goal應該如何執(zhí)行呢?這就是 mvn [goal(s)]
這里的goal也就是官方文檔中所說的standalone goal,也就是說若一個plugin中的某個goal沒有和一個phase進行綁定,可以通過這種方式來執(zhí)行??赡苡械淖x者使用過

mvn dependency:tree

這條命令,這里其實就是單獨執(zhí)行一個goal,這個goal的作用是分析該工程的依賴并使用樹狀的形式打印出來。這里的dependency:tree其實是一個簡寫的形式,其完整形式是:

mvn org.apache.maven.plugins:maven-dependency-plugin:<版本號信息>:tree

也就是說單獨執(zhí)行一個goal的方式是:
mvn :::

每次都要敲這么長一串命令是很繁瑣的,因此才有了上述的簡寫的形式。maven規(guī)定了對于plugin的artifactId是如下兩種形式:
maven-{prefix}-plugin{prefix}-maven-plugin

的可以使用簡寫的方式${prefix}來表示一個plugin.

模塊繼承
在面向對象中, 可以通過類繼承實現(xiàn)復用. 在Maven中同樣也可以創(chuàng)建POM的父子結構, 通過在父POM中聲明一些配置供子POM繼承來實現(xiàn)復用與消除重復

父pom
與聚合類似, 父POM的打包方式也是pom, 因此可以繼續(xù)復用聚合模塊的POM(這也是在開發(fā)中常用的方式):

project xmlns='http://maven./POM/4.0.0' xmlns:xsi='http://www./2001/XMLSchema-instance' xsi:schemaLocation='http://maven./POM/4.0.0 http://maven./xsd/maven-4.0.0.xsd'> modelVersion>4.0.0modelVersion> groupId>com.vdian.feedcentergroupId> artifactId>feedcenter-pushartifactId> packaging>pompackaging> version>1.0.0.SNAPSHOTversion> modules> module>feedcenter-push-clientmodule> module>feedcenter-push-coremodule> module>feedcenter-push-webmodule> modules> properties> finalName>feedcenter-pushfinalName> warName>${finalName}.warwarName> spring.version>4.0.6.RELEASEspring.version> junit.version>4.12junit.version> project.build.sourceEncoding>UTF-8project.build.sourceEncoding> warExplodedDirectory>exploded/${warName}warExplodedDirectory> properties> dependencyManagement> dependencies> dependency> groupId>org.springframeworkgroupId> artifactId>spring-coreartifactId> version>${spring.version}version> dependency> dependency> groupId>org.springframeworkgroupId> artifactId>spring-beansartifactId> version>${spring.version}version> dependency> dependency> groupId>org.springframeworkgroupId> artifactId>spring-contextartifactId> version>${spring.version}version> dependency> dependency> groupId>junitgroupId> artifactId>junitartifactId> version>${junit.version}version> scope>testscope> dependency> dependencies> dependencyManagement> build> pluginManagement> plugins> plugin> groupId>org.apache.maven.pluginsgroupId> artifactId>maven-source-pluginartifactId> version>3.0.0version> executions> execution> id>attach-sourcesid> phase>verifyphase> goals> goal>jar-no-forkgoal> goals> execution> executions> plugin> plugins> pluginManagement> build>project>
  • dependencyManagement: 能讓子POM繼承父POM的配置的同時, 又能夠保證子模塊的靈活性: 在父POMdependencyManagement元素配置的依賴聲明不會實際引入子模塊中, 但能夠約束子模塊dependencies下的依賴的使用(子模塊只需配置groupId與artifactId, 見下).
  • pluginManagement: 與dependencyManagement類似, 配置的插件不會造成實際插件的調用行為, 只有當子POM中配置了相關plugin元素, 才會影響實際的插件行為.
project xmlns='http://maven./POM/4.0.0'         xmlns:xsi='http://www./2001/XMLSchema-instance'         xsi:schemaLocation='http://maven./POM/4.0.0         http://maven./xsd/maven-4.0.0.xsd'>    parent>        groupId>com.vdian.feedcentergroupId>        artifactId>feedcenter-pushartifactId>        version>1.0.0.SNAPSHOTversion>    parent>    modelVersion>4.0.0modelVersion>    artifactId>feedcenter-push-clientartifactId>    dependencies>        dependency>            groupId>org.springframeworkgroupId>            artifactId>spring-coreartifactId>        dependency>        dependency>            groupId>org.springframeworkgroupId>            artifactId>spring-contextartifactId>        dependency>        dependency>            groupId>org.springframeworkgroupId>            artifactId>spring-beansartifactId>        dependency>        dependency>            groupId>junitgroupId>            artifactId>junitartifactId>        dependency>    dependencies>    build>        plugins>            plugin>                groupId>org.apache.maven.pluginsgroupId>                artifactId>maven-source-pluginartifactId>            plugin>            plugin>                groupId>org.apache.maven.pluginsgroupId>                artifactId>maven-compiler-pluginartifactId>            plugin>        plugins>    build>project>

**元素繼承 **
可以看到, 子POM中并未定義模塊groupId與version, 這是因為子POM默認會從父POM繼承了如下元素:

groupId、versiondependenciesdevelopers and contributorsplugin lists (including reports)plugin executions with matching idsplugin configurationresources

因此所有的springframework都省去了version、junit還省去了scope, 而且插件還省去了executions與configuration配置, 因為完整的聲明已經包含在父POM中.

優(yōu)勢: 當依賴、插件的版本、配置等信息在父POM中聲明之后, 子模塊在使用時就無須聲明這些信息, 也就不會出現(xiàn)多個子模塊使用的依賴版本不一致的情況, 也就降低了依賴沖突的幾率. 另外如果子模塊不顯式聲明依賴與插件的使用, 即使已經在父POM的dependencyManagement、pluginManagement中配置了, 也不會產生實際的效果.

推薦: 模塊繼承與模塊聚合同時進行,這意味著, 你可以為你的所有模塊指定一個父工程, 同時父工程中可以指定其余的Maven模塊作為它的聚合模塊. 但需要遵循以下三條規(guī)則:

  • 在所有子POM中指定它們的父POM;
  • 將父POM的packaging值設為pom;
  • 在父POM中指定子模塊/子POM的目錄.

parent元素內還包含一個relativePath元素, 用于指定父POM的相對路徑, 默認../pom.xml

超級pom-約定優(yōu)先于配置

任何一個Maven項目都隱式地繼承自超級POM, 因此超級POM的大量配置都會被所有的Maven項目繼承, 這些配置也成為了Maven所提倡的約定.

project>  modelVersion>4.0.0modelVersion>    repositories>    repository>      id>centralid>      name>Central Repositoryname>      url>https://repo.maven./maven2url>      layout>defaultlayout>      snapshots>        enabled>falseenabled>      snapshots>    repository>  repositories>  pluginRepositories>    pluginRepository>      id>centralid>      name>Central Repositoryname>      url>https://repo.maven./maven2url>      layout>defaultlayout>      snapshots>        enabled>falseenabled>      snapshots>      releases>        updatePolicy>neverupdatePolicy>      releases>    pluginRepository>  pluginRepositories>    build>    directory>${project.basedir}/targetdirectory>    outputDirectory>${project.build.directory}/classesoutputDirectory>    finalName>${project.artifactId}-${project.version}finalName>    testOutputDirectory>${project.build.directory}/test-classestestOutputDirectory>    sourceDirectory>${project.basedir}/src/main/javasourceDirectory>    scriptSourceDirectory>${project.basedir}/src/main/scriptsscriptSourceDirectory>    testSourceDirectory>${project.basedir}/src/test/javatestSourceDirectory>    resources>      resource>        directory>${project.basedir}/src/main/resourcesdirectory>      resource>    resources>    testResources>      testResource>        directory>${project.basedir}/src/test/resourcesdirectory>      testResource>    testResources>        pluginManagement>                  plugins>        plugin>          artifactId>maven-antrun-pluginartifactId>          version>1.3version>        plugin>        plugin>          artifactId>maven-assembly-pluginartifactId>          version>2.2-beta-5version>        plugin>        plugin>          artifactId>maven-dependency-pluginartifactId>          version>2.8version>        plugin>        plugin>          artifactId>maven-release-pluginartifactId>          version>2.3.2version>        plugin>      plugins>    pluginManagement>  build>    reporting>    outputDirectory>${project.build.directory}/siteoutputDirectory>  reporting>    profiles>        profile>      id>release-profileid>      activation>        property>          name>performReleasename>          value>truevalue>        property>      activation>      build>        plugins>          plugin>            inherited>trueinherited>            artifactId>maven-source-pluginartifactId>            executions>              execution>                id>attach-sourcesid>                goals>                  goal>jargoal>                goals>              execution>            executions>          plugin>          plugin>            inherited>trueinherited>            artifactId>maven-javadoc-pluginartifactId>            executions>              execution>                id>attach-javadocsid>                goals>                  goal>jargoal>                goals>              execution>            executions>          plugin>          plugin>            inherited>trueinherited>            artifactId>maven-deploy-pluginartifactId>            configuration>              updateReleaseInfo>trueupdateReleaseInfo>            configuration>          plugin>        plugins>      build>    profile>  profiles>project>

Maven Plugin 開發(fā)

詳細代碼在maven plugin demo

  1. 創(chuàng)建plugin項目
mvn archetype:generate -DgroupId=com.fq.plugins -DartifactId=lc-maven-plugin -Dversion=0.0.1-SNAPSHOT -DarchetypeArtifactId=maven-archetype-plugin -DinteractiveMode=false -DarchetypeCatalog=internal

使用maven-archetype-plugin Archetype可以快速創(chuàng)建一個Maven插件項目。
** pom.xml **
插件本身也是Maven項目, 特殊之處在于packaging方式為maven-plugin:

project xmlns='http://maven./POM/4.0.0'         xmlns:xsi='http://www./2001/XMLSchema-instance'         xsi:schemaLocation='http://maven./POM/4.0.0         http://maven./maven-v4_0_0.xsd'>    modelVersion>4.0.0modelVersion>    groupId>com.fq.pluginsgroupId>    artifactId>lc-maven-pluginsartifactId>    packaging>maven-pluginpackaging>    version>0.0.1-SNAPSHOTversion>    dependencies>        dependency>            groupId>com.google.guavagroupId>            artifactId>guavaartifactId>            version>19.0version>        dependency>        dependency>            groupId>org.apache.mavengroupId>            artifactId>maven-plugin-apiartifactId>            version>3.3.3version>        dependency>        dependency>            groupId>org.apache.maven.plugin-toolsgroupId>            artifactId>maven-plugin-annotationsartifactId>            version>3.3version>        dependency>    dependencies>project>

maven-plugin 打包方式能控制Maven為其在生命周期階段綁定插件處理的相關目標.

  1. 編寫目標Mojo
@Mojo(name = 'lc', defaultPhase = LifecyclePhase.VERIFY)public class LCMavenMojo extends AbstractMojo { private static final List DEFAULT_FILES = Arrays.asList('java', 'xml', 'properties'); @Parameter(defaultValue = '${project.basedir}', readonly = true) private File baseDir; @Parameter(defaultValue = '${project.build.sourceDirectory}', readonly = true) private File srcDir; @Parameter(defaultValue = '${project.build.testSourceDirectory}', readonly = true) private File testSrcDir; @Parameter(defaultValue = '${project.build.resources}', readonly = true) private List resources; @Parameter(defaultValue = '${project.build.testResources}', readonly = true) private List testResources; @Parameter(property = 'lc.file.includes') private Set includes = new HashSet<>(); private Log logger = getLog(); @Override public void execute() throws MojoExecutionException, MojoFailureException { if (includes.isEmpty()) { logger.debug('includes/lc.file.includes is empty!'); includes.addAll(DEFAULT_FILES); } logger.info('includes: ' + includes); try { long lines = 0; lines += countDir(srcDir); lines += countDir(testSrcDir); for (Resource resource : resources) { lines += countDir(new File(resource.getDirectory())); } for (Resource resource : testResources) { lines += countDir(new File(resource.getDirectory())); } logger.info('total lines: ' + lines); } catch (IOException e) { logger.error('error: ', e); throw new MojoFailureException('execute failure: ', e); } } private LineProcessor lp = new LineProcessor() { private long line = 0; @Override public boolean processLine(String fileLine) throws IOException { if (!Strings.isNullOrEmpty(fileLine)) { ++this.line; } return true; } @Override public Long getResult() { long result = line; this.line = 0; return result; } }; private long countDir(File directory) throws IOException { long lines = 0; if (directory.exists()) { Set files = new HashSet<>(); collectFiles(files, directory); for (File file : files) { lines += CharStreams.readLines(new FileReader(file), lp); } String path = directory.getAbsolutePath().substring(baseDir.getAbsolutePath().length()); logger.info('path: ' + path + ', file count: ' + files.size() + ', total line: ' + lines); logger.info('\t-> files: ' + files.toString()); } return lines; } private void collectFiles(Set files, File file) { if (file.isFile()) { String fileName = file.getName(); int index = fileName.lastIndexOf('.'); if (index != -1 && includes.contains(fileName.substring(index + 1))) { files.add(file); } } else { File[] subFiles = file.listFiles(); for (int i = 0; subFiles != null && i < subfiles.length;="" ++i)="" {="" collectfiles(files,="" subfiles[i]);="" }="" }="">

@Parameter: 配置點, 提供Mojo的可配置參數. 大部分Maven插件及其目標都是可配置的, 通過配置點, 用戶可以自定義插件行為

plugin>    groupId>com.fq.pluginsgroupId>    artifactId>lc-maven-pluginsartifactId>    version>0.0.1-SNAPSHOTversion>    executions>        execution>            id>lcid>            phase>verifyphase>            goals>                goal>lcgoal>            goals>            configuration>                includes>                    include>javainclude>                    include>luainclude>                    include>jsoninclude>                    include>xmlinclude>                    include>propertiesinclude>                includes>            configuration>        execution>    executions>plugin>

execute(): 實際插件功能;
異常: execute()方法可以拋出以下兩種異常:
MojoExecutionException: Maven執(zhí)行目標遇到該異常會顯示 BUILD FAILURE 錯誤信息, 表示在運行期間發(fā)生了預期的錯誤;
MojoFailureException: 表示運行期間遇到了未預期的錯誤, 顯示 BUILD ERROR 信息

  1. 測試&執(zhí)行

通過mvn clean install將插件安裝到倉庫后, 就可將其配置到實際Maven項目中, 用于統(tǒng)計項目代碼了:

$ mvn com.fq.plugins:lc-maven-plugins:0.0.1-SNAPSHOT:lc

你可能注意到為了調用該插件的goal,我們需要給出該插件的所有坐標信息,包裹groupId, artifactId,version號,你可能之前已經執(zhí)行過'mvn eclipase:eclipase'或'mvn idea:idea'這樣簡潔的命令,讓我們也來將自己的插件調用變簡單一點。要通過簡單別名的方式調用Maven插件,我們需要做到以下兩點:

  1. 插件的artifactId應該遵循-maven-plugin或maven--plugin命名規(guī)則,對于本文中的插件,我們已經遵循了。
  2. 需要將插件的groupId放在Maven默認的插件搜尋范圍之內,默認情況下Maven只會在org.apache.maven.plugins和org.codehaus.mojo兩個groupId下搜索插件,要讓Maven同時搜索我們自己的groupId,我們需要在~/.m2/settings.xml中加入:
pluginGroups>         pluginGroup>com.fq.pluginspluginGroup>  pluginGroups>

在達到以上兩點之后,我們便可以通過以下命令來調用自己的插件了:
mvn lc:lc

要在別的項目中應用插件也是簡單的,我們只需要在該項目的pom.xml文件中使用上面標簽聲明該插件即可。

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多