模块管理


在软件开发中,把一个大项目分拆为多个模块是降低软件复杂度的有效方法:

  1. ┌─────────┐
  2. Module A
  3. └─────────┘
  4. ┌──────────────┐ split ┌─────────┐
  5. Single Project│───────▶ Module B
  6. └──────────────┘ └─────────┘
  7. ┌─────────┐
  8. Module C
  9. └─────────┘

对于Maven工程来说,原来是一个大项目:

  1. single-project
  2. ├── pom.xml
  3. └── src

现在可以分拆成3个模块:

  1. mutiple-project
  2. ├── module-a
  3. ├── pom.xml
  4. └── src
  5. ├── module-b
  6. ├── pom.xml
  7. └── src
  8. └── module-c
  9. ├── pom.xml
  10. └── src

Maven可以有效地管理多个模块,我们只需要把每个模块当作一个独立的Maven项目,它们有各自独立的pom.xml。例如,模块A的pom.xml

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.itranswarp.learnjava</groupId>
  6. <artifactId>module-a</artifactId>
  7. <version>1.0</version>
  8. <packaging>jar</packaging>
  9. <name>module-a</name>
  10. <properties>
  11. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  12. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  13. <maven.compiler.source>11</maven.compiler.source>
  14. <maven.compiler.target>11</maven.compiler.target>
  15. <java.version>11</java.version>
  16. </properties>
  17. <dependencies>
  18. <dependency>
  19. <groupId>org.slf4j</groupId>
  20. <artifactId>slf4j-api</artifactId>
  21. <version>1.7.28</version>
  22. </dependency>
  23. <dependency>
  24. <groupId>ch.qos.logback</groupId>
  25. <artifactId>logback-classic</artifactId>
  26. <version>1.2.3</version>
  27. <scope>runtime</scope>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.junit.jupiter</groupId>
  31. <artifactId>junit-jupiter-engine</artifactId>
  32. <version>5.5.2</version>
  33. <scope>test</scope>
  34. </dependency>
  35. </dependencies>
  36. </project>

模块B的pom.xml

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.itranswarp.learnjava</groupId>
  6. <artifactId>module-b</artifactId>
  7. <version>1.0</version>
  8. <packaging>jar</packaging>
  9. <name>module-b</name>
  10. <properties>
  11. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  12. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  13. <maven.compiler.source>11</maven.compiler.source>
  14. <maven.compiler.target>11</maven.compiler.target>
  15. <java.version>11</java.version>
  16. </properties>
  17. <dependencies>
  18. <dependency>
  19. <groupId>org.slf4j</groupId>
  20. <artifactId>slf4j-api</artifactId>
  21. <version>1.7.28</version>
  22. </dependency>
  23. <dependency>
  24. <groupId>ch.qos.logback</groupId>
  25. <artifactId>logback-classic</artifactId>
  26. <version>1.2.3</version>
  27. <scope>runtime</scope>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.junit.jupiter</groupId>
  31. <artifactId>junit-jupiter-engine</artifactId>
  32. <version>5.5.2</version>
  33. <scope>test</scope>
  34. </dependency>
  35. </dependencies>
  36. </project>

可以看出来,模块A和模块B的pom.xml高度相似,因此,我们可以提取出共同部分作为parent

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.itranswarp.learnjava</groupId>
  6. <artifactId>parent</artifactId>
  7. <version>1.0</version>
  8. <packaging>pom</packaging>
  9. <name>parent</name>
  10. <properties>
  11. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  12. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  13. <maven.compiler.source>11</maven.compiler.source>
  14. <maven.compiler.target>11</maven.compiler.target>
  15. <java.version>11</java.version>
  16. </properties>
  17. <dependencies>
  18. <dependency>
  19. <groupId>org.slf4j</groupId>
  20. <artifactId>slf4j-api</artifactId>
  21. <version>1.7.28</version>
  22. </dependency>
  23. <dependency>
  24. <groupId>ch.qos.logback</groupId>
  25. <artifactId>logback-classic</artifactId>
  26. <version>1.2.3</version>
  27. <scope>runtime</scope>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.junit.jupiter</groupId>
  31. <artifactId>junit-jupiter-engine</artifactId>
  32. <version>5.5.2</version>
  33. <scope>test</scope>
  34. </dependency>
  35. </dependencies>
  36. </project>

注意到parent的<packaging>pom而不是jar,因为parent本身不含任何Java代码。编写parentpom.xml只是为了在各个模块中减少重复的配置。现在我们的整个工程结构如下:

  1. multiple-project
  2. ├── pom.xml
  3. ├── parent
  4. └── pom.xml
  5. ├── module-a
  6. ├── pom.xml
  7. └── src
  8. ├── module-b
  9. ├── pom.xml
  10. └── src
  11. └── module-c
  12. ├── pom.xml
  13. └── src

这样模块A就可以简化为:

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>com.itranswarp.learnjava</groupId>
  7. <artifactId>parent</artifactId>
  8. <version>1.0</version>
  9. <relativePath>../parent/pom.xml</relativePath>
  10. </parent>
  11. <artifactId>module-a</artifactId>
  12. <packaging>jar</packaging>
  13. <name>module-a</name>
  14. </project>

模块B、模块C都可以直接从parent继承,大幅简化了pom.xml的编写。

如果模块A依赖模块B,则模块A需要模块B的jar包才能正常编译,我们需要在模块A中引入模块B:

  1. ...
  2. <dependencies>
  3. <dependency>
  4. <groupId>com.itranswarp.learnjava</groupId>
  5. <artifactId>module-b</artifactId>
  6. <version>1.0</version>
  7. </dependency>
  8. </dependencies>

最后,在编译的时候,需要在根目录创建一个pom.xml统一编译:

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.itranswarp.learnjava</groupId>
  6. <artifactId>build</artifactId>
  7. <version>1.0</version>
  8. <packaging>pom</packaging>
  9. <name>build</name>
  10. <modules>
  11. <module>parent</module>
  12. <module>module-a</module>
  13. <module>module-b</module>
  14. <module>module-c</module>
  15. </modules>
  16. </project>

这样,在根目录执行mvn clean package时,Maven根据根目录的pom.xml找到包括parent在内的共4个<module>,一次性全部编译。

中央仓库

其实我们使用的大多数第三方模块都是这个用法,例如,我们使用commons logging、log4j这些第三方模块,就是第三方模块的开发者自己把编译好的jar包发布到Maven的中央仓库中。

私有仓库

私有仓库是指公司内部如果不希望把源码和jar包放到公网上,那么可以搭建私有仓库。私有仓库总是在公司内部使用,它只需要在本地的~/.m2/settings.xml中配置好,使用方式和中央仓位没有任何区别。

本地仓库

本地仓库是指把本地开发的项目“发布”在本地,这样其他项目可以通过本地仓库引用它。但是我们不推荐把自己的模块安装到Maven的本地仓库,因为每次修改某个模块的源码,都需要重新安装,非常容易出现版本不一致的情况。更好的方法是使用模块化编译,在编译的时候,告诉Maven几个模块之间存在依赖关系,需要一块编译,Maven就会自动按依赖顺序编译这些模块。

小结

Maven支持模块化管理,可以把一个大项目拆成几个模块:

  • 可以通过继承在parent的pom.xml统一定义重复配置;
  • 可以通过<modules>编译多个模块。

读后有收获可以支付宝请作者喝咖啡:

模块管理 - 图1