|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
1. Maven父子项目基础概念
Maven是一个强大的项目管理和构建工具,它通过一个中央信息管理的方式管理项目的构建、报告和文档。在大型项目开发中,Maven的父子项目(也称为多模块项目)结构尤为重要。
1.1 什么是Maven父子项目
Maven父子项目是一种项目组织结构,其中一个父项目(Parent Project)可以包含多个子项目(Child Modules)。这种结构允许我们将一个大型项目分解为多个更小、更易于管理的模块,每个模块可以独立开发、测试和构建,同时又能共享配置和依赖。
1.2 父子项目的优势
使用Maven父子项目结构有以下优势:
1. 统一管理:父项目可以统一管理所有子模块的版本号、依赖项、插件配置等,确保一致性。
2. 简化构建:通过一次命令可以构建整个项目或特定模块,提高构建效率。
3. 代码复用:公共代码可以放在共享模块中,其他模块可以依赖这些共享模块,避免代码重复。
4. 模块化开发:不同功能可以分离到不同模块,使项目结构更清晰,便于团队协作。
5. 依赖管理:模块间的依赖关系清晰可见,避免循环依赖等问题。
1.3 父子项目的结构
典型的Maven父子项目结构如下:
- parent-project/
- ├── pom.xml (父POM)
- ├── module-a/
- │ └── pom.xml
- ├── module-b/
- │ └── pom.xml
- └── module-c/
- └── pom.xml
复制代码
在这个结构中,parent-project是父项目,包含三个子模块:module-a、module-b和module-c。每个子模块都有自己的pom.xml文件,同时继承自父项目的pom.xml。
2. 创建和配置Maven父子项目
2.1 创建父项目
首先,我们需要创建一个父项目。父项目的pom.xml文件需要包含packaging类型为pom,以及modules元素来列出所有子模块。
以下是一个父项目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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.example</groupId>
- <artifactId>parent-project</artifactId>
- <version>1.0.0</version>
- <packaging>pom</packaging>
- <name>Parent Project</name>
- <description>Parent project for multi-module example</description>
- <modules>
- <module>module-a</module>
- <module>module-b</module>
- <module>module-c</module>
- </modules>
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <maven.compiler.source>1.8</maven.compiler.source>
- <maven.compiler.target>1.8</maven.compiler.target>
- <spring.boot.version>2.7.0</spring.boot.version>
- <junit.version>5.8.2</junit.version>
- </properties>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-dependencies</artifactId>
- <version>${spring.boot.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <version>${junit.version}</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
- <build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- <version>${spring.boot.version}</version>
- </plugin>
- <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>
- </plugins>
- </pluginManagement>
- </build>
- </project>
复制代码
在这个父项目的pom.xml中:
1. packaging设置为pom,表示这是一个聚合项目,不产生实际的构建产物。
2. modules部分列出了所有子模块。
3. properties部分定义了一些全局属性,如源代码编码、Java版本、依赖版本等。
4. dependencyManagement部分用于管理依赖版本,子模块在使用这些依赖时无需指定版本。
5. pluginManagement部分用于管理插件配置,子模块可以继承这些配置。
2.2 创建子模块
接下来,我们创建子模块。每个子模块都是一个独立的Maven项目,但会继承父项目的配置。
以下是一个子模块module-a的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <!-- 继承父项目 -->
- <parent>
- <groupId>com.example</groupId>
- <artifactId>parent-project</artifactId>
- <version>1.0.0</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
- <artifactId>module-a</artifactId>
- <packaging>jar</packaging>
- <name>Module A</name>
- <description>Module A for multi-module example</description>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
- </project>
复制代码
在这个子模块的pom.xml中:
1. parent部分指定了父项目的坐标和相对路径,表示这个模块继承自父项目。
2. artifactId定义了当前模块的唯一标识。
3. packaging设置为jar,表示这个模块将打包成JAR文件。
4. dependencies部分列出了当前模块所需的依赖,无需指定版本,因为版本已在父项目的dependencyManagement中定义。
5. build部分可以包含特定于当前模块的插件配置。
2.3 模块间依赖
在多模块项目中,模块之间可能存在依赖关系。例如,module-b可能依赖于module-a。我们可以在module-b的pom.xml中添加对module-a的依赖:
- <?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">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.example</groupId>
- <artifactId>parent-project</artifactId>
- <version>1.0.0</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
- <artifactId>module-b</artifactId>
- <packaging>jar</packaging>
- <name>Module B</name>
- <description>Module B for multi-module example</description>
- <dependencies>
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>module-a</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-jpa</artifactId>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </project>
复制代码
在这个例子中,module-b依赖于module-a,使用${project.version}表示使用与当前项目相同的版本。
3. 多模块项目的构建与管理
3.1 构建多模块项目
Maven提供了多种方式来构建多模块项目:
1. 构建整个项目:在父项目目录下运行mvn clean install,Maven会按照模块间的依赖关系顺序构建所有模块。
2. 构建特定模块:可以使用-pl选项指定要构建的模块,例如:mvn clean install -pl module-a
3. 构建特定模块及其依赖:可以使用-am选项同时构建指定模块及其依赖的模块,例如:mvn clean install -pl module-b -am
4. 构建特定模块及依赖它的模块:可以使用-amd选项同时构建指定模块以及依赖它的模块,例如:mvn clean install -pl module-a -amd
构建整个项目:在父项目目录下运行mvn clean install,Maven会按照模块间的依赖关系顺序构建所有模块。
构建特定模块:可以使用-pl选项指定要构建的模块,例如:
- mvn clean install -pl module-a
复制代码
构建特定模块及其依赖:可以使用-am选项同时构建指定模块及其依赖的模块,例如:
- mvn clean install -pl module-b -am
复制代码
构建特定模块及依赖它的模块:可以使用-amd选项同时构建指定模块以及依赖它的模块,例如:
- mvn clean install -pl module-a -amd
复制代码
3.2 跳过特定模块
在某些情况下,我们可能需要在构建过程中跳过某些模块。可以使用-pl选项结合!或-来排除特定模块:
- mvn clean install -pl !module-c
复制代码
或者:
- mvn clean install -pl -module-c
复制代码
3.3 并行构建
Maven支持并行构建多模块项目,可以显著提高构建速度。可以使用-T选项启用并行构建:
这个命令表示使用4个线程进行并行构建。也可以使用-T C表示使用CPU核心数相同的线程数:
3.4 模块排序
Maven在构建多模块项目时,会根据模块间的依赖关系自动确定构建顺序。如果模块间没有明确的依赖关系,则按照pom.xml中modules部分的顺序进行构建。
在某些情况下,我们可能需要显式指定模块的构建顺序,可以通过在父项目的pom.xml中使用<moduleOrder>元素来实现:
- <modules>
- <moduleOrder>reactor</moduleOrder>
- <module>module-a</module>
- <module>module-b</module>
- <module>module-c</module>
- </modules>
复制代码
moduleOrder可以设置为:
• reactor(默认):根据依赖关系确定顺序
• declaration:按照声明顺序
• none:不进行排序
4. 实际应用案例
4.1 创建一个多模块的Spring Boot应用
让我们创建一个实际的多模块Spring Boot应用示例,包含以下模块:
1. parent-project:父项目
2. common:公共模块,包含共享的工具类和配置
3. model:模型模块,包含实体类和数据传输对象
4. repository:数据访问模块,包含数据库访问逻辑
5. service:服务模块,包含业务逻辑
6. web:Web模块,包含控制器和Web相关配置
首先,创建父项目的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.example</groupId>
- <artifactId>multi-module-spring-boot</artifactId>
- <version>1.0.0</version>
- <packaging>pom</packaging>
- <name>Multi Module Spring Boot</name>
- <description>Example of multi-module Spring Boot application</description>
- <modules>
- <module>common</module>
- <module>model</module>
- <module>repository</module>
- <module>service</module>
- <module>web</module>
- </modules>
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <maven.compiler.source>1.8</maven.compiler.source>
- <maven.compiler.target>1.8</maven.compiler.target>
- <spring.boot.version>2.7.0</spring.boot.version>
- <spring.cloud.version>2021.0.3</spring.cloud.version>
- <mysql.connector.version>8.0.29</mysql.connector.version>
- <lombok.version>1.18.24</lombok.version>
- <junit.version>5.8.2</junit.version>
- </properties>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-dependencies</artifactId>
- <version>${spring.boot.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-dependencies</artifactId>
- <version>${spring.cloud.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>${mysql.connector.version}</version>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <version>${lombok.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <version>${junit.version}</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
- <build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- <version>${spring.boot.version}</version>
- </plugin>
- <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>
- </plugins>
- </pluginManagement>
- </build>
- </project>
复制代码
创建common模块的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.example</groupId>
- <artifactId>multi-module-spring-boot</artifactId>
- <version>1.0.0</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
- <artifactId>common</artifactId>
- <packaging>jar</packaging>
- <name>Common</name>
- <description>Common module with shared utilities and configurations</description>
- <dependencies>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </project>
复制代码
在common模块中,我们可以创建一些共享的工具类,例如:
- package com.example.common.utils;
- import lombok.extern.slf4j.Slf4j;
- import java.time.LocalDateTime;
- import java.time.format.DateTimeFormatter;
- @Slf4j
- public class DateUtils {
-
- private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-
- public static String formatNow() {
- return formatNow(DEFAULT_FORMATTER);
- }
-
- public static String formatNow(DateTimeFormatter formatter) {
- return LocalDateTime.now().format(formatter);
- }
-
- public static LocalDateTime parse(String dateStr) {
- return parse(dateStr, DEFAULT_FORMATTER);
- }
-
- public static LocalDateTime parse(String dateStr, DateTimeFormatter formatter) {
- try {
- return LocalDateTime.parse(dateStr, formatter);
- } catch (Exception e) {
- log.error("Failed to parse date: {}", dateStr, e);
- return null;
- }
- }
- }
复制代码
创建model模块的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.example</groupId>
- <artifactId>multi-module-spring-boot</artifactId>
- <version>1.0.0</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
- <artifactId>model</artifactId>
- <packaging>jar</packaging>
- <name>Model</name>
- <description>Model module with entities and DTOs</description>
- <dependencies>
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>common</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- </dependency>
- <dependency>
- <groupId>javax.persistence</groupId>
- <artifactId>javax.persistence-api</artifactId>
- <version>2.2</version>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </project>
复制代码
在model模块中,我们可以定义实体类和DTO,例如:
- package com.example.model.entity;
- import lombok.Data;
- import javax.persistence.Entity;
- import javax.persistence.GeneratedValue;
- import javax.persistence.GenerationType;
- import javax.persistence.Id;
- import javax.persistence.Table;
- import java.time.LocalDateTime;
- @Data
- @Entity
- @Table(name = "users")
- public class User {
-
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- private String username;
-
- private String password;
-
- private String email;
-
- private LocalDateTime createdAt;
-
- private LocalDateTime updatedAt;
- }
复制代码- package com.example.model.dto;
- import lombok.Data;
- import javax.validation.constraints.Email;
- import javax.validation.constraints.NotBlank;
- import javax.validation.constraints.Size;
- @Data
- public class UserDto {
-
- private Long id;
-
- @NotBlank(message = "Username is required")
- @Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
- private String username;
-
- @NotBlank(message = "Password is required")
- @Size(min = 6, max = 100, message = "Password must be between 6 and 100 characters")
- private String password;
-
- @NotBlank(message = "Email is required")
- @Email(message = "Email should be valid")
- private String email;
- }
复制代码
创建repository模块的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.example</groupId>
- <artifactId>multi-module-spring-boot</artifactId>
- <version>1.0.0</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
- <artifactId>repository</artifactId>
- <packaging>jar</packaging>
- <name>Repository</name>
- <description>Repository module with data access logic</description>
- <dependencies>
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>common</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>model</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-jpa</artifactId>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </project>
复制代码
在repository模块中,我们可以定义数据访问接口,例如:
- package com.example.repository;
- import com.example.model.entity.User;
- import org.springframework.data.jpa.repository.JpaRepository;
- import org.springframework.stereotype.Repository;
- import java.util.Optional;
- @Repository
- public interface UserRepository extends JpaRepository<User, Long> {
-
- Optional<User> findByUsername(String username);
-
- Optional<User> findByEmail(String email);
-
- boolean existsByUsername(String username);
-
- boolean existsByEmail(String email);
- }
复制代码
创建service模块的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.example</groupId>
- <artifactId>multi-module-spring-boot</artifactId>
- <version>1.0.0</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
- <artifactId>service</artifactId>
- <packaging>jar</packaging>
- <name>Service</name>
- <description>Service module with business logic</description>
- <dependencies>
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>common</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>model</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>repository</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-validation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </project>
复制代码
在service模块中,我们可以定义业务逻辑,例如:
创建web模块的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.example</groupId>
- <artifactId>multi-module-spring-boot</artifactId>
- <version>1.0.0</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
- <artifactId>web</artifactId>
- <packaging>jar</packaging>
- <name>Web</name>
- <description>Web module with controllers and web configurations</description>
- <dependencies>
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>common</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>model</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>service</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-test</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- <configuration>
- <mainClass>com.example.web.WebApplication</mainClass>
- </configuration>
- <executions>
- <execution>
- <goals>
- <goal>repackage</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </project>
复制代码
在web模块中,我们可以定义控制器和应用程序入口,例如:
- package com.example.web.controller;
- import com.example.model.dto.UserDto;
- import com.example.service.UserService;
- import lombok.RequiredArgsConstructor;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.*;
- import javax.validation.Valid;
- import java.util.List;
- @RestController
- @RequestMapping("/api/users")
- @RequiredArgsConstructor
- public class UserController {
-
- private final UserService userService;
-
- @PostMapping
- public ResponseEntity<UserDto> createUser(@Valid @RequestBody UserDto userDto) {
- UserDto createdUser = userService.createUser(userDto);
- return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
- }
-
- @GetMapping("/{id}")
- public ResponseEntity<UserDto> getUserById(@PathVariable Long id) {
- UserDto user = userService.getUserById(id);
- return ResponseEntity.ok(user);
- }
-
- @GetMapping("/username/{username}")
- public ResponseEntity<UserDto> getUserByUsername(@PathVariable String username) {
- UserDto user = userService.getUserByUsername(username);
- return ResponseEntity.ok(user);
- }
-
- @GetMapping
- public ResponseEntity<List<UserDto>> getAllUsers() {
- List<UserDto> users = userService.getAllUsers();
- return ResponseEntity.ok(users);
- }
-
- @PutMapping("/{id}")
- public ResponseEntity<UserDto> updateUser(@PathVariable Long id, @Valid @RequestBody UserDto userDto) {
- UserDto updatedUser = userService.updateUser(id, userDto);
- return ResponseEntity.ok(updatedUser);
- }
-
- @DeleteMapping("/{id}")
- public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
- userService.deleteUser(id);
- return ResponseEntity.noContent().build();
- }
- }
复制代码- package com.example.web;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.context.annotation.ComponentScan;
- @SpringBootApplication
- @ComponentScan(basePackages = "com.example")
- public class WebApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(WebApplication.class, args);
- }
- }
复制代码
在web模块的src/main/resources目录下,创建application.yml配置文件:
- server:
- port: 8080
- spring:
- application:
- name: multi-module-spring-boot
-
- datasource:
- url: jdbc:mysql://localhost:3306/demo_db?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
- username: root
- password: password
- driver-class-name: com.mysql.cj.jdbc.Driver
-
- jpa:
- hibernate:
- ddl-auto: update
- properties:
- hibernate:
- dialect: org.hibernate.dialect.MySQL8Dialect
- format_sql: true
- show-sql: true
- logging:
- level:
- com.example: DEBUG
- org.springframework.web: INFO
复制代码
4.2 构建和运行项目
要构建整个项目,可以在父项目目录下运行以下命令:
要运行应用程序,可以在web模块目录下运行以下命令:
或者,先构建整个项目,然后运行生成的JAR文件:
- mvn clean package
- java -jar web/target/web-1.0.0.jar
复制代码
5. 高级技巧和最佳实践
5.1 统一版本管理
在多模块项目中,统一版本管理是非常重要的。我们可以通过以下几种方式实现:
1. 使用父项目的dependencyManagement:如前面示例所示,在父项目的dependencyManagement部分声明所有依赖的版本,子模块在使用时无需指定版本。
2. 使用属性定义版本:在父项目的properties部分定义版本号,然后在dependencyManagement中引用这些属性。这样可以方便地统一升级版本。
3. 使用BOM(Bill of Materials):对于一些框架(如Spring Boot、Spring Cloud),它们提供了BOM依赖,可以通过importscope导入,统一管理相关依赖的版本。
使用父项目的dependencyManagement:如前面示例所示,在父项目的dependencyManagement部分声明所有依赖的版本,子模块在使用时无需指定版本。
使用属性定义版本:在父项目的properties部分定义版本号,然后在dependencyManagement中引用这些属性。这样可以方便地统一升级版本。
使用BOM(Bill of Materials):对于一些框架(如Spring Boot、Spring Cloud),它们提供了BOM依赖,可以通过importscope导入,统一管理相关依赖的版本。
5.2 依赖范围优化
合理使用依赖范围(scope)可以避免不必要的依赖传递,减小最终构建产物的大小。常见的依赖范围有:
1. compile:默认范围,依赖在编译、测试和运行时都需要。
2. provided:依赖在编译和测试时需要,但在运行时由容器提供(如Servlet API)。
3. runtime:依赖在测试和运行时需要,但在编译时不需要(如JDBC驱动)。
4. test:依赖仅在测试时需要(如JUnit)。
5. system:类似于provided,但需要显式指定依赖的JAR文件路径。
5.3 可选依赖
使用可选依赖(optional dependencies)可以避免传递依赖带来的问题。当一个模块依赖另一个模块,但这个依赖不是所有使用场景都需要的,可以将其标记为可选:
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>optional-module</artifactId>
- <version>${project.version}</version>
- <optional>true</optional>
- </dependency>
复制代码
5.4 资源过滤
Maven支持资源过滤,可以在构建过程中替换资源文件中的占位符。这在多模块项目中特别有用,可以为不同环境(开发、测试、生产)提供不同的配置。
在父项目的pom.xml中配置资源过滤:
- <build>
- <resources>
- <resource>
- <directory>src/main/resources</directory>
- <filtering>true</filtering>
- </resource>
- </resources>
- </build>
复制代码
然后,在资源文件中使用占位符:
- server:
- port: ${server.port}
复制代码
在pom.xml中定义属性:
- <properties>
- <server.port>8080</server.port>
- </properties>
复制代码
或者通过命令行参数指定:
- mvn clean install -Dserver.port=8081
复制代码
5.5 多环境构建
Maven支持通过Profile来支持多环境构建。可以为不同的环境(开发、测试、生产)定义不同的配置,然后在构建时指定激活的Profile。
在父项目的pom.xml中定义Profile:
- <profiles>
- <profile>
- <id>dev</id>
- <activation>
- <activeByDefault>true</activeByDefault>
- </activation>
- <properties>
- <environment>dev</environment>
- <server.port>8080</server.port>
- <datasource.url>jdbc:mysql://localhost:3306/dev_db</datasource.url>
- <datasource.username>dev_user</datasource.username>
- <datasource.password>dev_password</datasource.password>
- </properties>
- </profile>
- <profile>
- <id>test</id>
- <properties>
- <environment>test</environment>
- <server.port>8080</server.port>
- <datasource.url>jdbc:mysql://test-db:3306/test_db</datasource.url>
- <datasource.username>test_user</datasource.username>
- <datasource.password>test_password</datasource.password>
- </properties>
- </profile>
- <profile>
- <id>prod</id>
- <properties>
- <environment>prod</environment>
- <server.port>80</server.port>
- <datasource.url>jdbc:mysql://prod-db:3306/prod_db</datasource.url>
- <datasource.username>prod_user</datasource.username>
- <datasource.password>prod_password</datasource.password>
- </properties>
- </profile>
- </profiles>
复制代码
构建时指定Profile:
5.6 模块聚合的最佳实践
在设计多模块项目结构时,应该遵循一些最佳实践:
1. 合理划分模块:根据功能或层次划分模块,每个模块应该有明确的职责。
2. 避免循环依赖:模块间的依赖关系应该是单向的,避免循环依赖。
3. 最小化依赖范围:模块应该只依赖它真正需要的模块,避免不必要的依赖。
4. 使用接口隔离:模块间通过接口交互,而不是直接依赖实现类。
5. 考虑部署需求:根据部署需求设计模块结构,例如,可能需要将Web层和业务层分开部署。
5.7 持续集成与多模块项目
在持续集成环境中构建多模块项目时,可以考虑以下策略:
1. 增量构建:当只有部分模块的代码发生变化时,只构建这些模块及其依赖的模块。
2. 并行构建:使用Maven的并行构建功能,充分利用多核CPU提高构建速度。
3. 构建缓存:使用构建缓存(如Maven的本地仓库或远程缓存)避免重复构建未变化的模块。
4. 构建分析:使用Maven的构建分析工具(如maven-dependency-plugin)分析依赖关系,发现潜在问题。
6. 常见问题与解决方案
6.1 循环依赖问题
循环依赖是指两个或多个模块之间相互依赖,例如:模块A依赖模块B,同时模块B也依赖模块A。这种情况会导致Maven无法确定构建顺序,从而构建失败。
解决方案:
1. 重构代码:将共同依赖的代码提取到一个新的模块中,使原来的模块都依赖这个新模块。
2. 使用接口:定义接口模块,实现模块依赖接口模块,而不是直接相互依赖。
3. 使用事件机制:通过事件机制实现模块间的通信,而不是直接调用。
6.2 版本冲突问题
在多模块项目中,不同模块可能依赖同一个库的不同版本,导致版本冲突。
解决方案:
1. 使用父项目的dependencyManagement:在父项目中统一管理依赖版本。
2. 使用mvn dependency:tree:分析依赖树,找出冲突的依赖。
3. 使用<exclusions>:排除传递依赖中不需要的版本。
4. 使用<dependencyManagement>:在子模块中覆盖父项目定义的版本。
6.3 构建速度慢问题
随着模块数量的增加,多模块项目的构建时间可能会变长。
解决方案:
1. 使用并行构建:通过-T选项启用并行构建。
2. 增量构建:只构建发生变化的模块及其依赖的模块。
3. 优化测试:减少不必要的测试,或者将测试分类,只运行必要的测试。
4. 使用构建缓存:使用Maven的本地仓库或远程缓存。
5. 优化插件配置:禁用不必要的插件,或者优化插件配置。
6.4 模块间通信问题
在多模块项目中,模块间的通信可能会变得复杂,特别是在分布式部署的情况下。
解决方案:
1. 使用API模块:定义专门的API模块,包含接口和数据传输对象。
2. 使用消息队列:通过消息队列实现异步通信。
3. 使用REST API:通过HTTP REST API实现模块间通信。
4. 使用服务注册与发现:在微服务架构中,使用服务注册与发现机制。
7. 总结
Maven父子项目(多模块项目)是一种强大的项目组织结构,特别适合大型复杂项目的开发和管理。通过合理地划分模块,统一管理依赖和配置,可以显著提高开发效率和代码质量。
在实际应用中,我们应该根据项目的具体需求,合理设计模块结构,遵循最佳实践,避免常见问题。同时,充分利用Maven提供的各种功能,如依赖管理、并行构建、多环境构建等,优化构建过程,提高开发效率。
通过本文的介绍,相信读者已经对Maven父子项目有了深入的了解,并能够在实际项目中灵活应用这些知识和技巧,构建高效、可维护的多模块项目。 |
|