maven系列(六)聚合与继承(还有TODO)

第8章 聚合与继承

Maven的聚合特性能够把项目的各个模块聚合在一起构建,而maven的继承特性则能帮助抽取各模块相同的依赖和插件等配置,在简化POM的同时,还能促进各个模块配置的一致性。

8.1 mirana-dal

mirana-dal模块负责mirana的数据的持久化,以json文件的形式保存数据,并支持账户的创建、读取、更新、删除等操作。

【由于腾讯云那个mysql很难装,暂时又不想深入学习mongo,因此这个地方直接使用文件系统作为数据库,顺便学习java的IO知道,代码参考spring-core里面的io部分】

相关类和方法有

  1. java.util.Properties(在项目内部)
  2. io和nio相关知识(更多见NIO)
<?xml version="1.0" encoding="UTF-8"?>
<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">  
<parent>
        <artifactId>mirana-parent</artifactId>
        <groupId>com.ssozh</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    
    
    <build>
        <testResources>
            <testResource>
                <directory>src/test/resources</directory>
                <filtering>true</filtering>
            </testResource>
        </testResources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

该模块坐标为com.ssozh:mirana-parent:1.0.0-SNAPSHOT。和其他模块有相同的groupId,artifactId有相同的前缀。

一般来说,一个项目的子模块都应该使用相同的groupid,如果在一起开发和发布,还应该使用相同的version。artifactId也应该有相同的前缀,以方便其他项目区分。

build元素他包含了一个testResource子元素,这是为了开启资源过滤。【用于单元测试】。下面还包含两个插件,一个是maven-compiler-plugin用于支持java1.8(实际上该版本在超级POM中有设定,而这个1.8是在springboot-start-parent中设定的),此外还配置了maven-resources-plugin使用了UTF-8编码处理资源文件。

8.2 聚合

到目前为止,mirana这个项目分别在biz层实现了createGroup方法,和dal层的createGroup方法。这时,一个简单的需求就会自然而然地显示出来:我们会想要一次构建两个项目,而不是到两个模块的目录下分别执行mvn命令。

为了能够使用一条命令就能够构建mirana-api、mirana-controller、mirana-common、mirana-dal等多个模块,我们需要额外创建一个名为mirana-parent的模块,然后通过该模块构建整个项目的所有模块。mirana-parent本身作为一个maven项目,它必须要有自己的POM,不过同时作为一个聚合项目,其POM又有特殊的地方,pom.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.ssozh</groupId>
    <artifactId>mirana-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>mirana-parent</name>

    <description>Demo project for Spring Boot</description>

    <packaging>pom</packaging>

    <modules>
        <module>assembly</module>
        <module>controller</module>
        <module>service</module>
        <module>biz</module>
        <module>dal</module>
        <module>common</module>
        <module>integration</module>
        <module>api</module>
        <module>starter</module>
        <module>test</module>
    </modules>

</project>

这里有几个特殊的地方:

  1. packaging:pom,聚合模块的打包方式必须是pom,否则就无法构建
  2. modules:实现聚合的最核心位置,用户可以通过自一个打包方式为pom的maven项目中声明任意数量的module元素来实现模块的聚合。
  3. 一般来讲,模块所处的目录名称应该遇其artifactId一致,如果不一致,则需要将聚合pom中的<module>改成相应的目录名。
  4. 聚合模块与其他模块的目录结构并非一定要是父子关系,也可以使用平行目录结构,这个时候,<module>也应该指向正确的模块目录
  5. mvn clean install的过程:
    1. 解析聚合模块的POM,分析要构建的模块
    2. 计算出一个反应堆构建顺序(reactor build order)
      1. 这个就是依赖关系:parent->api->common->integration->dal->biz->service->controller->starter->assembly
    3. 根据这个顺序依次构建各个模块。
    4. 其他:上述过程中输出的是各个模块的名称,而不是artifactId。

8.3 继承

可以通过继承抽取出重复的配置,maven也有这种继承机制。

作为父模块的POM和聚合模块的POM一样都必须是POM的打包类型。由于父模块不仅仅是为了帮助消除配置的重复,因为他本身不包含POM以外的项目文件,也就不需要src/main/java之类的文件夹了。

relativePath表示父模块POM的相对路径,默认值是../pom.xml,其他情况需要手动写入。

如果开发团队需要从源码库中迁出一个包含父子关系的Maven项目,则需要设置正确的<relativePath>

8.3.1可继承的POM元素

从上一节我们看到,groupId和version是可以被继承的,以下是完整的继承列表:

  • groupId:项目组Id,坐标核心元素。
  • version:项目版本,坐标核心元素。
  • properties:自定义Maven属性
  • dependencies:项目的依赖配置
  • dependencyManagement:项目的依赖管理配置
  • repositories:项目的仓库配置
  • build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等。
  • reporting:包括项目的报告输出目录配置、报告插件配置等。
  • description:项目的描述信息
  • organization:项目的组织信息。
  • inceptionYear:项目的创始年份
  • url:项目的url地址
  • developers、contributors:项目的开发者贡献值信息
  • distributionManagement:项目的部署配置
  • ciManagement:项目的持续集成系统信息
  • scm:项目的版本控制系统信息

8.3.2 依赖管理(常用)

Maven提供的dependencyManagement元素能够让子模块依赖父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用。

	<properties>
    	<java.version>1.8</java.version>
        <mirana.parent.version>1.0.0-SNAPSHOT</mirana.parent.version>
        <mirana.api.version>1.0.0-SNAPSHOT</mirana.api.version>
        <fastjson.version>1.2.76</fastjson.version>
        <guava.version>28.2-android</guava.version>
        <mapstruct.version>1.4.2.Final</mapstruct.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.ssozh</groupId>
                <artifactId>mirana-api</artifactId>
                <version>${mirana.api.version}</version>
            </dependency>
            <dependency>
                <groupId>com.ssozh</groupId>
                <artifactId>mirana-starter</artifactId>
                <version>${mirana.parent.version}</version>
            </dependency>
            <!-- ................. -->
        </dependencies>
</dependencyManagement>

这样在实际test中使用的时候,直接省略了相关的versionscope信息。这些信息可以省略是因为子项目继承了父项目中的dependencyManagement配置。 这样虽然不能减少太多的POM配置,但是在父POM中声明是能够规范统一项目依赖的版本,子模块在使用依赖的时候就无须声明版本,也就不会发生多个子模块使用依赖版本不一致的情况,从而降低依赖冲突的几率。

另外,对于前面介绍的import的依赖范围。使用该范围的依赖通常指向一个POM,作用是将目标POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中。如果想要在另外一个模块中使用与某个模块玩去哪一样的dependencyManagement配置,除了复制配置或者继承这两种方式外,还可以使用import范围依赖将这一配置导入。

8.3.2 插件管理

Maven提供了dependencyManagement元素帮助管理依赖,Maven也提供了pluginManagement元素帮助管理插件。在该元素中配置的依赖不会造成实际的插件调用行为,当POM中配置了真正的plugin元素,并且GAV与pluginManagement中配置的插件匹配时,pluginManagement的配置才会影响实际的插件行为。

例如父项目中可以这么写:

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-source-plugin</artifactId>
                    <version>3.2.1</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

8.4 聚合与继承的关系

多模块Maven项目中的聚合与继承其实是两个概念,其目的是完全不同的。

聚合 继承
概念和目的 为了方便快速构建项目 为了消除重复配置
使用方法 <modules>来聚合项目 子项目<parent><relativePath>。父项目的<xxxManagement>

现有的实际项目中,往往一个POM既是聚合POM,又是父POM,这么做主要是为了方便。

8.5 约定优于配置

Maven会假设用户的项目是这样的:

  • 源码目录为src/main/java/
  • 编译输出目录为target/classes
  • 打包方式为jar
  • 包输出目录为target/

遵循约定虽然损失了一定的灵活性,用户不能随意安排目录结构。如果想要修改 也可以在<build>中进行修改:

<build>
	<sourceDirectory>src/java</sourceDirectory>
</build>

实际上,任何一个Maven项目都是继承的超级POM。这点有点类似于任何一个java类都是隐式继承于Object类一样。在maven3中位置位于$MAVEN_HOMEmaven-model-buildersrcmain esourcesorgapachemavenmodelpom-4.0.0.xml

具体看相关的代码:

 <build>
    <directory>${project.basedir}/target</directory>
    <outputDirectory>${project.build.directory}/classes</outputDirectory>
    <finalName>${project.artifactId}-${project.version}</finalName>
    <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
    <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
    <scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory>
    <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
    <resources>
      <resource>
        <directory>${project.basedir}/src/main/resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>${project.basedir}/src/test/resources</directory>
      </testResource>
    </testResources>
    <pluginManagement>
      <!-- NOTE: These plugins will be removed from future versions of the super POM -->
      <!-- They are kept for the moment as they are very unlikely to conflict with lifecycle mappings (MNG-4453) -->
      <plugins>
        <plugin>
          <artifactId>maven-antrun-plugin</artifactId>
          <version>1.3</version>
        </plugin>
        <plugin>
          <artifactId>maven-assembly-plugin</artifactId>
          <version>2.2-beta-5</version>
        </plugin>
        <plugin>
          <artifactId>maven-dependency-plugin</artifactId>
          <version>2.8</version>
        </plugin>
        <plugin>
          <artifactId>maven-release-plugin</artifactId>
          <version>2.5.3</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

8.6 反应堆

在一个多模块的Maven项目中,反应堆(reactor)是指所有组成的一个构建结构。对于单模块的项目,反应堆就是该模块本身,对于多模块而言,反应堆就包含了各模块之间继承与依赖的关系,从而计自动计算出合理的模块构建顺序。

8.6.1 反应堆的构建顺序

模块间的依赖关系会将反应堆构建成一个有向非循环图(directed acyclic graph,DAG)各个模块是该图的节点,依赖关系构成了有向边。这个图不允许出现循环,因此,当出现模块A依赖模块B,而B又依赖A的情况时,Maven就会报错。

8.6.2 裁剪反应堆

有些时候,用户会想要仅仅构建完整反应堆中的某些模块,换句话说,用户需要实时地裁剪反应堆。

maven提供很多的命令行选择支持裁剪反应堆,输出mvn -h 可以看到这些选项:

  • -am:--also-make通收件所列模块的依赖模块
  • -amd:--alse-make-dependents同时构建依赖于所列模块的模块
  • -pl:--projects<args>构建指定的模块,模块间用逗号分隔
  • -rf:-resume-from <args>从指定的模块回复反应堆

具体例子:

# 构建完整反应堆
mvn clean install
# 使用-pl选项指定构建某几个模块
mvn clean install -pl mirana-dal, mirana-biz
## 使用-am选项考研同时构建所列模块的依赖模块
mvn clean install -pl mirana-dal -am
## 使用-amd选项考研同时构建依赖于所列模块的模块
mvn clean install -pl mirana-dal -amd
## 使用—rf选项可以在完整的反应堆顺序基础上指定从哪个模块开始构建。例如:
mvn clean install -rf mirana-dal

在开发过程中,灵活应用上述4个参数,可以帮助我们跳过无需构建的模块,从而加速构建。【目前】我们的实际项目中没有用过这些

8.7 小结

本章介绍了聚合和继承两个特性,然后介绍了优化大于配置,最后介绍了反应堆 和相关的命令参数用于裁剪反应堆。

其他

  1. junit注解

    1. @Before可以用在执行测试用例之前,执行该方法。可以用来初始化springframework的IoC容器。
    2. @Test注解标注的就是要测试的方法。
  2. 关于Maven中Model.class是如何通过maven-model中的maven.mdo生成的?

  3. 既然Maven不允许循环依赖,spring中通过@Resource的循环依赖是什么样的呢?

原文地址:https://www.cnblogs.com/SsoZhNO-1/p/15516559.html