Maven项目继承和聚合,你学废了吗?

Java
379
0
0
2023-05-27
标签   Maven

我们常见的 Java 项目结构是这样的,

单一模块项目

实际的Java项目是这样的,

大型开源Java项目

通常一个稍大型的项目都是由多个模块组成的,比如上面著名的RPC框架Dubbo,包括了21个模块。多模块项目的依赖问题如何解决,如何更好地管理和发布多模块项目?

Maven 使用项目继承和聚合来管理多模块项目,本文将通过实例详细介绍如何使用Maven的项目继承和聚合。

maven 使用 pom .xml来管理项目,包括项目的名称、版本、依赖、打包路径等等项目信息。

什么是POM,它是英文名称 project Object Model,项目对象模型。把项目当作对象来管理,类似面向对象编程语言中的对象,可以建立继承关系,或者说父子关系,方便进行复杂项目的管理。

pom的父子关系

注意,本文的例子都比较简单,不需要使用archetype创建,按照项目结构直接创建所需要的文件即可。

项目继承

项目继承的意思是子项目或者说子模块可以继承父项目的pom配置,主要作用是将各个子项目或者子模块的相同配置提取出来,比如共同的依赖、编码、编译选项等,避免重复冗余的pom配置项。每个模块的pom.xml只需要配置本模块特有的配置项。

我们通过maven-inheritance项目来加以说明,

项目结构

 maven-inheritance/
├── module1
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── my
│                       └── demo
│                           └── Module1.java
├── module2
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── my
│                       └── demo
│                           └── Module2.java
└── pom.xml

14 directories, 5 files  

pom.xml

 <project>
    <!-- 设置pom的版本,这是必须的 -->
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.my.demo</groupId>
    <artifactId>demo-parent</artifactId>
    <version>1.0</version>
    <!--  packaging 默认值是jar,这里父级pom不需要打包,只是用来配置子模块共同的配置项,所以packaging设置为pom -->
    <packaging>pom</packaging>

    <!-- 子模块共同的属性 -->
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <!-- 子模块共同的插件,maven-jar-plugin将模块打包成jar -->
    < build >
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
            </plugin>
        </plugins>
    </build>

</project> 

module1pom.xml

 <project>
    <modelVersion>4.0.0</modelVersion>

    <!-- 指定父项目,如果父项目的pom.xml不在项目根目录,你需要使用relativePath指定 -->
    <parent>
        <groupId>com.my.demo</groupId>
        <artifactId>demo-parent</artifactId>
        <version>1.0</version>
        <!-- <relativePath>../pom.xml</relativePath> -->
    </parent>

    <groupId>com.my.demo</groupId>
    <artifactId>demo-module1</artifactId>
    <version>1.0</version>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                < configuration >
                    <archive>
                        <manifest>
                            <mainClass>com.my.demo.Module1</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project> 

module1srcmainjavacommydemoModule1.java

 package com.my.demo;

public class Module1 {

    public static void main(String[] args) {
        System.out.println("hello, module1");
    }
} 

module2pom.xml

 <project>
    <modelVersion>4.0.0</modelVersion>

    <!-- 指定父项目,如果父项目的pom.xml不在项目根目录,你需要使用relativePath指定 -->
    <parent>
        <groupId>com.my.demo</groupId>
        <artifactId>demo-parent</artifactId>
        <version>1.0</version>
        <!-- 默认父pom路径 -->
        <!-- <relativePath>../pom.xml</relativePath> -->
    </parent>

    <groupId>com.my.demo</groupId>
    <artifactId>demo-module2</artifactId>
    <version>1.0</version>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.my.demo.Module2</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project> 

module2srcmainjavacommydemoModule2.java

 package com.my.demo;

public class Module2 {

    public static void main(String[] args) {
        System.out.println("hello, module2");
    }
} 

上面是整个maven-inheritance项目的源文件,module1和module2也比较简单。这里需要注意的就是根目录的pom.xml,里面配置了子模块共同的配置项和依赖插件,子模块的pom.xml通过<parent>标签指定了父级项目。

当需要执行编译、打包操作的时候,需要进入每个模块的根目录,执行mvn package即可,像下面这样。

 PS D:demosmaven-demosmaven-inheritance> cd .module1
PS D:demosmaven-demosmaven-inheritancemodule1> mvn clean package
...
PS D:demosmaven-demosmaven-inheritancemodule1> java -jar .targetdemo-module1-1.0.jar
hello, module1
PS D:demosmaven-demosmaven-inheritancemodule1> cd ../module2
PS D:demosmaven-demosmaven-inheritancemodule2> mvn clean package
...
PS D:demosmaven-demosmaven-inheritancemodule2> java -jar .targetdemo-module2-1.0.jar 
hello, module2  

项目继承的作用就是抽取各个项目共同的依赖和配置项,简化每个模块的配置,编译、打包需要进入每个模块分别操作。

能否所有模块统一编译和打包呢?

答案是肯定的,Maven项目聚合可以实现这个目标。

项目聚合

Maven项目聚合功能可以统一管理多个子项目或者多个子模块,统一编译、打包和发布等一系列操作。

我们通过maven-aggregation项目来加以说明,

项目结构

 maven-aggregation
├── module1
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── my
│                       └── demo
│                           └── Module1.java
├── module2
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── my
│                       └── demo
│                           └── Module2.java
└── pom.xml

14 directories, 5 files  

maven-aggregation项目和maven-inheritance项目的目录结构完全一样,不同之处就在于3个pom.xml内容不同。

pom.xml

 <project>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.my.demo</groupId>
    <artifactId>demo-parent</artifactId>
    <version>1.0</version>
    <packaging>pom</packaging>

    < modules >
        <module>module1</module>
        <module>module2</module>
    </modules>

</project>  

module1pom.xml

 <project>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.my.demo</groupId>
    <artifactId>demo-module1</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.my.demo.Module1</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project> 

module2pom.xml

 <project>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.my.demo</groupId>
    <artifactId>demo-module2</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.my.demo.Module2</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project> 

module1和module2的Java文件内容完全一样,这里就不重复写了。这里注意一下3个pom.xml的内容,父级pom.xml通过<modules>标签来指定项目包括哪些模块,通过<module>标签指定具体的每一个模块,注意<module>标签内是子模块的目录名,而非子模块的名称。

通过下面的命令执行统一打包操作,

 PS D:demosmaven-demosmaven-aggregation> mvn clean package
...
PS D:demosmaven-demosmaven-aggregation> java -jar .module1targetdemo-module1-1.0.jar
hello, module1
PS D:demosmaven-demosmaven-aggregation> java -jar .module2targetdemo-module2-1.0.jar 
hello, module2  

注意这里,我执行了一次编译打包命令,module1和module2同时打包好了,可以直接运行。

混合使用

就像开头的Dubbo项目,同时使用了Maven项目继承和聚合,这样既能统一管理各个子项目的配置,也能统一各个子项目的编译打包发布流程。

你可以结合maven-inheritance和maven-aggregation项目的配置,修改对应的pom.xml,同时使用Maven项目继承和项目聚合特性。

总结

  • 通过上面的文章,你可以学会Maven项目继承和项目聚合进行Java项目管理;
  • 像Java的Object类以及Python的object类,是所有类的父类,pom也有Super POM,如果pom.xml没有指定parent,就是继承Super POM,感兴趣的自行了解;
  • 除此之外你还可以通过文章学习到如何设置打包的main class,如何设置打包的编译选项,包括jdk版本。

声明,以上代码经过测试!