zoukankan      html  css  js  c++  java
  • 【转】Maven多模块项目构建

    划分多模块项目的原因

    多人协作的Maven管理的真实的项目可以选择分模块,每个模块都对应着一个pom.xml。它们之间通过继承和聚合(也称作多模块,multi-module)相互关联。那么,为什么要这么做呢?我们明明在开发一个项目,划分模块后,导入Eclipse变成了N个项目,这会带来复杂度,给开发带来不便。
    为了解释原因,假设有这样一个项目,很常见的Java Web应用。在这个应用中,我们分了几层:

    • Dao层负责数据库交互,封装了Mysql交互的接口 。
    • Service层处理业务逻辑,放一些Service接口和实现相关的Bean。
    • Web层负责与客户端交互,主要有一些Control类。

    对应的,在一个项目中,我们会看到一些包名:

    • hans.app.dao
    • hans.app.service
    • hans.app.web
    • hans.app.util

    这样整个项目的框架就清晰了,但随着项目的进行,你可能会遇到如下问题:
    这个应用可能需要有一个前台和一个后台管理端(admin),你发现大部分dao,一些service,和大部分util是在两个应用中可。这样的问题,你一周内遇到了好几次。
    pom.xml中的依赖列表越来越长以重用的,但是,由于目前只有一个项目(WAR),你不得不新建一个项目依赖这个WAR,这变得非常的恶心,因为在Maven中配置对WAR的依赖远不如依赖JAR那样简单明了,而且你根本不需要hans.app.web。有人修改了dao,提交到git并且不小心导致build失败了,你在编写service的代码,发现编译不过,只能等那人把dao修复了,你才能继续进行,很多人都在修改,到后来你根本就不清楚哪个依赖是谁需要的,渐渐的,很多不必要的依赖被引入。甚至出现了一个依赖有多个版本存在。
    build整个项目的时间越来越长,尽管你只是一直在web层工作,但你不得不build整个项目。
    某个模块,比如util,你只想让一些经验丰富的人来维护,可是,现在这种情况,每个开发者都能修改,这导致关键模块的代码质量不能达到你的要求。

    我们会发现,其实这里实际上没有遵守一个设计模式原则:“高内聚,低耦合”。虽然我们通过包名划分了层次,并且你还会说,这些包的依赖都是单向的,没有包的环依赖。这很好,但还不够,因为就构建层次来说,所有东西都被耦合在一起了。因此我们需要使用Maven划分模块。

    聚合

    一个简单的Maven模块结构是这样的:

    ---- app-parent
    |-- pom.xml (pom)
    |
    |-- app-util
    | |-- pom.xml (jar)
    |
    |-- app-dao
    | |-- pom.xml (jar)
    |
    |-- app-service
    | |-- pom.xml (jar)
    |
    |-- app-web
    |-- pom.xml (war)

    上述简单示意图中,有一个父项目(app-parent)聚合很多子项目(app-util, app-dao, app-service, app-web)。每个项目,不管是父子,都含有一个pom.xml文件。而且要注意的是,小括号中标出了每个项目的打包类型。父项目是pom,也只能是pom。子项目有jar,或者war。根据它包含的内容具体考虑。

    这些模块的依赖关系如下:

    app-dao --> app-util
    app-service --> app-mapper
    app-web --> app-service

    注意依赖的传递性(大部分情况是传递的,除非你配置了特殊的依赖scope),app-dao依赖于app-util,app-service依赖于app-dao,于是app-service也依赖于app-util。同理,app-web依赖于app-dao,app-util。

    用项目层次的划分替代包层次的划分能给我们带来如下好处:
    方便重用,如果你有一个新的项目需要用到app-dao和app-service,添加对它们的依赖即可,你不再需要去依赖一个WAR。而有些模块,如app-util,完全可以渐渐进化成公司的一份基础工具类库,供所有项目使用。这是模块化最重要的一个目的。
    由于你现在划分了模块,每个模块的配置都在各自的pom.xml里,不用再到一个混乱的纷繁复杂的总的POM中寻找自己的配置。
    某些模块,如app-util被所有人依赖,但你不想给所有人修改,现在你完全可以从这个项目结构出来,做成另外一个项目,svn只给特定的人访问,但仍提供jar给别人使用。
    多模块的Maven项目结构支持一些Maven的更多的特性(如DepencencyManagement)

    接下来讨论一下POM配置细节,实际上非常简单,先看app-parent的pom.xml:

    1.  
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2.  
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    3.  
      <modelVersion>4.0.0</modelVersion>
    4.  
      <groupId>com.test.hans</groupId>
    5.  
      <artifactId>app-parent</artifactId>
    6.  
      <packaging>pom</packaging>
    7.  
      <version>1.0-SNAPSHOT</version>
    8.  
      <modules>
    9.  
      <module>app-util</module>
    10.  
      <module>app-dao</module>
    11.  
      <module>app-service</module>
    12.  
      <module>app-web</module>
    13.  
      </modules>
    14.  
      </project>

    Maven的坐标GAV(groupId, artifactId, version)在这里进行配置,这些都是必须的。特殊的地方在于,这里的packaging为pom。所有带有子模块的项目的packaging都为pom。packaging如果不进行配置,它的默认值是jar,代表Maven会将项目打成一个jar包。
    该配置重要的地方在于modules,例子中包含的子模块有app-util, app-dao, app-service, app-war。在Maven build app-parent的时候,它会根据子模块的相互依赖关系整理一个build顺序,然后依次build。这里model中的名称应与子模块所在目录名保持一致。
    父模块的内容仅是一个pom.xml文件,它不想其他模块那样有src/main/java等目录。这也是容易理解的,父模块仅仅是帮助聚合其他模块构建的工具,它本身并无实际的内容。
    父模块与其他模块的目录结构并非一定是父子关系,还可以是平行关系。那样的话只需要更改一下modules

    1.  
      <modules>
    2.  
      <module>../app-util</module>
    3.  
      <module>../app-mapper</module>
    4.  
      <module>../app-service</module>
    5.  
      <module>../app-web</module>
    6.  
      </modules>

    在父模块运行mvn clean install命令 会得到:


    命令行输出

    上述输出中显示的是各模块的name,而不是artifactId,这也解释了为什么要在POM中配置合理的name字段,其目的是让Maven的构建输出更清晰。

    继承

    多模块构建的另一个特性就继承。继承解决最大的问题就是,重复。重复往往意味着更多的劳动和更多潜在的问题。POM的继承机制能让我们抽取重复的配置。

    1.  
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2.  
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    3.  
      <parent>
    4.  
      <artifactId>app-parent</artifactId>
    5.  
      <groupId>com.test.hans</groupId>
    6.  
      <version>1.0-SNAPSHOT</version>
    7.  
      </parent>
    8.  
      <modelVersion>4.0.0</modelVersion>
    9.  
      <artifactId>app-util</artifactId>
    10.  
      </project>

    这个POM没有为app-util声明groupId和version,不过这并不代表app-util没有groupId和version。实际上,这个子模块隐式的从父模块继承了这两个元素,这也就消除了一些不必要的配置。如果子模块需要使用和父模块不一样的groupId和version完全可以显示声明。
    还有哪些POM元素可以被继承呢?以下是一个常见的列表:

    • url:项目的URL地址
    • dependencies:项目的依赖配置
    • dependencyManagement:项目的依赖管理配置
    • repositories:项目的仓库配置
    • build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等
    • reporting:包括项目的报告输出目录配置、报告插件配置等

    上面的列表中包含了dependencies,说明依赖是会被继承的,这时我们会想,将spring等我们依赖的jar包就都可以配置到父模块中,这样子模块就能移除这些依赖,简化配置。但这种做法是存在问题的。当其中一个子模块不需要这些依赖的时候,强制它依赖spring等显然是不合理的。Maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块的灵活性。在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用。下面是测试项目的POM文件:
    父模块:

    1.  
      <dependencyManagement>
    2.  
      <dependencies>
    3.  
      <dependency>
    4.  
      <groupId>com.test.hans</groupId>
    5.  
      <artifactId>org-remote-service</artifactId>
    6.  
      <version>1.0.26</version>
    7.  
      </dependency>
    8.  
      </dependencies>
    9.  
      </dependencyManagement>

    子模块

    1.  
      <dependencies>
    2.  
      <dependency>
    3.  
      <groupId>com.test.hans</groupId>
    4.  
      <artifactId>org-remote-service</artifactId>
    5.  
      </dependency>
    6.  
      </dependencies>

    而且还可以将这些依赖的版本会Maven变量的形式提取了出来,这样所有模块的依赖的版本处于更加明显的位置。这样可以很好的解决多个子模块使用的依赖版本不一致的情况。可以降低依赖冲突的几率。如果子模块没有声明,即使该依赖已经在父POM中,子模块也不会引用。
    但是如果其中的一个子模块想使用和父模块一样的POM,只要像下面这么写就可以了

    1.  
      <dependencyManagement>
    2.  
      <dependencies>
    3.  
      <dependency>
    4.  
      <groupId>com.test.hans</groupId>
    5.  
      <artifactId>app-parent</artifactId>
    6.  
      <version>1.0-SNAPSHOT</version>
    7.  
      <type>pom</type>
    8.  
      <scope>import</scope>
    9.  
      </dependency>
    10.  
      </dependencies>
    11.  
      </dependencyManagement>

    Maven提供了dependencyManagement元素帮助管理项目依赖,类似的,Maven也提供了pluginManagement元素帮助管理插件。在该元素中配置的依赖不会造成实际的插件调用行为,当POM中配置了真正的plugin元素,并且其groupId和artifactId与pluginManagement中配置的插件匹配时,pluginManagement的配置才会影响实际的插件行为。
    父模块:

    1.  
      <build>
    2.  
      <pluginManagement>
    3.  
      <plugins>
    4.  
      <plugin>
    5.  
      <groupId>org.apache.maven.plugins</groupId>
    6.  
      <artifactId>maven-resources-plugin</artifactId>
    7.  
      <version>2.5</version>
    8.  
      <configuration>
    9.  
      <encoding>${encoding}</encoding>
    10.  
      </configuration>
    11.  
      </plugin>
    12.  
      </plugins>
    13.  
      </pluginManagement>
    14.  
      </build>

    子模块

    1.  
      <build>
    2.  
      <plugins>
    3.  
      <plugin>
    4.  
      <groupId>org.apache.maven.plugins</groupId>
    5.  
      <artifactId>maven-resources-plugin</artifactId>
    6.  
      </plugin>
    7.  
      </plugins>
    8.  
      </build>

    聚合与继承的关系

    如果用一句话总结两者的关系就是没有关系。聚合主要是为了方便构建项目,继承主要是为了消除重复配置。
    对于聚合模块来说,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在。
    对于继承关系的父pom来说,它不知道有哪些子模块继承于它,但那些子模块都必须知道自己的父POM是什么。
    它们之间的共同点就是,聚合POM与继承关系中的父POM的packaging都必须是pom,同时聚合模块与继承关系中的父模块出了POM之外都没有实际内容。
    在现有的实际项目中,一般是一个POM即是聚合POM,又是父POM,这么做十分方便同时也是Maven的默认约定。



    文/hansjason(简书作者)
    原文链接:http://www.jianshu.com/p/79cc250be1b2
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
    每天进步一点点,快乐生活多一点。
  • 相关阅读:
    deeplearning.ai 卷积神经网络 Week 1 卷积神经网络
    deeplearning.ai 构建机器学习项目 Week 2 机器学习策略 II
    deeplearning.ai 构建机器学习项目 Week 1 机器学习策略 I
    deeplearning.ai 改善深层神经网络 week3 超参数调试、Batch Normalization和程序框架
    deeplearning.ai 改善深层神经网络 week2 优化算法
    deeplearning.ai 改善深层神经网络 week1 深度学习的实用层面
    cs231n spring 2017 lecture8 Deep Learning Networks
    cs231n spring 2017 lecture7 Training Neural Networks II
    cs231n spring 2017 lecture6 Training Neural Networks I
    cs231n spring 2017 Python/Numpy基础
  • 原文地址:https://www.cnblogs.com/yiruliu/p/15634053.html
Copyright © 2011-2022 走看看