Maven提高篇系列之(一)——多模块 vs 继承

 

这是一个Maven提高篇的系列,包含有以下文章:

  1. Maven提高篇系列之(一)——多模块 vs 继承
  2. Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例)
  3. Maven提高篇系列之(三)——使用自己的Repository(Nexus)
  4. Maven提高篇系列之(四)——使用Profile
  5. Maven提高篇系列之(五)——处理依赖冲突
  6. Maven提高篇系列之(六)——编写自己的Plugin(本系列完)

通常来说,在Maven的多模块工程中,都存在一个pom类型的工程作为根模块,该工程只包含一个pom.xml文件,在该文件中以模块(module)的形式声明它所包含的子模块,即多模块工程。在子模块的pom.xml文件中,又以parent的形式声明其所属的父模块,即继承。然而,这两种声明并不必同时存在,我们将在下文中讲到这其中的区别。

(一)创建Maven多模块工程

多模块的好处是你只需在根模块中执行Maven命令,Maven会分别在各个子模块中执行该命令,执行顺序通过Maven的Reactor机制决定。先来看创建Maven多模块工程的常规方法。在我们的示例工程中,存在一个父工程,它包含了两个子工程(模块),一个core模块,一个webapp模块,webapp模块依赖于core模块。这是一种很常见的工程划分方式,即core模块中包含了某个领域的核心业务逻辑,webapp模块通过调用core模块中服务类来创建前端网站。这样将核心业务逻辑和前端展现分离开来,如果之后决定开发另一套桌面应用程序,那么core模块是可以重用在桌面程序中。

首先通过Maven的Archetype插件创建一个父工程,即一个pom类型的Maven工程,其中只包含一个pom.xml文件:

mvn archetype:generate -DgroupId=me.davenkin -DartifactId=maven-multi-module -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=pom-root -DinteractiveMode=false

以上命令在当前目录下创建了一个名为maven-multi-module的目录,该目录便表示这个pom类型的父工程,在该目录只有一个pom.xml文件:

<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>

 <groupId>me.davenkin</groupId>

 <artifactId>maven-multi-module</artifactId>

 <version>1.0-SNAPSHOT</version>

 <packaging>pom</packaging>

 <name>maven-multi-module</name>

</project>

这个pom.xml非常简单,最值得一看的是其中的“<packaging>pom</packaging>”,表示该工程为pom类型。其他的Maven工程类型还有jar、war、ear等。

此时,父工程便创建好了,接下来我们创建core模块,由于core模块属于maven-multi-module模块,我们将工作目录切换到maven-multi-module目录下,创建core模块命令如下:

mvn archetype:generate -DgroupId=me.davenkin -DartifactId=core  -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

这里我们使用了Archetype插件的maven-archetype-quickstart,它创建一个jar类型的模块。此时,如果我们在打开maven-multi-module模块的pom.xml会发现,其中多了以下内容:

 <modules>

   <module>core</module>

 </modules>

这里的core即是我们刚才创建的core模块,再看看core模块中的pom.xml文件:

<?xml version="1.0"?>

<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

 <modelVersion>4.0.0</modelVersion>

 <parent>

   <artifactId>maven-multi-module</artifactId>

   <groupId>me.davenkin</groupId>

   <version>1.0-SNAPSHOT</version>

 </parent>

 <groupId>me.davenkin</groupId>

 <artifactId>core</artifactId>

 <version>1.0-SNAPSHOT</version>

 <name>core</name>

 <url>http://maven.apache.org</url>

 <dependencies>

   <dependency>

     <groupId>junit</groupId>

     <artifactId>junit</artifactId>

     <version>3.8.1</version>

     <scope>test</scope>

   </dependency>

 </dependencies>

</project>

请注意里面的  “<parent>...  </parent>”,它将maven-multi-module模块做为了自己的父模块。这里我们看出,当创建core模块时,Maven将自动识别出已经存在的maven-multi-module父模块,然后分别创建两个方向的指引关系,即在maven-multi-module模块中将core作为自己的子模块,在core模块中将maven-multi-module作为自己的父模块。要使Maven有这样的自动识别功能,我们需要在maven-multi-module目录下创建core模块(请参考前文),不然,core模块将是一个独立的模块,但是我们可以通过手动修改两个模块中的pom.xml文件来创建他们之间的父子关系,从而达到同样的目的。

依然在maven-multi-module目录下,通过与core相同的方法创建webapp模块:

mvn archetype:generate -DgroupId=me.davenkin -DartifactId=webapp -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

这里的maven-archetype-webapp表明Maven创建的是一个war类型的工程模块。此时再看maven-multi-module模块的pom.xml文件,其中的<modules>中多了一个webapp模块:

<modules>

   <module>core</module>

   <module>webapp</module>

 </modules>

而在webapp模块的pom.xml文件中,也以 “<parent>...  </parent>”的方式将maven-multi-module模块声明为自己的父模块,这些同样是得益于Maven自动识别的结果。

此时,在maven-multi-module目录下,我们执行以下命令完成整个工程的编译、打包和安装到本地Maven Repository的过程:

mvn clean install

(二)手动添加子模块之间的依赖关系

此时我们虽然创建了一个多模块的Maven工程,但是有两个问题我们依然没有解决:

(1)没有发挥Maven父模块的真正作用(配置共享)

(2)webapp模块对core模块的依赖关系尚未建立

针对(1),Maven父模块的作用本来是使子模块可以继承并覆盖父模块中的配置,比如dependency等,但是如果我们看看webapp和core模块中pom.xml文件,他们都声明了对Junit的依赖,而如果多个子模块都依赖于相同的类库,我们应该将这些依赖配置在父模块中,继承自父模块的子模块将自动获得这些依赖。所以接下来我们要做的便是:将webapp和core模块对junit的依赖删除,并将其迁移到父模块中。

对于(2),Maven在创建webapp模块时并不知道webapp依赖于core,所以这种依赖关系需要我们手动加入,在webapp模块的pom.xml中加入对core模块的依赖:

       <dependency>

           <groupId>me.davenkin</groupId>

           <artifactId>core</artifactId>

           <version>1.0-SNAPSHOT</version>

       </dependency>

此时再在maven-multi-module目录下执行 “mvn clean install”,Maven将根据自己的Reactor机制决定哪个模块应该先执行,哪个模块应该后执行。比如,这里的webapp模块依赖于core模块,那么Maven会先在core模块上执行“mvn clean install”,再在webapp模块上执行相同的命令。在webapp上执行“mvn clean install”时,由于core模块已经被安装到了本地的Repository中,webapp便可以顺利地找到所依赖的core模块。

总的来看,此时命令的执行顺序为maven-multi-module -> core -> webapp,先在maven-multi-module上执行是因为其他两个模块都将它作为父模块,即对它存在依赖关系,又由于core被webapp依赖,所以接下来在core上执行命令,最后在webapp上执行。

这里又有一个问题:为什么非得在maven-multi-module目录下执行 “mvn clean install”?答案是,并不是非得如此,只是你需要搞清楚Maven的工作机制。在maven-multi-module目录下执行,即是在父工程中执行,此时Maven知道父模块所包含的所有子模块,并会自动按照模块依赖关系处理执行顺序。如果只在子模块中执行,那么Maven并不知道它对其他模块的依赖关系。举个例子,当在webapp中执行 “mvn clean install”,Maven发现webapp自己依赖于core,此时Maven会在本地的Repository中去找core,如果存在,那么你很幸运,如果不存在,那么对不起,运行失败,说找不到core,因为Maven并不会先将core模块安装到本地Repository。此时你需要做的是,切换到core目录,执行“mvn clean install”将core模块安装到本地Repository,再切换回webapp目录,执行“mvn clean install”,万事才大吉。

多么繁琐的步骤,此时你应该能体会到在maven-multi-module下执行Maven命令的好处了吧。总结一下:在maven-multi-module下执行“mvn clean install”, Maven会在每个模块上执行该命令,然后又发现webapp依赖于core,此时他们之间有一个协调者(即父工程),它知道将core作为webapp的依赖,于是会先在core模块上执行“mvn clean install”,当在webapp上执行命令时,无论先前的core模块是否存在于本地Repository中,父工程都能够获取到core模块(如果不存在于本地Repository,它将现场编译core模块,再将其做为webapp的依赖,比如此时使用“mvn clean package”也是能够构建成功的),所以一切成功。

这里又牵扯到Maven如何查找依赖的问题,简单来说,Maven会先在本地Repository中查找依赖,如果依赖存在,则使用该依赖,如果不存在,则通过pom.xml中的Repository配置从远程下载依赖到本地Repository中。默认情况下,Maven将使用Maven Central Repository作为远端Repository。于是你又有问题了:“在pom.xml中为什么没有看到这样的配置信息啊?”原因在于,任何一个Maven工程都默认地继承自一个Super POM,Repository的配置信息便包含在其中。

(三)多模块 vs 继承

在文章一开始我们便提到,在Maven中,由多模块(Project Aggregation)和继承(Project Inheritance)关系并不必同时存在。

(1)如果保留webapp和core中对maven-multi-module的父关系声明,即保留 “<parent>...  </parent>”,而删除maven-multi-module中的子模块声明,即“<modules>...<modules>”,会发生什么情况?此时,整个工程已经不是一个多模块工程,而只是具有父子关系的多个工程集合。如果我们在maven-multi-module目录下执行“mvn clean install”,Maven只会在maven-multi-module本身上执行该命令,继而只会将maven-multi-module安装到本地Repository中,而不会在webapp和core模块上执行该命令,因为Maven根本就不知道这两个子模块的存在。另外,如果我们在webapp目录下执行相同的命令,由于由子到父的关系还存在,Maven会在本地的Repository中找到maven-multi-module的pom.xml文件和对core的依赖(当然前提是他们存在于本地的Repository中),然后顺利执行该命令。

这时,如果我们要发布webapp,那么我们需要先在maven-multi-module目录下执行“mvn clean install”将最新的父pom安装在本地Repository中,再在core目录下执行相同的命令将最新的core模块安装在本地Repository中,最后在webapp目录下执行相同的命令完成最终war包的安装。麻烦。

(2)如果保留maven-multi-module中的子模块声明,而删除webapp和core中对maven-multi-module的父关系声明,又会出现什么情况呢?此时整个工程只是一个多模块工程,而没有父子关系。Maven会正确处理模块之间的依赖关系,即在webapp模块上执行Maven命令之前,会先在core模块上执行该命令,但是由于core和webapp模块不再继承自maven-multi-module,对于每一个依赖,他们都需要自己声明,比如我们需要分别在webapp和core的pom.xml文件中声明对Junit依赖。

综上,多模块和父子关系是不同的。如果core和webapp只是在逻辑上属于同一个总工程,那么我们完全可以只声明模块关系,而不用声明父子关系。如果core和webapp分别处理两个不同的领域,但是它们又共享了很多,比如依赖等,那么我们可以将core和webapp分别继承自同一个父pom工程,而不必属于同一个工程下的子模块。更多解析请参考这里

转 :https://www.cnblogs.com/davenkin/p/advanced-maven-multi-module-vs-inheritance.html

Maven 梳理 -多模块 vs 继承的更多相关文章

  1. Maven提高篇系列之(一)——多模块 vs 继承

    这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...

  2. Maven学习(八)继承和聚合

    *聚合(多模块) 在一个项目中 往往有多个模块组成,例如有项目demo下面有a, b两个模块 为了能使用一条命令就能构建demo-a, demo-b两个模块, 需要创建一个额外的聚合模块, 然后通过该 ...

  3. SSH框架之一详解maven搭建多模块项目

    闲来无事,思量着自己搭建一个ssh框架,一来回顾熟悉一下ssh的内容,hibernate还就没用过了,生疏了都.二来整合一下,将其他掌握的和正在学习的框架核技术糅合到一起,就当是做一个demo练手了. ...

  4. Maven管理 划分模块

    转载地址:juvenshun.iteye.com/blog/305865 “分天下为三十六郡,郡置守,尉,监” —— <史记·秦始皇本纪> 所有用Maven管理的真实的项目都应该是分模块的 ...

  5. 使用Maven构建多模块项目

    [转] 使用Maven构建多模块项目 在平时的Javaweb项目开发中为了便于后期的维护,我们一般会进行分层开发,最常见的就是分为domain(域模型层).dao(数据库访问层).service(业务 ...

  6. Maven使用笔记(六)使用Maven进行多模块拆分

    模块拆分是Maven经常使用的功能,简单梳理一下如何使用Maven进行多模块拆分, 只做归纳总结,网上资料很多,不再一步一步实际创建和部署. >>建立Maven多模块项目 一个简单的Jav ...

  7. Maven管理多模块项目

    首先,我们要明确的多模块项目的含义,它是指一个应用中包含多个module.一般来说,一个应用单独部署成服务,只是打包的时候,maven会把各个module组合在一起.各模块一般单独打成jar放到lib ...

  8. Maven学习(2) - Maven构建多模块Java工程

    概述 项目开发时,通常会将项目分为多个模块进行开发,本文讨论如何用Maven构建多模块的Java工程. 软件环境 Java:1.6.0_26 Maven:3.1.1 OS:WindowXP SP3 项 ...

  9. Maven管理多模块应用

    穿越至目录: 从0开始,构建前后端分离应用 对于概念的一些理解 Maven的作用 管理模块之间的依赖:根据业务需求,系统会划分很多模块,这些模块彼此之间存在着依赖关系.比如系统管理模块依赖着文件上传模 ...

随机推荐

  1. ZooKeeper异步调用命令

    在ZooKeeper中,所有的同步调用命令,都会有一个相应的异步调用方法.异步调用能在一个单独线程中同时提交更多的命令,也能在一定程度上简化代码实现. 1 异步create方法 如创建zNode的命令 ...

  2. ‎CocosBuilder 学习笔记(1) CCBReader 解析.ccbi文件流程

    1. 简介 CocosBuilder是免费开源的Cocos2d UI编辑器. .ccb文件是CCB项目的原始文件. .ccbi文件是CCB项目发布后的生成的二进制文件.CCBReader可以快速通过该 ...

  3. Android定时锁屏功能实现(AlarmManager定时部分)

    菜鸟入坑记——第一篇 关键字:AlarmManager 一.AlarmManager简介: 参考网址:https://www.jianshu.com/p/8a2ce9d02640        参考网 ...

  4. VS Code 配置 Python 开发环境

    1.终端运行 Python2.安装 Python 插件3.查看.安装外部库4.代码补全工具5.代码检查工具5.1.pylint5.2.flake8 和 yapf 本文基于 VS Code 1.36.1 ...

  5. c#中的委托01

    delegate 是表示对具有特定参数列表和返回类型的方法的引用的类型. 在实例化委托时,你可以将其实例与任何具有兼容签名和返回类型的方法相关联. 你可以通过委托实例调用方法. 委托用于将方法作为参数 ...

  6. CodeForces - 445B - DZY Loves Chemistry-转化问题

    传送门:http://codeforces.com/problemset/problem/445/B 参考:https://blog.csdn.net/littlewhite520/article/d ...

  7. hihocoder 1523 数组重排2+思维

    参考:http://blog.csdn.net/howardemily/article/details/74991367 题意:每次可以移动数组中的一个数到数组的最左边,问最少操作数,使得数列升序: ...

  8. Another Version of Inversion 二维树状数组求逆序对

    Another Version of Inversion 题意:只有2种走路方式,往右或者往下,求先走到一个大的数,在走到小的数的这种方式有多少.也就是说求出关于这个2维矩阵的逆序数. 题解:二维数组 ...

  9. POJ-2018 Best Cow Fences 二分

    题意:找到一个连续区间,区间的长度至少大于f,现在要求这个区间的平均值最大. 题解: 二分找答案. 每次对于2分的mid值, 都把原来的区间减去mid, 然后找到一长度至少为f的区间, 他们的区间和& ...

  10. CodeForces 1082 G Petya and Graph 最大权闭合子图。

    题目传送门 题意:现在有一个图,选择一条边,会把边的2个顶点也选起来,最后会的到一个边的集合 和一个点的集合 , 求边的集合 - 点的集合最大是多少. 题解:裸的最大权闭合子图. 代码: #inclu ...