前言

在Java开发的浩瀚宇宙中,有一类工具,它们不像Spring那样光芒万丈,也不像Redis那样身负绝技,但它们却是所有Java项目的“地基”和“血管”。它们就是项目管理与构建工具——Maven和Gradle。

想象一下,如果你正在开发一个电商系统,需要用到Spring、MyBatis、Log4j、MySQL驱动等几十个甚至上百个第三方库。如果没有一个统一的管理工具,你需要:

  1. 手动去官网下载每个库的Jar包。

  2. 手动处理Jar包之间的依赖关系(比如Spring Core依赖于Commons Logging)。

  3. 手动将这些Jar包复制到项目的lib目录下。

  4. 当项目需要打包部署时,手动执行javac命令编译源代码,再将所有Jar包一起打包成可运行的JAR或WAR文件。

这个过程简直是一场噩梦,我们称之为 “依赖地狱” 。任何一个环节出错,都可能导致项目无法编译或运行。而Maven和Gradle的出现,就是为了彻底终结这场噩梦。

本文将带领您从零开始,全面剖析Maven/Gradle的核心知识,并深入讲解企业级必备的Nexus私服搭建,最终让你不仅能“会用”,更能“用好”它们,在团队协作和项目构建中游刃有余。


第一阶段:问题锚定 —— 为什么我们要学习项目管理工具?

1. 场景化抛出问题:企业开发中的真实痛点

让我们回到一个真实的开发场景。

假设你是一个初创公司的技术负责人,团队有5名开发人员。你们要开发一个在线商城系统,技术栈定为:Spring Boot 2.6.6 + MyBatis-Plus 3.5.0 + MySQL 8.0 + JUnit 5。

在没有任何项目管理工具的情况下,你们的开发过程会是这样:

  • 痛点一:依赖下载与传递

    • 开发人员A 需要Spring Boot,他去官网下载了spring-boot-2.6.6.jar,放到项目的lib目录下,并告诉团队成员“我下载好了,你们从我这里拷贝一份吧”。

    • 开发人员B 在写MyBatis-Plus的代码时,发现mybatis-plus-core-3.5.0.jar依赖了mybatis-3.5.9.jar,他需要再去官网手动下载这个依赖。他可能根本不知道MyBatis-Plus还依赖于MyBatis本身,导致代码运行时一直报ClassNotFoundException

    • 更可怕的是,Spring Boot 2.6.6 又依赖了 spring-corespring-contextspring-aop 等几十个Jar包,而且这些Jar包之间还有复杂的版本兼容关系。如何确保所有依赖的版本都是兼容的?这对任何一个开发人员来说都是不可能完成的任务。

  • 痛点二:版本不一致问题

    • 开发人员A使用了自己下载的commons-lang3-3.12.0.jar,而开发人员B从其他地方拷贝了一份commons-lang3-3.10.jar。两个Jar包版本不同,功能上可能有细微差异。A写了一个依赖3.12版本新API的代码,B的机器上由于是旧版本,代码无法编译。每次遇到这种问题,都需要花大量时间去排查和同步。

  • 痛点三:构建与打包的混乱

    • 项目开发完成后,需要部署到服务器。开发人员C写了一个脚本,用javac命令编译所有.java文件,然后再用jar命令将编译好的.class文件和所有依赖打包成一个可执行的JAR包。

    • 但问题来了:javac命令需要指定-classpath参数,把所有依赖的Jar包路径都列出来。当依赖项有几十个时,这个命令会变得极其冗长且容易出错。而且,每次新增或删除一个依赖,都需要手动修改这个脚本。

    • 最终,每个开发人员可能都有一套自己的打包方式,导致打出来的包结构不同,在测试环境和生产环境运行结果不一致。

总结一下,没有项目管理工具,我们面临的核心问题是:

  • 依赖管理难:无法自动下载、管理和传递依赖。

  • 构建过程复杂且不统一:编译、测试、打包、部署等生命周期管理混乱。

  • 项目信息不透明:项目结构、开发人员、代码仓库等信息分散,无法统一描述。

2. 技术定位梳理:Maven/Gradle的核心价值

Maven和Gradle正是为了解决上述痛点而生的。它们的核心价值可以用一句话概括:标准化、自动化、声明式的项目管理与构建工具。

  • Maven:以 “约定优于配置” 为核心哲学。它定义了标准化的项目结构(如src/main/javasrc/test/java)、标准化的构建生命周期(cleancompiletestpackageinstalldeploy)和标准化的依赖管理模型(通过pom.xml文件)。只要遵循Maven的约定,任何开发者都能快速上手一个项目,理解其结构,并执行统一的构建命令。

  • Gradle:以 “灵活性” 和 “性能” 著称。它继承了Maven的很多优点,但放弃了XML配置,转而使用基于Groovy或Kotlin的DSL(领域特定语言)来编写构建脚本(build.gradle)。这使得Gradle的配置更加简洁、灵活和强大。同时,它引入了 增量构建 和 构建缓存 等特性,极大地提升了大型项目的构建速度。

3. 技术边界说明:它们不是什么?

虽然Maven和Gradle非常强大,但它们并非万能,我们需要明确它们能做什么、不能做什么:

  • 能做什么

    • 依赖管理:自动从中央仓库或私服下载项目所需的第三方库,并自动处理它们之间的传递性依赖。

    • 构建管理:将项目从源代码编译、测试、打包、部署的整个流程自动化,并提供可插拔的插件机制来扩展功能。

    • 项目管理:通过统一的项目对象模型(POM),记录项目的基本信息(如项目名称、版本、开发者、SCM地址等),方便管理和协作。

    • 多模块管理:支持将大型项目拆分成多个独立的子模块(子项目),并统一管理它们的构建和依赖关系。

  • 不能做什么

    • 不能替代版本控制系统(如Git):它管理的是项目的构建过程和依赖,而不是代码的版本历史。

    • 不能替代代码编辑器/IDE:它不提供代码编辑、调试、重构等功能。这些功能由IDE(如IntelliJ IDEA、Eclipse)提供,但IDE会深度集成Maven/Gradle,利用它们的信息来辅助开发。

    • 不能解决所有构建问题:对于一些非常复杂的、非标准的构建逻辑(如生成代码、执行特定脚本),你可能需要编写自定义插件或任务来扩展其能力。


第二阶段:基础认知 —— 从“Hello World”开始

理解了“为什么学”,现在我们正式进入“是什么”的阶段。我们以Maven为例,因为它约定优于配置的理念非常适合新手理解,然后再对比Gradle的异同。

1. 核心概念拆解(用大白话解释)

  • 项目对象模型 (Project Object Model, POM):这是Maven的核心。你可以把它想象成一张“项目身份证”。这张身份证上记录了项目的所有信息:名字是什么(groupId:artifactId)、当前版本是多少(version)、依赖了哪些第三方库(dependencies)、用什么插件来打包(plugins)等。在Maven中,这张身份证就是根目录下的pom.xml文件。

  • 坐标 (Coordinates):这是Maven用来唯一标识一个项目的“经纬度”。一个项目(或一个Jar包)由三个坐标唯一定位:

    • groupId:通常代表组织或公司,如org.springframework

    • artifactId:代表具体的项目名,如spring-core

    • version:代表项目的版本,如5.3.20
      通过这三个坐标,Maven就能在全球的仓库中找到你需要的那个Jar包。

  • 仓库 (Repository):这是存放所有Jar包和插件的地方。Maven有三种仓库:

    • 本地仓库:在你自己的电脑上。Maven下载的所有Jar包都会保存在这里(默认在用户目录下的.m2/repository)。同一个包只需下载一次,即可供你电脑上的所有项目使用。

    • 中央仓库:由Maven社区维护的全球公共仓库。当你本地仓库没有某个依赖时,Maven会自动从中央仓库下载。这是默认的下载源。

    • 远程仓库/私服:公司内部搭建的私有仓库。用于存放公司内部的公共Jar包,或者作为中央仓库的镜像,加速下载。我们将在第五阶段重点讲解。

  • 生命周期 (Lifecycle):Maven将项目的构建过程抽象成一套标准的生命周期。它定义了一系列阶段,当你运行一个阶段时,这个阶段之前的所有阶段都会自动执行。

    • Clean Lifecycle:清理,包含pre-cleancleanpost-cleanmvn clean会删除项目的target目录。

    • Default Lifecycle:构建,这是最核心的。包含了:

      • compile:编译src/main/java下的源代码。

      • test:运行src/test/java下的单元测试。

      • package:打包,将编译后的代码打包成JAR或WAR文件。

      • install:将打好的包安装到本地仓库,供本机其他项目使用。

      • deploy:将打好的包部署到远程仓库(私服),供团队其他成员使用。

    • Site Lifecycle:生成项目站点文档。

2. 最小可运行示例:创建一个Maven项目

我们通过一个最简单的“Hello World”来演示Maven的威力。

步骤一:创建项目目录和pom.xml文件

在你的工作目录下,创建一个名为my-first-maven的文件夹,并在其中创建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>my-first-maven</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- 我们依赖一个第三方库:Apache Commons Lang -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
        <!-- 我们依赖JUnit进行单元测试 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.8.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.8.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 使用maven-surefire-plugin来运行测试 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
        </plugins>
    </build>
</project>
步骤二:创建符合Maven约定的目录结构

在项目根目录下,创建以下目录:

my-first-maven/
├── pom.xml
└── src/
    ├── main/
    │   └── java/          # 存放主代码
    │       └── com/
    │           └── example/
    │               └── App.java
    └── test/
        └── java/          # 存放测试代码
            └── com/
                └── example/
                    └── AppTest.java
步骤三:编写Java代码

src/main/java/com/example/App.java中:

package com.example;

import org.apache.commons.lang3.StringUtils;

public class App {
    public static void main(String[] args) {
        String message = "Hello, Maven!";
        // 使用我们引入的commons-lang3库中的方法
        String reversed = StringUtils.reverse(message);
        System.out.println("Original: " + message);
        System.out.println("Reversed: " + reversed);
    }
}

src/test/java/com/example/AppTest.java中:

package com.example;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class AppTest {

    @Test
    public void testMain() {
        // 一个简单的测试用例,验证永远不会失败
        assertTrue(true);
    }
}
步骤四:执行Maven命令

打开终端,进入my-first-maven目录,执行以下命令:

  • 编译mvn compile

    • Maven会下载pom.xml中声明的commons-lang3及其依赖(如果有的话),然后编译我们的App.java。你会看到控制台输出大量的下载信息(如果是第一次运行),然后显示BUILD SUCCESS

  • 测试mvn test

    • 它会先执行compile,然后运行src/test/java下的所有测试类。

  • 打包mvn package

    • 它会先执行test,然后将编译后的App.class和其他资源打包成一个JAR文件,输出到target目录下,文件名为my-first-maven-1.0-SNAPSHOT.jar

  • 运行打包后的程序

    • java -cp target/my-first-maven-1.0-SNAPSHOT.jar com.example.App

    • 你会看到输出:

      text

      Original: Hello, Maven!
      Reversed: !nevaM ,olleH

这就是Maven的第一次亮相。我们仅通过一个pom.xml文件,就完成了项目的定义、依赖的引入和自动下载、标准化的编译、测试和打包流程。整个过程无需任何手动操作。

3. Gradle的初体验

Gradle的哲学是“灵活性”。让我们用Gradle重新实现上面的Hello World。

步骤一:创建项目目录和build.gradle文件

创建一个新的文件夹my-first-gradle,在其中创建build.gradle文件:

plugins {
    id 'java'
    id 'application'
}

group = 'com.example'
version = '1.0-SNAPSHOT'

repositories {
    // 声明依赖的仓库来源
    mavenCentral()
}

dependencies {
    // 声明依赖
    implementation 'org.apache.commons:commons-lang3:3.12.0'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
}

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

tasks.test {
    useJUnitPlatform()
}

application {
    mainClass = 'com.example.App'
}
步骤二:创建源代码目录(与Maven相同)

创建同样的目录结构:

my-first-gradle/
├── build.gradle
└── src/
    ├── main/
    │   └── java/
    │       └── com/
    │           └── example/
    │               └── App.java
    └── test/
        └── java/
            └── com/
                └── example/
                    └── AppTest.java

Java代码与Maven示例完全一致。

步骤三:执行Gradle命令
  • 编译gradle compileJava 或 gradle build(build会包含测试和打包)

  • 测试gradle test

  • 打包gradle jar 或 gradle build

  • 运行gradle run

Maven vs Gradle 对比

  • 配置语言:Maven使用XML,结构清晰但冗长;Gradle使用Groovy/Kotlin DSL,代码量更少,可读性更强,支持逻辑编程。

  • 性能:Gradle的增量构建和守护进程(Gradle Daemon)使其在大型项目上比Maven快很多。

  • 灵活性:Gradle允许你直接编写任务(Task)来自定义构建逻辑,而Maven则更依赖插件。


第三阶段:核心用法拆解 —— 实战中的依赖与插件

理解了基础,我们来看看在实际开发中,如何“用好”Maven/Gradle的依赖和插件。

1. 按场景拆解依赖管理

依赖管理是构建工具最核心的功能。在pom.xmlbuild.gradle中声明依赖,工具会自动帮你下载和引用。但依赖的配置远不止“声明”这么简单。

核心场景 Maven用法 (pom.xml) Gradle用法 (build.gradle) 说明与示例
添加一个依赖 <dependency> <groupId>...</groupId> <artifactId>...</artifactId> <version>...</version> </dependency> implementation 'group:artifact:version' 最基础的用法,声明项目的直接依赖。
指定依赖作用域 <scope>test</scope>
<scope>provided</scope>
testImplementation
compileOnly
作用域决定了依赖在什么阶段可用
• test:只在测试代码中生效,不会被打包进最终产物。如JUnit。
• provided:表示JDK或容器(如Tomcat)已经提供了,打包时不需要包含。如servlet-api
• compile(Maven默认)/ implementation(Gradle默认):对所有阶段都有效,会参与打包。
管理传递性依赖 <exclusions> exclude 有时候一个依赖会隐式地引入另一个你不想要的依赖。例如,你引入A,A又依赖了B,但B的版本和你项目中的其他部分冲突,你可以排除掉A引入的B。
统一版本管理 <dependencyManagement> dependency 块配合 ext 或 catalog 多模块项目中的最佳实践。在父项目中定义一个dependencyManagement块,声明所有子模块可能用到的依赖版本,子模块只需声明groupIdartifactId,无需声明version,确保版本统一。

2. 关键插件使用说明

插件是Maven/Gradle功能的延伸。没有插件,它们只能做最基础的编译和打包。

插件名称 作用 Maven用法 Gradle用法
maven-surefire-plugin 运行单元测试,并生成测试报告 <plugin>...<artifactId>maven-surefire-plugin</artifactId></plugin> 默认集成了,但可以通过tasks.test配置
spring-boot-maven-plugin 将Spring Boot应用打包成可执行的JAR/WAR,并管理依赖版本 <plugin>...<artifactId>spring-boot-maven-plugin</artifactId></plugin> id 'org.springframework.boot' version '...'
maven-compiler-plugin 指定Java编译版本 <plugin>...<artifactId>maven-compiler-plugin</artifactId><configuration>... 通过java { sourceCompatibility = ... }配置
maven-source-plugin 将源代码打包成JAR,用于发布到私服 <plugin>...<artifactId>maven-source-plugin</artifactId></plugin> id 'java' 插件自带,可通过java.sourceSets配置
maven-jar-plugin / maven-war-plugin 自定义JAR/WAR包名、Manifest信息等 基础插件,配置方式类似 基础插件,通过jar { ... } 或 war { ... } 任务配置

场景示例:使用Spring Boot插件构建一个可运行的JAR

对于一个Spring Boot项目,你几乎不需要做任何特殊的配置,只需引入Spring Boot的父POM或插件,然后执行mvn packagegradle bootJar,它就会自动将你的应用和所有依赖打成一个独立的、可直接通过java -jar运行的JAR文件。

3. 常见坑点演示

  • 坑点一:依赖版本冲突

    • 现象:你的项目引入了A和B,A依赖了C的1.0版本,B依赖了C的2.0版本,而C的1.0和2.0接口不兼容。编译通过,但运行时抛出NoSuchMethodError

    • 解决方案:使用Maven的mvn dependency:tree或Gradle的gradle dependencies命令查看依赖树,找出冲突。然后在pom.xml中使用<exclusions>排除一个版本,或使用<dependencyManagement>强制指定C的版本。

  • 坑点二:依赖下载缓慢或失败

    • 现象:第一次构建时,下载Jar包非常慢,或者由于网络问题(如GFW)导致某些包下载失败。

    • 解决方案:配置国内镜像。在Maven的settings.xml或Gradle的init.gradle中配置阿里云镜像或其他国内仓库。这是企业开发中必须做的一步。

  • 坑点三:pom.xml文件写错导致IDE无法解析

    • 现象:在IDE中修改pom.xml后,出现大量红色波浪线,IDE无法导入依赖。

    • 解决方案:检查XML语法是否正确(标签是否闭合),检查网络连接,尝试在IDE中刷新Maven项目(通常是“Reimport”按钮),或者手动执行mvn clean install刷新本地仓库。


第四阶段:场景融合 —— 与Spring Boot、多模块协作

现在,我们把Maven/Gradle放到更真实的业务场景中,看它们如何与其他技术协同工作。

1. 业务流程串联:Spring Boot项目的完整构建过程

想象你正在开发一个Spring Boot微服务。整个开发、构建、部署流程如下:

  1. 代码编写:你在IDE中编写代码,IDE通过解析pom.xml(或build.gradle),知道项目依赖了Spring Boot的Starter包,所以当你写@RestController@Autowired时,IDE能正确识别并提供代码补全。

  2. 本地测试:你在本地执行mvn test。Maven会:

    • 启动Spring Boot的测试上下文(通过spring-boot-starter-test)。

    • 运行你写的所有单元测试和集成测试。

    • 如果测试失败,构建失败,阻止代码提交。

  3. 本地构建与打包:你执行mvn package

    • Maven会先执行测试(确保质量),然后调用spring-boot-maven-plugin

    • 该插件会将编译后的App.class和你项目中所有依赖的Jar包(如spring-coretomcat-embed等)全部解压,然后重新打包成一个名为my-app-1.0.0.jar的 “胖Jar”

    • 这个胖Jar的META-INF/MANIFEST.MF文件中指定了Main-Class: org.springframework.boot.loader.JarLauncher,使得它能被java -jar直接运行。

  4. 提交与CI/CD:你将代码提交到Git仓库(如GitLab)。CI/CD服务器(如Jenkins)检测到代码变更后,自动拉取最新代码,然后在服务器上执行mvn clean package,生成最终的Jar包。

  5. 部署与运行:Jenkins将生成的Jar包通过SSH或Docker镜像的方式,部署到测试环境或生产环境的服务器上,并执行java -jar my-app-1.0.0.jar启动应用。

在这个流程中,Maven/Gradle就像一条无形的纽带,连接了代码编写、单元测试、持续集成和部署发布,保证了整个过程的自动化和标准化。

2. 技术选型对比:Maven vs Gradle

对比维度 Maven Gradle
学习曲线 平缓,XML配置易于理解,约定优于配置,适合新手。 较陡峭,DSL语法需要学习,灵活性带来了复杂度。
配置复杂度 简单项目配置清晰;复杂逻辑(如条件化依赖、自定义生命周期)需要通过插件或Maven Profile实现,配置较繁琐。 非常灵活,可以在build.gradle中编写Groovy/Kotlin代码实现复杂逻辑。
构建性能 中等,每次构建都是相对完整的生命周期。 高,基于任务的依赖图、增量构建、构建缓存和守护进程,在大项目上速度优势明显。
多模块支持 通过<parent><modules>,标准且强大。 通过settings.gradleinclude,同样强大,且配置更简洁。
生态系统 历史悠久,插件生态极其丰富。 后来居上,Android官方支持,Spring Boot也全面支持,插件数量增长迅速。
选择建议 绝大多数传统企业级项目、开源项目(如Spring、Hibernate本身)的首选。当项目构建逻辑相对标准、团队成员对Maven熟悉时,选它没错。 适用于对构建性能有极高要求的超大型项目、构建逻辑复杂、需要深度定制,或是Android开发。

第五阶段:企业级实战 —— 子父工程与多模块打包

在企业级开发中,一个项目通常被拆分成多个模块,如common(公共模块)、api(接口定义)、service(业务实现)、web(前端控制器)等。这种拆分利于解耦、复用和团队协作。这就要用到Maven/Gradle的子父工程多模块打包功能。

1. 实战项目:构建一个多模块的电商系统

假设我们要开发一个简化版的电商系统,拆分为以下模块:

  • ecom-parent:父工程,管理所有公共配置和依赖版本。

  • ecom-common:公共模块,包含工具类、常量、通用异常等。

  • ecom-dao:数据访问层模块,依赖于ecom-common,包含MyBatis的Mapper接口和实体类。

  • ecom-service:业务逻辑层模块,依赖于ecom-dao

  • ecom-web:Web层模块,依赖于ecom-service,提供RESTful API。

步骤一:创建父工程 ecom-parent

Maven 方式:父工程的pom.xml,它的packaging必须是pom

<?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.ecom</groupId>
    <artifactId>ecom-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <properties>
        <java.version>11</java.version>
        <spring-boot.version>2.6.6</spring-boot.version>
        <mybatis-plus.version>3.5.0</mybatis-plus.version>
        <lombok.version>1.18.22</lombok.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <!-- 依赖版本管理,子模块继承后无需指定版本 -->
    <dependencyManagement>
        <dependencies>
            <!-- Spring Boot 统一版本管理 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- MyBatis Plus -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <!-- Lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- 声明子模块 -->
    <modules>
        <module>ecom-common</module>
        <module>ecom-dao</module>
        <module>ecom-service</module>
        <module>ecom-web</module>
    </modules>
</project>

Gradle 方式:父工程的settings.gradlebuild.gradle

settings.gradle:

rootProject.name = 'ecom-parent'
include 'ecom-common'
include 'ecom-dao'
include 'ecom-service'
include 'ecom-web'

父工程build.gradle:

groovy

// 所有子模块通用的配置
subprojects {
    apply plugin: 'java'
    apply plugin: 'io.spring.dependency-management'

    group = 'com.ecom'
    version = '1.0.0-SNAPSHOT'

    java {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }

    repositories {
        mavenCentral()
    }

    // 版本管理
    dependencyManagement {
        imports {
            mavenBom "org.springframework.boot:spring-boot-dependencies:2.6.6"
        }
    }
}
步骤二:创建子模块

每个子模块都是一个独立的Maven项目,需要在其pom.xml中声明父项目。

ecom-common模块为例:

Maven ecom-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">
    <parent>
        <artifactId>ecom-parent</artifactId>
        <groupId>com.ecom</groupId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ecom-common</artifactId>

    <dependencies>
        <!-- 无需指定版本,从父工程的dependencyManagement中继承 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>
</project>

ecom-dao模块需要依赖ecom-common模块,同时需要MyBatis-Plus依赖。

Maven ecom-dao/pom.xml:

<parent>...</parent>
<artifactId>ecom-dao</artifactId>
<dependencies>
    <dependency>
        <groupId>com.ecom</groupId>
        <artifactId>ecom-common</artifactId>
        <version>${project.version}</version> <!-- 引用父工程版本 -->
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>
    <!-- 其他如MySQL驱动等 -->
</dependencies>
步骤三:多模块打包构建

在父工程根目录下,执行mvn clean package

Maven会按照模块间的依赖关系,自动确定构建顺序:

  1. 先构建ecom-common(因为它是基础)。

  2. 然后构建依赖于commonecom-dao

  3. 接着是ecom-service

  4. 最后是ecom-web

最终,ecom-web模块的target目录下,会生成一个包含整个应用所有功能的可执行JAR包。

企业开发规范落地:在这个多模块项目中,我们可以清晰地看到:

  • 代码分层:各个模块职责明确,互不干扰,通过依赖关系建立联系。

  • 版本统一:父工程中的propertiesdependencyManagement确保了所有模块的第三方依赖版本一致。

  • 配置分离:各模块的配置文件(如application.yml)可以独立,也可以在ecom-web模块中统一管理。


第六阶段:复盘升华 —— 最佳实践与Nexus私服搭建

最后,我们来探讨如何“用好”这些工具,并引入企业级必备的Nexus私服,完成从开发到共享的闭环。

1. 最佳实践总结

  • 依赖管理

    • 永远不要使用System scope:避免依赖本地lib目录下的Jar包,这会使项目失去可移植性。

    • 善用<exclusions>排除不必要的依赖:精简你的Jar包大小,避免版本冲突。

    • 为你的项目定义BOM(Bill of Materials):如果你是中间件或公共库的作者,可以发布一个BOM(如spring-boot-dependencies),方便其他项目统一管理你的相关依赖版本。

  • 插件使用

    • 在Maven中,优先使用插件管理(Plugin Management):在父POM中配置插件的公共配置和版本,子模块只需引用,无需重复配置。

    • Gradle中,理解Task的输入和输出:这有助于充分利用增量构建,提升构建速度。

  • 构建优化

    • 配置Maven/IDEA的本地仓库路径:不要放在C盘系统盘,避免占用空间,方便备份。

    • 使用国内镜像:在settings.xmlinit.gradle中配置阿里云镜像,大幅提升下载速度。

    • 在CI/CD环境中,使用构建缓存:将Maven的本地仓库或Gradle的缓存目录作为CI任务的缓存目录,避免每次构建都重新下载所有依赖。

2. 技术演进:Maven与Gradle的未来

  • Maven:作为Java构建工具的老大哥,Maven依然保持稳健的演进。Java社区推出了Maven Wrapper(mvnw),解决了不同机器上Maven版本不一致的问题。其核心的“约定优于配置”思想依然深刻影响着现代开发。

  • Gradle:Gradle的性能优势和灵活性使其在大型项目和Android开发中占据主导地位。Kotlin DSL的普及,让构建脚本的编写更加类型安全、更易维护。随着Java 21的普及,Gradle对虚拟线程等新特性的支持也走在前面。

3. 企业级核心:Nexus私服搭建与使用

在大型企业或团队中,直接依赖中央仓库是不现实的。一是因为网络问题,二是因为公司内部需要发布和共享自己的公共Jar包(例如,你们团队封装了一个公司的公共工具类库)。这就需要一个私服

Nexus Repository Manager OSS是目前最流行的私服解决方案之一。

步骤一:搭建Nexus私服
  1. 下载:从Sonatype官网下载Nexus Repository OSS(免费版)。

  2. 解压:解压到服务器上的目录,如/opt/nexus

  3. 启动:进入bin目录,执行./nexus start

  4. 访问:通过http://服务器IP:8081访问Nexus管理界面。默认用户名/密码是admin/admin123

步骤二:配置仓库

Nexus默认会创建几个仓库:

  • maven-central:代理中央仓库。当你请求一个依赖时,它会先从代理仓库下载,然后缓存在本地,下次再从缓存中取。

  • maven-releases:用于存放公司内部的正式版本(如1.0.0)。

  • maven-snapshots:用于存放公司内部的快照版本(如1.0.0-SNAPSHOT)。

  • maven-public:一个“仓库组”,它聚合了以上三个仓库,是给用户统一使用的URL。

步骤三:配置Maven使用Nexus私服

在你的Maven的settings.xml文件中,配置镜像和认证信息。

  1. 配置镜像:将所有对中央仓库的请求,都指向Nexus的maven-public仓库组。

    <mirrors>
        <mirror>
            <id>nexus</id>
            <name>Nexus Repository</name>
            <url>http://你的nexus服务器IP:8081/repository/maven-public/</url>
            <mirrorOf>*</mirrorOf>
        </mirror>
    </mirrors>
  2. 配置认证信息:当需要发布(deploy)Jar包到maven-releasesmaven-snapshots时,需要提供用户名密码。

    <servers>
        <server>
            <id>nexus-releases</id>
            <username>admin</username>
            <password>admin123</password>
        </server>
        <server>
            <id>nexus-snapshots</id>
            <username>admin</username>
            <password>admin123</password>
        </server>
    </servers>
步骤四:在项目中配置发布地址

在你的项目pom.xml中,配置distributionManagement,告诉Maven将Jar包发布到哪个仓库。

<distributionManagement>
    <repository>
        <id>nexus-releases</id> <!-- 对应settings.xml中server的id -->
        <name>Releases</name>
        <url>http://你的nexus服务器IP:8081/repository/maven-releases/</url>
    </repository>
    <snapshotRepository>
        <id>nexus-snapshots</id> <!-- 对应settings.xml中server的id -->
        <name>Snapshots</name>
        <url>http://你的nexus服务器IP:8081/repository/maven-snapshots/</url>
    </snapshotRepository>
</distributionManagement>
步骤五:发布你的Jar包

在你项目的根目录下,执行mvn clean deploy。Maven会自动编译、测试、打包,然后将生成的Jar包(以及对应的pom文件、源码包等)上传到你配置的Nexus仓库中。其他团队成员只需要配置好Nexus镜像,就可以像使用中央仓库的依赖一样,引用你发布的Jar包了。

例如,团队成员在pom.xml中直接引用你发布的包:

<dependency>
    <groupId>com.yourcompany</groupId>
    <artifactId>your-common-lib</artifactId>
    <version>1.0.0</version> <!-- 从Nexus下载 -->
</dependency>

至此,我们完成了一个企业级开发中从依赖管理、多模块构建到内部共享的完整闭环。


总结与展望

从手动下载Jar包到一键构建,从单模块项目到复杂的多模块拆分,从依赖中央仓库到搭建私有Nexus,Maven/Gradle贯穿了Java项目开发的始终。

回顾我们的学习路径:

  • 问题锚定:我们明白了Maven/Gradle是为了解决“依赖地狱”和“构建混乱”而生的。

  • 基础认知:我们掌握了POM、坐标、仓库、生命周期这些核心概念,并亲手运行了第一个Hello World项目。

  • 核心用法:我们深入学习了依赖的作用域、插件的使用,并知道了如何避免常见的坑。

  • 场景融合:我们看到了Maven/Gradle是如何在Spring Boot项目和多模块项目中发挥作用,串联起开发、测试、部署的整个流程。

  • 企业级实战:我们亲手搭建了一个多模块的电商项目骨架,并引入了Nexus私服,实现了公司内部Jar包的共享和管理。

  • 复盘升华:我们总结了最佳实践,探讨了技术演进,并完成了Nexus私服的搭建与配置。

掌握了这些知识,你不仅能独立完成一个Java项目的构建与管理,更能理解大型企业级项目中,构建工具是如何作为基石,支撑起整个研发流程的自动化和规范化的。

希望这篇文章能成为你Java进阶之路上的坚实一步。未来,无论是面对Maven的XML配置,还是Gradle的Groovy/Kotlin脚本,你都将游刃有余,从容应对。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐