Maven高级

分模块设计与开发

1. 分模块设计的核心概念

A. 模块(Module)的定义

一个模块通常是一个独立的代码单元,它具有单一的、明确的职责。在 Maven 或 Gradle 项目中,一个模块通常对应一个子项目(sub-project)。

B. 核心目标

  1. 解耦 (Decoupling): 降低模块之间的依赖程度。

  2. 复用 (Reusability): 将通用功能提取出来,供多个模块共享。

  3. 可维护性 (Maintainability): 模块独立,修改一个模块不轻易影响其他模块。

  4. 团队协作 (Collaboration): 不同的团队或个人可以并行开发不同的模块。

🧱 2. 典型的模块划分方法

在设计时,模块可以根据技术层次(三层架构)或业务功能(微服务)进行划分。

方案一:基于技术层次(传统三层架构)

这种划分适用于中小型单体应用,便于管理代码结构。

模块名称职责定位典型内容
xxx-api (或 xxx-interface)定义接口和模型。 存放 DTO/VO/Entity 等数据模型,以及供外部调用的服务接口。不包含任何业务实现。Java 接口 (UserService.java)、数据传输对象 (UserDTO.java)。
xxx-service业务逻辑实现层。 实现 xxx-api 定义的接口,包含核心业务逻辑、事务处理等。UserServiceImpl.java、业务验证代码。
xxx-dao (或 xxx-repository)数据访问层。 负责与数据库的交互,封装 SQL/持久化操作。MyBatis Mapper 接口、JPA Repository 接口。
xxx-web (或 xxx-controller)用户接口层。 接收和响应 HTTP 请求,调用 xxx-service 层的接口。Spring MVC 控制器 (UserController.java)。

方案二:基于业务功能(微服务/领域驱动)

这种划分更适用于大型、复杂的企业应用,追求极致的解耦和独立部署。

模块名称职责定位典型内容
order-service仅处理所有与订单相关的业务逻辑。OrderController, OrderService, OrderMapper 等全部相关代码。
user-service仅处理所有与用户相关的业务逻辑(注册、登录、权限)。UserController, UserService, UserRepository 等全部相关代码。
common-utils存放所有业务都需要的通用工具日期处理工具、加密工具、自定义异常类等。

🛠️ 3. 分模块开发的关键实践

实践 A: 依赖管理

  • 单向依赖: 模块之间的依赖关系应该是清晰且单向的。例如,order-service 可以依赖 user-serviceapi 模块,但不能反过来。

  • 父子项目 (Parent-Child): 使用 Maven 或 Gradle 的父项目来统一管理所有子模块的公共依赖版本(使用 <dependencyManagement>),避免版本冲突。

实践 B: 接口隔离原则 (Interface Segregation Principle)

  • 如果两个模块需要通信,只让它们依赖彼此的 API/Interface 模块,而不是依赖具体的实现模块(xxx-service)。这样,即使修改了实现细节,也不会影响依赖方。

实践 C: 独立测试

  • 每个模块都应具备独立的单元测试和集成测试能力,确保在修改一个模块后,不会引入新的回归错误。

实践 D: 服务的发现与通信(在微服务中)

  • 如果采用业务划分,模块间通常通过 HTTP/RPC(如 Feign, gRPC)进行网络通信,并通过 服务注册中心(如 Eureka, Nacos)进行服务的发现和负载均衡。


总结: 分模块设计是将“大问题”分解为“小问题”的过程,通过清晰的边界和依赖关系,极大地提高了项目的质量和开发效率。

继承与聚合

1. 继承(Inheritance)

继承主要用于解决 配置共享版本统一 的问题。

概念

通过继承,子模块可以从父 POM 中获取通用的配置信息,从而避免在每个子模块中重复定义。

核心机制

元素作用解释
子模块:<parent>建立继承关系子模块的 pom.xml 中必须使用 <parent> 标签指向父模块的 groupId, artifactIdversion
父模块:<dependencyManagement>统一依赖版本父 POM 在 <dependencyManagement> 中定义依赖的版本号。子模块引用该依赖时,无需指定版本号,版本由父 POM 继承而来。
父模块:<pluginManagement>统一插件配置类似于依赖管理,统一管理插件的版本和通用配置。
父模块:<properties>共享变量定义公共变量(如 JDK 版本、Spring 版本),供所有子模块引用。

2. 聚合(Aggregation)

聚合主要用于解决 统一构建 的问题。

概念

通过聚合,一个父 POM 可以将多个子模块组织在一起,形成一个单一的构建单元。当你对父 POM 执行构建命令(如 mvn install)时,Maven 会自动依次构建所有被聚合的子模块。

核心机制

元素作用解释
父模块:<packaging>pom</packaging>标记聚合/父项目聚合模块的打包方式必须是 pom。这意味着父模块本身不产生任何可执行文件(如 JAR 或 WAR)。
父模块:<modules>列出子模块父 POM 使用 <modules> 标签列出所有需要被聚合和构建的子模块的相对目录。

聚合的优势

执行 mvn install 命令时:

  1. Maven 找到父 POM 中的 <modules> 列表。

  2. 它会根据模块的依赖关系(如果 service 依赖 api),自动确定正确的构建顺序。

  3. 然后 Maven 会按照这个顺序,从上到下依次执行 clean install 命令。

3. 继承与聚合的关系

在实际的多模块项目中,继承和聚合通常是结合在一起使用的

  1. 一个父 POM<packaging>pom</packaging>)同时负责:

    • 聚合: 通过 <modules> 管理所有子模块的构建顺序。

    • 继承: 通过 <dependencyManagement><properties> 为所有子模块提供统一的配置。

  2. 所有子模块<packaging>jar</packaging>war)都通过 <parent> 标签继承自这个父 POM。

这种结构确保了整个项目既有统一的构建流程(聚合),又有统一的技术配置(继承),是标准 Maven 多模块项目的最佳实践。

 4. Maven 中的版本锁定机制

核心标签:<dependencyManagement>

<dependencyManagement> 标签位于父 POM 中,专门用于集中管理项目所有依赖的版本号。

  1. 定义版本,但不引入依赖: 当你在 <dependencyManagement> 中声明一个依赖时,它不会将该依赖实际引入到项目中。它只是定义了一个版本查找表

  2. 子模块继承: 所有子模块都继承这个版本表。

  3. 子模块使用: 子模块在自己的 <dependencies> 中再次声明需要的依赖时,无需填写版本号。Maven 会自动从父 POM 的 <dependencyManagement> 中查找并使用锁定的版本。

为什么需要版本锁定?

  • 避免冲突: 如果项目中有多个模块都使用了 log4j,但在不同模块中版本不一致(一个用 2.17.0,一个用 2.19.0),可能会导致运行时错误或依赖地狱。

  • 统一升级: 当需要升级某个核心依赖(如 Spring 版本)时,只需要修改父 POM 中的一个版本号,所有子模块都会自动同步。

  • 清晰可见: 所有关键依赖的版本号集中在一个地方管理,便于审计和维护。

父模块中的自定义属性

 5. 版本锁定与依赖引入的区别

机制标签作用结果
版本锁定<dependencyManagement> (在父 POM)声明依赖的版本,供子模块继承和查找。不引入实际的 JAR 包到 Classpath。
依赖引入<dependencies> (在子 POM)声明模块实际需要的依赖。引入实际的 JAR 包到 Classpath。

💡 Spring Boot 的版本锁定

对于 Spring Boot 项目,其版本锁定机制更为强大和自动化:

当你继承 Spring Boot 的父级项目 spring-boot-starter-parent 时,它内部已经包含了庞大的 <dependencyManagement> 块,默认就锁定了几乎所有主流依赖的版本(如 Spring Framework、Tomcat、Thymeleaf、Logback 等)。

什么是私服 (Private Repository)?

私服充当了开发者和外部公共仓库(如 Maven Central)之间的代理和缓存层

常用工具

实现私服的常见软件有:

  • Nexus Repository Manager (Sonatype Nexus)

  • JFrog Artifactory

私服的三大核心功能

  1. 代理(Proxy): 代理外部的公共仓库(如 Maven Central、JCenter)。

  2. 缓存(Cache): 存储从外部下载的依赖,供内部快速重用。

  3. 宿主(Host): 存储和发布企业内部自己开发的、需要共享的模块或组件。


为什么企业需要使用私服?

使用私服带来了多方面的优势,尤其对于大型团队和持续集成/部署 (CI/CD) 环境至关重要。

1. 提升构建速度和稳定性

  • 加速构建: 第一次下载依赖后,私服会将其缓存到本地服务器。团队中的其他成员再次请求时,直接从内网的私服下载,速度远快于从国外公共仓库下载。

  • 网络稳定: 避免因公共仓库网络波动或限制而导致的构建失败。

2. 安全性与合规性

  • 安全扫描: 可以集中管理依赖,方便进行安全漏洞扫描(如检测 Log4j 漏洞),避免使用带有已知安全问题的组件。

  • 隔离外部风险: 只有私服需要连接外部网络,开发机可以完全隔离在内网中,提高了安全级别。

  • 审核与控制: 可以阻止团队成员下载和使用某些不符合企业规范的依赖。

3. 共享内部组件(宿主功能)

  • 内部协作: 这是实现 分模块开发微服务 的关键。企业内部团队开发的公共库(如 common-utilsuser-api)可以发布到私服上。

  • 其他项目可以直接通过依赖的方式,从私服拉取这些内部组件,实现组件的快速复用。


🛠️ 私服的工作流程(以 Maven 为例)

release版本:发行版本

snapshot版本:快照版本

  1. 配置: 开发者修改 Maven 的 settings.xml 文件,将默认的公共仓库地址替换为私服的地址。

  2. 请求: 当开发者执行 mvn compile 时,Maven 向内部的私服请求所需的依赖。

  3. 私服处理:

    • 命中缓存? 如果私服的缓存中存在该依赖,直接返回给开发者。

    • 未命中? 私服作为代理,会去连接配置好的外部公共仓库(如 Maven Central)下载依赖。

    • 缓存: 下载成功后,私服将依赖缓存起来。

    • 返回: 将依赖返回给开发者。

  4. 发布: 当开发者需要发布自己的内部组件时,执行 mvn deploy,组件会被上传到私服的宿主仓库中。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值