文章目录
0.<Maven实战>
- 相关Maven实战电子书地址请自行百度。
1.关于Maven相关基础知识
- Maven基本坐标标签含义
- :定义项目属于哪个组,这个组往往和项目所在的组织和公式有关。必填项
- :定义当前Maven项目的唯一ID。必填项
- :指定当前开发项目的版本,(1.0.0-SNAPSHOT)SNAPSHOT意为快照,说明项目还在开发中。当项目稳定后即为relase包(1.0.0)(必填项)
- :scope为依赖范围,若依赖范围为test(测试包),则表示该依赖只对测试环境(test)有效。换句话说,在测试环境引入Junit包是没有问题的,但是在正常src/main引入就会编译错误。ps:如果不声明依赖范围,那么默认值是complie,表示该依赖对主代码和测试均有效(必填项)
- :该元素定义了Maven项目的打包方式。如:war、jar(若不配置package,则默认jar),可选项。
- :该元素用来帮组定义构建输出的一些附属构件。ps:不能直接定义项目的classifier,因为附属构件不是项目默认直接生成的,而是由附加的插件帮助生成(所以该项为可选项)
- :可选依赖,true or false,理想情况下是不会选择可选传递依赖的,因为这是违反单一原则的,单个类就应该只有一个职责。
- Maven打包相关流程
- mvn clean compile(编译)->mvn clean test(测试)->mvn clean package(打包)->mvn clean install(安装到本地仓库)
- Maven生成项目骨架(快速生成maven项目)
- $:mvn artchetype:generate
- 紧接着Maven会提示输入要创建的项目的groupId、artifactId、version、package,对应输入即可。
2.Maven项目实战
2.1 外观(Facade)模式:
- 外观模式是一种非常简单的模式,简单到我们经常都会使用,比如对于类A和B,如果两者需要交互,经过一定的处理过程才能实现某一个具体的功能,那么我们可以将这个处理的过程定义为一个新的类,然后在这个类里面将类A和B的处理步骤整合在一起,对于外界我们只暴露新的类中的这个接口,这样代码的复用性就非常的好了,可以将这些代码作为组件去让其他程序去使用,这在我们的开发之中是非常常见的
2.2 关于依赖范围详解:
- Maven项目在不同环境有不同的classpath(编译classpath、测试classpath、运行classpath)。那么依赖范围就是用来控制这三种不同的classpath的关系。
- 具体的依赖范围如下:
- compile:编译依赖范围(默认)。使用此依赖范围,则对于三种不同环境的classpath均有效。(最典型的例子:spring-core,它在三种环境都需要使用该依赖)
- test:测试依赖范围。使用此依赖范围,只对测试环境classpath有效,在编译主代码或者运行项目将无法使用此依赖。(最典型的例子:Junit,它只有在编译测试代码和运行测试代码的时候才需要)
- provided:已提供依赖范围。仅对编译和测试环境的classpath有效,但在运行时无效。(最典型的例子:servlet-api,编译和测试环境有效,但在运行的时候,由于容器已经提供了,就不需要Maven重复引入了)
- runtime:运行时依赖范围。对于测试和运行classpath有效,但在编译主代码无效。(典型的例子:JDBC驱动,项目编译时只需要JDBC的接口,只有在测试和运行时才需要实现上述接口的DJBC驱动)
- system:系统依赖范围(同provided)。使用system必须显示指定依赖文件路径,且此类依赖不是由Maven解析的,而且与本机系统绑定,可能构成不可移植性,建议谨慎使用。
- import:导入依赖范围。对三种环境不会产生实际影响。
2.3 传递性依赖
- 何为传递性依赖?
- 考虑一个给予Spring Framework的项目,如果不使用Maven,那么在项目中就需要手动下载相关依赖。由于Spring Framework又会依赖其他开源类库,因此实际下载其他相关的依赖。这么做往往就引入了很多不必要的依赖。
- 例如:xx项目有一个compile范围的spring-core依赖,spring-core有一个compile范围的commons-logging依赖,那么commons-logging就会成为xx项目的compile依赖范围,commons-logging是xx项目的一个传递性依赖,过程:
xx项目->spring-core-commons-logging | | --------------->--------------
- 传递性依赖范围:
- 假如A依赖B,B依赖C,我们说A对B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围,如下图:
第一\第二 compile test provided runtime comile compile - - runtime test test - - test provided provided - provided provided runtime runtime - - runtime - 从上图我们可以看出,第一直接依赖决定相关依赖范围。
- 假如A依赖B,B依赖C,我们说A对B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围,如下图:
- 依赖调解
- 例如:xx项目有两条这样的依赖关系:A->B->C->X(1.0),A->D->X(2.0),X是A的传递性依赖,但是两条依赖路径上有两个版本的X,那么哪个X会被Maven解析使用呢?显然,两个都解析肯定是不对的,因为那会造成依赖重复,因此必须选择一个。Maven依赖调解的第一原则:路径最近者优先。前者路径的长度为3,后者的路径长度为2,因此X(2.0)会被解析使用。
- 例如:A-B->X(1.0),A->B->X(2.0),该怎么选?Maven从2.8.9开始,定义了依赖调解的第二原则:第一声明者优先,在依赖路径长度相等的前提下,在POM中依赖声明的顺序解决了谁会被解析水用,顺序最靠前的那个依赖优胜。
- 可选依赖
- A->B,B->X(可选),B->Y(可选呢),根据传递性依赖的定义,由于X和Y是可选的,传递将不会得以依赖,因此X和Y将不会对A有任何影响。
2.4 排除依赖
- 传递性依赖带来简便的同时,也带来麻烦。例如:xx项目有一个第三方依赖,而这个第三方依赖由于某些原因依赖了另外一个类库的SNAPSHOT的不稳定性会直接影响当前的项目。这时就需要排除掉该SNAPSHOT,并且在当前项目中声明该类库的某个正式发布的版本。
- 例如:项目A依赖项目,但是由于一些原因,不想引入传递性依赖C,而是自己显示地的声明对项目C(1.1.0)版本的依赖。代码如下:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.juvernxu.mvnbook</groupId> <artifactId>project-a</artifactId> <version>1.0.0</version> <dependencies> <dependency> <group>com.juvenxu.mvnbook</group> <artifactId>project-b</artifactId> <version>1.0.0</versionn> <!--单独排除不稳定的依赖SNAPSHOT--> <exclusions> <exclusion> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-c</artifactId> </exclusion> </exclusions> </dependency> <!--单独声明对C的依赖--> <dpendency> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-c</artifactId> <version>1.1.0</version> </dependency> </dependencies> </project>
- ps:需要注意的是,声明exclusion的时候只需要groupId和artifactId,而不需要verision元素,这是因为在Maven中只需要groupId和artifactId即可唯一定位依赖图中的某个依赖。换句话说,在maven解析后,不可能出现groupId和artifactId相同,但是version不同的两个依赖。
2.5 归类依赖
- 对于POM的统一管理,方便散落在各module的依赖管理。例如:对spring Framework升级,只需要修改一次地方即可,采用如下代码:
-
<project> <groupId>xx</groupId> <artifactId>xxx</artifactId> <version>1.0.0</version> <properties> <springframework.version>2.5.0</springframework.version> </properties> <dependencies> <dependency> <groupId>com.springframework</groupId> <artifactId>spring-xx-xx</artifactId> <version>${springframework.version}</version> </depedency> </dependencies> </project>
2.6 优化依赖
- 已经被Maven解析依赖即可用于工作(已解析依赖),可以通过如下命令查看当前项目的已解析依赖。解析后构成依赖树,通过依赖树就可以清楚看看到某个依赖是由哪条路径引入的,可以通过查命令查看依赖树。有了前两者可以帮助我们了解项目的依赖,同时也可以通过maven的分析工具来分析依赖。
// 列出已解析的依赖 $ mvn dependency:list // 查看依赖树 $ mvn dependency:tree // 分析依赖 $ mvn dependency:analyze
3.Maven仓库
3.1 Maven仓库分类
- Maven仓库分为本地仓库和远程仓库。具体结构如下图
-
|-Maven仓库 |--本地仓库(默认路径:~/.m2/repository) |--远程仓库 |----中央仓库 |----私服 |----其他公共仓库
3.2 中央仓库和本地仓库
- 本地仓库需要将远程仓库的jar同步下载至本地,Maven解析时先从本地仓库搜寻,如果没有。则从远程的进行搜寻,下载至本地。例如采用如下命令:
$ mvn clean install
- 中央仓库包含绝大多开源jar,保证了Maven开箱即用。中央仓库的配置仓库认证信息 ,在具体的settings.xml文件进行配置。配置完毕后,如何将本地项目构建至远程仓库(供其他人方便访问)呢?可采用如下命令:
$ mvn clean deploy
4.生命周期和插件
- Maven的生命周期就是为了对所有的构建过程进行抽象和统一。Maven从大量的项目和构建呢工具中学习和反思,然后总结了一套高度完善、易拓展的生命周期。这个生命周期包含:
- 清理
- 初始化
- 编译
- 测试
- 打包
- 集成测试
- 验证
- 部署
- 站点
4.1 三套生命周期
- Maven拥有三套相互独立的生命周期,它们分别为:clean、default、site。clean的生命周期的目的是清理项目,default的目的是构建项目,而site的的目的是建立项目站点。
- clean生命周期
- pre-clean
- clean
- post-clean
- default生命周期
- validate
- initialize
- generate-sources
- process-sources
- generate-resources
- process-resources
- compile
- process-classes
- generate-test-sources
- process-test-sources
- generate-test-resources
- process-test-resouurces
- test-compile
- process-test-classes
- test
- prepare-package
- package
- pre-integration-test
- integration-test
- post-integration-test
- verify
- install
- deploy
- site生命周期
- pre-site
- site
- post-site
- site-deploy
4.2 命令行与生命周期
- $ mvn clean:实际执行的阶段为clean生命周期的pre-clean和clean阶段
- $ mvn test:该命令调用default生命周期的test阶段。实际执行的阶段为default生命周期的validate、intialize,直到test的所有阶段。
- $ mvn clean install install:实际执行阶段为clean生命周期的pre-clean、clean阶段,以及default生命周期从validate到install的所有阶段。该命令结合两个生命周期,在执行真正的项目构建之前清理项目是一个很好的实践。
- $ mvn clean deploy site-deploy:实际包含了三个生命周期整个阶段。
- ==Ps:==由于Maven中主要的生命周期阶段并不多,而常用的Maven命令实际都是基于这些阶段简单组合而成的,因此只要对Maven生命周期又一个基本的理解,就可以熟练的使用Maven命令。
5.聚合和继承
- 当把Maven应用到实际项目中时,需要将项目分成不同的模块,例如:account-email、account-persist等模块。Maven的聚合特性能够把项目的各个模块聚合在一起构建,而Maven的继承特性能够帮助抽取各个模块相同的依赖和插件等配置,在简化POM的同时,还能促进各个模块配置的一致性。
5.1 聚合
- 针对例子中的两个模块:account-email、account-persist,我们的需求是想要一次构建两个项目,而不是到两个模块下分别执行mvn命令。Maven聚合(多模块)这一特性就是为了该需求服务的。
- 为了能使一条命令就能构建:account-email、account-persist两个模块,我们需要做如下操作:
- 1.创建额外的聚合模块:account-aggregator的模块,然后通过该模块构建整个项目的所有模块。
- 2.account-aggregator本身作为一个Maven项目,它必须由自己的POM。不过作为一个聚合项目,其POM又有特殊的地方,其POM配置如下:
<project xmlns:xsi="xxx"> <modelVersion>4.0.0</modelVersion> <group>com.juvenx.mvnbook.account</group> <artifactId>account-aggregator</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Account Aggregator</name> <modules> <module>account-email</module> <module>account-persist</module> </modules> </project>
- 3.上述POM依旧使用了共同的groupId、artifactId为独立的accouunt-aggregator,其版本与两个模块一致,为1.0.0-SNAPSHOT。不同的是两个模块没有使用,即默认值为jar。对于聚合模块来说,其打包方式packaging的值必须为pom,否则无法构建。
- 4.为了方便用户构建项目,通常将聚合模块(aggrator)放在项目的最顶层,其他抹开则走位聚合模块的子目录存在,这样当用户得到源码时,第一眼就能发现的时聚合模块的POM,而不用从多个模块中去寻找聚合模块来构建整个项目。
- 5.聚合模块的父子目录结构和平行结构如下:
// 聚合模块的父子目录 |-accounnnt-aggregator |--account-email |---src |---target |---pom.xml |--account-persist |---src |---target |---pom.xml |--pom.xml // 平行结构 |-account-aggreagtor |--pom.xml |-account-email |--src |--target |--pom.xml |-account-persist |--src |--target |--pom.xml
- 6.如果使用平行结构的话,聚合模块的POM也需要相应的修改,如下:
<modules> <module>../account-email</module> <module>../account-persist</module> </modules>
- 7.具体mvn执行:$ mvn clean install
|-Building Account Aggregator 1.0.0-SNAOSHOT |-Building Account Email 1.0.0-SNAPSHOT |-Building Account Persist 1.0.0-SNAPSHOT |... |-Reactor Summary: |-Account Aggregator-----SUCCESS[1.0S] |-Account Email----------SUCCESS[1.0S] |-Account Persist--------SUCCESS[1.0S] |... |-BUILED SUECCESS
- 8.总结:Maven会首先解析聚合模块的POM,分析要构建的模块,并计算出一个反应堆构建顺序(Reactor Summary),然后根据整个顺序依次构建各个模块。
5.2 继承
- 对于聚合我们已经知道可以通过一条命令同时构建account-email、account-persist两个模块,不过仅仅解决了多模块的Maven项目的一个问题,我们还需要处理对于相同的maven依赖处理的问题。
- 通过对account-email、account-persist两个模块进行对比,我们可以发现其中有相同的依赖包:spring-core、spring-beans、spring-context等依赖,大量的重复必然是没有必要的,我们可以通过类似面向对象的继承原理来消除一定的重复。
- 父模块:account-parent
- 1.类似面向对象设计中,建立一种父子结构,然后在父类中声明一些字段和方法供子类继承,这样就可以做到“一处声明,多处使用”。
- 2.父POM如下,父POM使用了与其他模块一致的groupId和version,使用artifactId为account parent表示这是一个父模块。它的packagiing必须为pom,聚合模块。作为父模块,其打包类型也必须为pom。
<project > <modelVersion>4.0.0</modelVersion> <group>com.juvenx.mvnbook.account</group> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Account Parent</name> </project>
- 3.子POM继承父POM,例如:account-email的pom如下。其中parent元素声明父模块,parent下的子元素groupId,artifactId和version追定了父模块的坐标,这三个元素是必须的。当项目构建时,Maven会首先根据relativePath检查父POM,如果找不到,再从那个本地仓库查找。relativePom的默认值:…/pom.xml,即Maven默认父POM在上一层目录下。
<project xmlns:xsi="xxx"> <moduleVersion>4.0.0</moduleVersion> <parent> <group>com.juvenx.mvnbook.account</group> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <relativePath>../account-email/pom.xml</relativePath> </parent> <artifactId>account-email</artifactId> <name>Account Emil</name> <dependencies> ... </dependencies> <build> <plugins> ... </plugins> </build> </project>
- 4.从account-email的pom可以看出,父子模块使用了相同的groupId和version,如果遇到子模块需要使用父模块不一样的groupId或version时。那么需要用户自己在子模块中显示声明。对于arctifactId元素来说,子模块应该显示声明,一方面,如果完全继groupId、artifactId和version,会造成坐标冲突;另一方面,即使使用不同的groupId和version,同样的artifactId也容易造成混淆。
- 5.整合parent和aggregator模块,如下:
<project xmlns:xsi="xxx"> <modelVersion>4.0.0</modelVersion> <group>com.juvenx.mvnbook.account</group> <artifactId>account-aggregator</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Account Aggregator</name> <modules> <module>account-parent</module> <module>account-email</module> <module>account-persist</module> </modules> </project>
5.3 可继承的POM元素
- 从前面parent的pom可知,groupId和version是可以被继承的,那么还有以下元素可以被继承。
- groupId
- verison
- description:项目描述信息
- organizatiion:项目的组织信息
- inceptionYear:项目的创始年份
- url:项目的url
- developers:项目的开发者信息
- distributionManagement:项目的部署信配置
- issueManagement:项目的缺陷跟踪系统信息
- ciManagement:项目的持续继承系统信息
- scm:项目的版本控制系统信息
- mailingLists:项目的邮件列表信息
- properties:自定义的Maven属性
- dependencies:项目的依赖配置
- dependencyManagement:项目的依赖管理配置
- repositories:项目的仓库信息
- build:项目的目录等配置信息
- reporting:项目的输出目录配置、报告插件配置等
5.4 依赖管理
- 1.对于可继承的元素中包含了dependencies元素,说明依赖是可以被继承的。那么我们可以将这一特性应用到account-parent中。对于子模块中重复:spring-core、spring-context等依赖,可以将这些依赖配置放在account-parent中,这样两个子模块就可以移除这些依赖了,简化配置。
- 2.对于我们已经知道account-email、account-persist两个模块都包含:spring-core等依赖,但是我们无法确定以后的子模块是否需要这些依赖。假设将来项目中增加一个account-util模块,该模块只是提供一些简单帮助工具,与springframework完全无关,难道也要让它依赖spring-core、spring-context等依赖吗?
- 3.Maven提供dependencyManagement元素既能让子模块继承父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用,例如account-parent的配置如下:
<project >
<modelVersion>4.0.0</modelVersion>
<group>com.juvenx.mvnbook.account</group>
<artifactId>account-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Account Parent</name>
<properties>
<springframework.version>2.5.6</springframework.version>
<junit.verison>4.7</junit.version>
</properties>
<!--可被子模块继承的依赖(可选项)-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
- 4.以上account-parent的pom中引入dependencyManagement的声明既不会给account-parent引入依赖,也不会它的子模块引入依赖,不过这段配置是会被继承。
- 5.子模块account-email对应修改配置如下:
<properties>
<javax.mail.version>1.4.1</javax.mail.version>
<greenmail.version>1.3.1</greenmail.version>
<dependencies>
<!--从父模块继承的start-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--从父模块继承的end-->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>${javax.mail.version}</version>
</dependency>
<dependency>
<groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId>
<version>${greeenmail.version}</version>
</dependency>
</dependencies>
</properties>
- 6.account-email从account-parent中继承了dependencyManagement配置,完整的依赖声明已经包含在父POM中,子模块子需要配置简单的groupId和artifactId就能获取对应的依赖信息,从而引入正确的依赖。对于采用这种依赖管理机制似乎不能减少太多的POM配置,不过笔者还是强烈建议采用这几种方法。其主要原因:在父POM中声明之后,子模块在使用依赖的时候就无须声明版本,也就不会发生多个子模块使用依赖版本不一致的情况,这可以帮助降低依赖冲突的几率。
- 7.如果子模块不声明依赖的使用,即使该依赖在父POM中的dependencyManagement中声明了,也不会产生任何实际的效果,如下面的account-persist的pom配置。这里没有声明spring-context-support,那么该依赖就不会被引入,这正式dependencyManagement的灵活性所在。
<properties>
<dom4.version>1.6.1</dom4.version>
<dependencies>
<!--从父模块继承的start-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>${dom4.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--从父模块继承的end-->
</dependencies>
</properties>
5.5 依赖管理和import引入的关系
- 在前面介绍过依赖范围的时候提及过import的依赖范围,且该范围的依赖只在dependencyManagement元素下才有效果,使用该依赖范围的依赖通常指向一个POM,作用是将目标POM中的dependencyManagement配置导入并合并到当前POM的dependecyManagement元素中。例如想要在另一个模块A中使用跟模块B一样的dependecyManagement配置,除了复制配置或者继承这种两种方式之外,还可以使用import范围依赖将这一配置导入,代码如下:
<dependencyManagement>
<dependencies>
<dependency>
<!--引入account-parent的dependencyManagement的依赖配置-->
<groupId>com.juvenxu.mvnbook,account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependenctManagement>
5.6 插件管理
- 与Maven提供的dependecyManagement元素提供管理类型,Mavem也提供pluginManagement元素帮助管理插件。在该元素中配置的依赖不会造成实际的插件调用行为,同depencyManagement的作用范围类似。在父POM中配置pluginManagement代码如下:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1.</version>
<!--配置maven-source-plugin,将其jar-no-fork目标绑定到了verity生命周期阶段,以生成项目源码包-->
<executions>
<execution>
<id>attach-souurces</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</excutions>
</plugin>
</plugins>
</pluginManagement>
</build>
5.7 聚合与继承的关系
- 聚合:它知道有哪些模块被聚合了,但被聚合的模块不知道这个聚合模块的存在。
- 继承:对于继承关系的父POM来说,它不知道有哪些子模块继承于它,但那些子模块都必须要知道自己的父POM是什么。
- 两者的关系图如下:
|--> 被聚合A
聚合模块-|--> 被聚合B
|--> 被聚合C
子模块A-->|
子模块B-->|-父模块
子模块C-->|
- 在实际项目开发中,往往会发现一个POM即是聚合POM、又是父POM,这么做就是为了方便。例如可以将account-aggregator和account-parent合并成一个新的account-parent,其POM配置如下:
<project xmlns="xx">
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>4.0.0</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Account Parent</name>
<modules>
<module>account-email</module>
<module>account-persist</module>
</modules>
<properties>
<spring.framework.version>2.5.6</spring.framwork.version>
<junit.version>4.7</junit.version>
</properties>
<dependencyManagement>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.pl ugins</groupId>
<artifactId>maven-source-plu gin</artifactId>
<version>2.1.1.</version>
<!--配置maven-source-plugin, 将其jar-no-fork目标绑定到了 verity生命周期阶段,以生成项 目源码包-->
<executions>
<execution>
<id>attach-souurces</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</excutions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
- 子模块的修改配置,Maven默认能识别父模块位置为上一层模块。因此当父模块在上级目录时不再需要relativePath。
<parent>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<artifactId>account-email</artifactId>
<name>Account Email</name>
</parent>
5.8 约定大于配置
- Maven会假定用户的项目目录结构如下:
- 源目录:src/main/java/
- 编译输出目录:target/classes/
- 打包方式:jar
- 包输出目录:target/
5.9 反应堆
- 在多模块的Maven项目中,反应堆(Reactor)是指所有模块组成的一个构建结构,对于单模块的项目,反应堆就是该模块本身,但与对多模块的项目而言,反应堆就包括了个模块之间的继承与依赖的关系,从而能够自动计算出合理的模块构建顺序。
- 反应堆顺序:根据account-email、account-persist、account-aggregator、account-parent的构建顺序如下:
| account-aggregator
| account-email
| account-persist
|
| account-parent
\|/
<------------------------
- 首先构建:account-aggregator(无依赖关系)、account-parent、account-email、account-persist。即模块间的依赖关系会将反应堆构成一个有向非循环图,各模块是该图的节点,依赖关系构成了有向边,这个图不允许出现循环。因此,当出现模块A依赖模块B,而模块B又依赖A的情况,Maven就会报错。
5.10 裁剪反应堆
- 一般来说,用户会选择构建整个项目或者选择构建单个模块,但有些时候,用户会想要仅仅构建完整反应堆中的某些模块,换句话说,用户需要实时的裁剪反应堆。Maven提供了相关命令:
- -am,–also-make 同时构建所列模块的依赖模块
- -amd -also-make-dependents 构建指定的模块,模块间用逗号分隔
$ mvn clean install -pl account-email -amd
- -pl,–projects 构建指定的模块,模块间用逗号分隔
$ mvn clean install -pl account-email,account-persist
- -rf resume-form 从指定的模块回复反应堆
$ mvn clean install -rf account-email
- 以上命令行可组合使用,例如在构建account-parent时移除account-email模块
$ mvn clean install -pl account-parent -rf account-email