如何彻底学会Maven?

281 阅读25分钟

Maven 是一个基于 Java 的项目管理和构建自动化工具,广泛用于管理 Java 项目的构建、依赖和发布过程。它通过一套标准化的项目结构和流程,简化了开发人员管理项目的复杂性。

我们来讲一个事例,如果你要构建一个 Java 项目,最简单的方式就是创建一个文件夹,然后创建一个java源文件,编译打包后就可以跑起来了。这时候你发现你需要一个新功能,这个新功能别人已经写好了,你直接使用就可以,于是你上网把别人的 jar 包下载了下来,然后放到你的项目文件夹。这个时候你突然发现,你下载下来的 jar 包,里面还依赖了其他的 jar,你又得去网上找,然后放到自己的项目,于是陷入循环了,你得不停的找包了…

怎么办?maven 来了

Maven 背景

Maven 由 Jason van Zyl 于 2002 年创建,最初是 Apache Turbine 的一个子项目。2003 年,Maven 被接受为 Apache 软件基金会的顶级项目。

Maven 的诞生源于开发人员在项目管理中遇到的以下几个问题:

  • 依赖管理复杂:在开发过程中,项目通常需要依赖第三方库,手动管理这些库及其版本关系极易出错,且依赖库之间可能还有复杂的嵌套依赖。
  • 构建流程混乱:不同项目可能使用不同的构建工具和流程,导致团队间的协作困难。
  • 重复劳动:手动编译代码、运行测试、打包发布不仅耗时,还容易出现人为错误。

Maven 是为了解决这些问题而设计的,它通过统一的构建工具和标准化的项目结构,简化了构建和依赖管理过程。

快速实践

1. 安装 Maven

# Maven 依赖于 Java 环境,因此首先需要确保已经安装 JDK(建议安装 JDK 8 或更高版本)。
# 所以先安装 Java 环境 【这里就不介绍了】
# 安装完成后,使用 java -version 命令确认 Java 是否安装正确。

# 安装 Maven

 下载 Maven:Maven 官方下载页面 [https://maven.apache.org/]
 解压下载的文件,并将 bin 目录路径添加到系统环境变量 PATH 中。
 打开终端/命令行,输入 mvn -v,查看 Maven 是否安装成功。
  检测安装结果
  mvn –v
  mvn –version

2. 快速创建 Maven 项目

# 使用 Maven 创建项目

 1. 打开命令行,进入你希望创建项目的目录。
 2. 执行以下命令快速生成一个 Maven 项目:
  这将创建一个标准的 Maven 项目,groupId 和 artifactId 可以自定义。
  mvn archetype:generate -DgroupId=com.example -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

# 项目结构

 my-app
  ├── pom.xml
  └── src
      ├── main
      │   └── java
      └── test
          └── java

   pom.xml 是 Maven 项目的核心配置文件,包含项目依赖、版本信息等。
   src/main/java 目录放置源代码。
   src/test/java 目录放置测试代码。

3. 配置依赖

打开 pom.xml,添加项目需要的依赖。例如,你可以在标签内添加常用库,Maven 会自动下载依赖库并添加到项目中。

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

4. 编译和运行项目

# 执行以下命令编译项目
mvn compile

# 运行测试
mvn test

# 打包项目
mvn package

 打包成功后,可以在 target/ 目录下找到生成的 JAR 文件。

Maven 详解

Maven 的项目结构

Maven 项目遵循一种标准的目录结构,旨在为开发人员提供一致的组织方式,使得项目的管理、构建和依赖处理更加便捷。典型的 Maven 项目结构如下:

my-app
 ├── pom.xml
 └── src
     ├── main
     │   ├── java
     │   ├── resources
     │   └── webapp (可选,用于 Web 应用)
     └── test
         ├── java
         └── resources


# 说明

 1.根目录 (my-app)
  pom.xml: 根目录包含项目的主要配置文件 pom.xml。这是 Maven 项目的核心文件,定义了项目的元数据、依赖、构建配置等。

 2. 该目录包含项目的所有源文件和资源文件 (src/)
  src/main/java/:       这是存放应用程序 Java 源代码的目录。Java 源文件应按照包结构放置。
  src/main/resources/:  用于存放应用程序需要的资源文件,如配置文件、静态资源(XML、YAML、图片等)。这些资源在构建时会被打包到类路径中,供应用程序使用。
  src/main/webapp/:     用于 Web 应用项目。包含 Web 资源如 HTML、CSS、JS 文件和 JSP 页面。Maven 会将该目录的内容打包进 WAR 文件。

  src/test/java/:       存放单元测试的 Java 代码,通常使用 JUnit 或其他测试框架。测试代码的包结构与 src/main/java/ 中的源代码相对应。
  src/test/resources/:  存放测试时需要用到的资源文件(例如测试数据或配置文件)。
  

Maven 中的坐标概念

在平面几何中坐标(x,y)可以标识平面中唯一的点,而在立体几何中 (x,y,z)可以标识空间中的一个坐标。Maven 也类似采用了这样的一个概念来标识唯一的包坐标。

# Maven 坐标主要组成

 groupId      定义当前Maven项目,【包】
 artifactId   定义实际项目中的一个模块【模块】
 version      定义当前项目的当前版本【版本】
 packaging     定义该项目的打包方式,默认jar,还可以是war、ear等

# groupId:

 项目的全球唯一标识符,通常使用全限定的包名区分该项目和其他项目。
 并且构建时生成的路径也是由此生成。
 举例:一般我们公司或者项目的域名是这样的:www.xxx.com.cn  
 我们会将 xxx.com.cn 的倒转 [cn.com.xxx](http://cn.com.xxx) 作为 groupId,同时也是我们代码工程目录的结构。

# artifactId

 构件的标识符,它和 groupId 一起唯一标识一个构件。
 换一种说法:groupId 如果理解成一家公司或者项目。artifactId 就想是这家公司下的某个子公司或者子项目。
 构件是项目产生的或使用的一个东西,Maven为项目产生的构件包括:JARs,源 码,二进制发布和WARs等。

# version

 项目当前版本,格式为:主版本.次版本.增量版本-限定版本号 
 [**版本种类集合以及常用版本**]
 (https://www.notion.so/4cd54c6efbe143388f1afdbfd7c0f18e?pvs=21)

# packaging

 项目产生的构件类型,例如jar、war、ear、pom。
 插件可以创建他们自己的构件类型,所以前面列的不是全部构件类型

# description

 项目的详细描述, Maven 产生的文档用

########## Maven为什么使用坐标?##########

 Maven世界拥有大量构建,我们需要找一个用来唯一标识一个构建的统一规范,拥有了统一规范,就可以把查找工作交给机器,默认查找jar包,也就是说在其他项目中依赖它时,能找到它。

 举例: 以下是一个 spring-jdbc 的包
 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jdbc</artifactId>
     <version>5.1.1.RELEASE</version>
 </dependency>

Maven 中配置文件 pom.xml

pom.xml 是 Maven 项目的核心配置文件,它定义了项目的基本信息、依赖关系、构建配置、插件等内容。Maven 会根据 pom.xml 里的配置文件自动化管理项目构建流程,包括依赖解析、编译、测试、打包等。

可以说 pom.xml 是 Maven 中最重要的配置文件。上面我们定义了项目的坐标,同时如果我们需要引入其他项目的坐标都是通过这个配置文件。还有管理项目的整个生命周期也是这个文件。

官方说明:maven.apache.org/pom.html

典型的 pom.xml 文件示例

<!-- pom文件的顶级元素 -->
<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">
                             
    <!-- pom模型版本,描述这个pom文件是遵循哪个版本 -->                        
    <modelVersion>4.0.0</modelVersion>
  
  <!-- 自身项目的坐标配置 --> 
    <groupId>com.example</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

  <!-- 自身项目名称和描述 --> 
    <name>My Application</name>
    <description>A simple Maven project</description>

   <!-- 定义项目中使用的属性,类似定义变量 -->
   <!-- 用于设置项目中的全局属性,这些属性可以在 pom.xml 中其他地方引用。例如,<maven.compiler.source> 和 <maven.compiler.target> 定义了编译时使用的 Java 版本 --> 
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

  <!-- 定义项目的依赖项 -->
    <dependencies>
      <!-- 这个节点定义了项目依赖的外部库。每个依赖项都用 <dependency> 元素表示,并包含 groupId、artifactId、version 等信息。 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

  <!-- 配置构建过程: 节点定义了项目构建时的行为,包括插件配置、生成目录等 -->
    <build>
      
      <!-- 定义项目构建时使用的插件 -->
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Maven 中常用命令

Maven 提供了一系列常用的命令,用于管理项目的构建、依赖、测试、打包等操作。以下是 Maven 中常用的命令和它们的作用

# mvn v
 
 查看版本

# mvn clean

 作用:清理项目,删除项目生成的所有编译文件和输出文件。
 执行内容:删除 target/ 目录。
 使用场景:当需要重新构建项目时,清除旧的构建文件,确保构建过程是干净的。

# mvn compile

 作用:编译项目的源代码。
 执行内容:将 src/main/java 目录下的源代码编译为 .class 文件,生成到 target/classes 目录中。
 使用场景:仅需要编译项目时使用。

# mvn test

 作用:编译并执行测试代码。
 执行内容:Maven 首先编译 src/test/java 目录下的测试代码,并运行测试用例。需要在 pom.xml 中配置测试框架(如 JUnit 或 TestNG)。
 使用场景:运行单元测试和集成测试。

# mvn package

 作用:将项目打包成可分发的格式(如 JAR 或 WAR)。
 执行内容:Maven 会根据 pom.xml 中的打包配置,将项目编译后的类和资源文件打包成 JAR 或 WAR 文件,存储在 target/ 目录下。
 使用场景:项目构建完成后,需要将其打包成一个可分发的格式时。

# mvn install

 作用:将打包好的项目安装到本地 Maven 仓库。
 执行内容:打包项目后,将其安装到本地仓库(通常在 ~/.m2/repository 中)。这使得其他项目可以依赖这个项目的构建结果。
 使用场景:将项目作为库或依赖安装到本地 Maven 仓库,方便其他项目引用。

# mvn clean install

 作用:首先清理项目,然后重新编译、测试并安装。
 执行内容:先执行 clean,再执行 install。这是一种常见的命令组合,确保从干净的状态下构建项目并安装。
 使用场景:需要完全重新构建项目,并将结果安装到本地仓库时。

# mvn dependency:tree

 作用:查看项目的依赖树,列出所有依赖项及其传递依赖。
 执行内容:输出项目所有直接依赖和传递依赖的层次结构。
 使用场景:检查项目的依赖关系,尤其是分析依赖冲突问题时。

# mvn test-compile

 作用:编译测试代码。
 执行内容:仅编译 src/test/java 下的测试源代码。
 使用场景:在运行测试前单独编译测试代码。

# mvn validate

 作用:验证项目是否正确,并检查所有必要的项目配置信息是否完整。
 执行内容:对 pom.xml 进行验证,确保没有缺失的必要信息。
 使用场景:在构建之前检查项目的基本配置和依赖是否正确。

# mvn verify

 作用:验证项目是否满足质量标准,运行集成测试。
 执行内容:在打包之后,执行集成测试,检查项目是否按预期行为工作。
 使用场景:确保项目在打包前通过所有集成测试。

# mvn site

 作用:生成项目的站点文档。
 执行内容:生成包含项目信息的站点报告,例如项目依赖、插件配置、测试覆盖率等,并输出到 target/site/ 目录中。
 使用场景:生成项目文档,便于发布或查看项目的整体信息。

# mvn deploy

 作用:将项目部署到远程仓库中。
 执行内容:在 install 之后,将打包好的项目发布到配置的远程 Maven 仓库。
 使用场景:将项目发布到公司内部或公共的 Maven 仓库中,供其他开发者或项目使用。

# mvn archetype:generate

 作用:使用 Maven 原型(archetype)生成一个新的 Maven 项目。
 执行内容:根据提供的项目模板(archetype)快速生成一个 Maven 项目骨架。
 使用场景:创建新的 Maven 项目时,快速生成标准化的项目结构。

Maven 中的仓库

有没有想过一个问题,你引用的别人的包从哪里来呢?还有如果你自己开发了一个包想给别人使用,那你得把这个包放在哪里呢?别人又怎么知道,怎么获取呢?这就是Maven 仓库。

Maven 仓库是存储项目依赖库、插件以及项目构建结果的地方。Maven 使用仓库来管理项目中的各种依赖(如第三方库、插件等)以及构建后的工件(如 JAR 包、WAR 包等)。它是 Maven 项目构建生命周期中必不可少的部分。

Maven 的仓库分为:本地仓库(Local Repository) 远程仓库(Remote Repository)

远程库又分为:中央仓库(Central Repository)镜像仓库(Mirror Repository)快照仓库(Snapshot Repository)

# 本地仓库(Local Repository)

 定义:本地仓库是存储在开发者机器上的 Maven 仓库,通常位于 ~/.m2/repository 目录中。
 用途:本地仓库用于缓存从远程仓库下载的依赖包和项目构建后的结果。当 Maven 构建项目时,首先会检查本地仓库中是否已经存在所需的依赖,如果存在则直接使用,否则会从远程仓库下载并存储到本地仓库中。
 特点:
  • 速度快,不需要每次从远程下载依赖。
  • 开发者无需手动管理依赖,Maven 会自动下载并存储。
  • 允许离线构建项目,因为本地仓库已经缓存了依赖。
  • 路径:默认路径为 ~/.m2/repository,可以通过 Maven 的 settings.xml 配置文件自定义本地仓库的位置。

# 远程仓库(Remote Repository)

 定义:远程仓库是位于网络上的公共或私有仓库,存储了大量的依赖库、插件和构建结果。Maven 会从远程仓库下载项目所需的依赖,并缓存到本地仓库中。
 常见远程仓库:
 Maven Central:Maven 官方提供的公共仓库,地址为 https://repo.maven.apache.org/maven2/。绝大多数常用的开源库都可以从 Maven Central 下载。
 其他公共仓库:例如 JCenter、Google 的 Android 仓库等,都是公共可访问的远程仓库。
 私有仓库:公司内部或团队维护的私有仓库,用于存储内部的库或定制的构建结果。私有仓库通过类似 Nexus、Artifactory 等工具搭建。
 特点:
  • 公共远程仓库通常是开源库的主要发布渠道,开发者可以从中下载依赖库。
  • 私有远程仓库用于内部项目依赖的管理和共享,提升团队的协作效率。

# 中央仓库(Central Repository)

 定义:Maven 中央仓库是 Maven 官方提供的默认远程仓库,包含了大量的常用开源库。Maven 中央仓库是所有 Maven 项目默认配置的远程仓库地址,不需要额外配置。
 作用:中央仓库是开发者最常用的依赖来源,开发者可以轻松地通过 Maven 自动下载和管理项目中的开源依赖库。
 地址:https://repo.maven.apache.org/maven2/
 特点:
  • 默认配置的远程仓库,用户可以不做任何设置直接使用。
  • 提供了大量常见的开源库和插件,便于快速构建项目。
  • 自动化版本控制和更新。

# 镜像仓库(Mirror Repository)

 定义:镜像仓库是对中央仓库或其他远程仓库的镜像(副本),通常用于提高下载速度和稳定性。通过配置镜像仓库,开发者可以从较近或访问较快的服务器下载依赖。
 特点:
  • 通过镜像仓库加速依赖下载,解决访问中央仓库慢的问题。
  • 在配置 settings.xml 文件中可以指定镜像仓库。
 使用场景:
  • 当访问 Maven Central 仓库速度较慢时,可以使用国内的镜像仓库(例如阿里云镜像)。
  • 当公司内部需要部署一个高效的远程仓库时,可以搭建自己的中央仓库镜像。

# 快照仓库(Snapshot Repository)

 定义:快照仓库用于存储项目的开发版本或不稳定的版本,这些版本被称为 “快照”(Snapshot)。快照版本代表的是处于开发过程中的动态版本,而非最终发布的稳定版本。
 特点:
  • 快照版本是不断更新的,Maven 会每次从远程仓库下载最新的快照版本。
  • 快照版本通常用于开发和测试阶段,直到项目进入稳定状态并发布为正式版本。
 使用场景:
  • 项目处于活跃开发过程中,需要持续集成和测试的版本。
  • 快速迭代和试验阶段,发布不稳定的开发版本供团队使用。

Maven 仓库的工作机制

  • 搜索依赖:Maven 构建项目时,会首先在本地仓库中查找所需的依赖。如果找不到,则会去远程仓库搜索。
  • 下载依赖:如果 Maven 在本地仓库中没有找到依赖,它会从远程仓库下载依赖并存储在本地仓库中。
  • 使用依赖:依赖下载并存储后,Maven 会将这些库加入到项目的构建路径中,以便编译、测试和打包时使用。

仓库配置 「官网配置文件说明:maven.apache.org/settings.ht…

仓库的配置主要通过 settings.xml 文件进行。settings.xml 通常位于 ~/.m2/ 目录中,也可以在 Maven 项目中的 pom.xml 文件中配置特定仓库。

<!-- 本地仓库。该值表示构建系统本地仓库的路径。 其默认值为${user.home}/.m2/repository。  -->
<localRepository>/Users/wushiwei/.m2/repository</localRepository>

<!-- Maven是否需要和用户交互以获得输入。如果Maven需要和用户交互以获得输入,则设置成true,反之则应为false。默认为true。 -->
<interactiveMode>true</interactiveMode>

<proxies>
    <!--代理元素包含配置代理时需要的信息 -->
    <proxy>
        <!--代理的唯一定义符,用来区分不同的代理元素。 -->
        <id>myproxy</id>
        <!--该代理是否是激活的那个。true则激活代理。当我们声明了一组代理,而某个时候只需要激活一个代理的时候,该元素就可以派上用处。  -->
        <active>true</active>
        <!--代理的协议。 协议://主机名:端口,分隔成离散的元素以方便配置。 -->
        <protocol>http://…</protocol>
        <!--代理的主机名。协议://主机名:端口,分隔成离散的元素以方便配置。   -->
        <host>proxy.somewhere.com</host>
        <!--代理的端口。协议://主机名:端口,分隔成离散的元素以方便配置。  -->
        <port>8080</port>
         <!--代理的用户名,用户名和密码表示代理服务器认证的登录名和密码。  -->
        <username>proxyuser</username>
        <!--代理的密码,用户名和密码表示代理服务器认证的登录名和密码。  -->
        <password>somepassword</password>
        <!--不该被代理的主机名列表。该列表的分隔符由代理服务器指定;例子中使用了竖线分隔符,使用逗号分隔也很常见。 -->
        <nonProxyHosts>*.google.com|ibiblio.org</nonProxyHosts>
    </proxy>
</proxies>

<!--配置服务端的一些设置。一些设置如安全证书不应该和pom.xml一起分发。这种类型的信息应该存在于构建服务器上的settings.xml文件中。 -->
<servers>
    <!--服务器元素包含配置服务器时需要的信息  -->
    <server>
        <!--这是server的id(注意不是用户登陆的id),该id与distributionManagement中repository元素的id相匹配。 -->
        <id>server001</id>
        <!--鉴权用户名。鉴权用户名和鉴权密码表示服务器认证所需要的登录名和密码。  -->
        <username>my_login</username>
        <!--鉴权密码 。鉴权用户名和鉴权密码表示服务器认证所需要的登录名和密码。  -->
        <password>my_password</password>
        <!--鉴权时使用的私钥位置。和前两个元素类似,私钥位置和私钥密码指定了一个私钥的路径(默认是/home/hudson/.ssh/id_dsa)以及如果需要的话,一个密钥 -->
        <!--将来passphrase和password元素可能会被提取到外部,但目前它们必须在settings.xml文件以纯文本的形式声明。  -->
        <privateKey>${usr.home}/.ssh/id_dsa</privateKey>
        <!--鉴权时使用的私钥密码。 -->
        <passphrase>some_passphrase</passphrase>
        <!--文件被创建时的权限。如果在部署的时候会创建一个仓库文件或者目录,这时候就可以使用权限(permission)。-->
        <!--这两个元素合法的值是一个三位数字,其对应了unix文件系统的权限,如664,或者775。  -->
        <filePermissions>664</filePermissions>
        <!--目录被创建时的权限。  -->
        <directoryPermissions>775</directoryPermissions>
        <!--传输层额外的配置项  -->
        <configuration></configuration>
    </server>
</servers>

<!-- 配置私有仓库和镜像仓库 -->
<mirrors>
    <!-- 阿里云 Maven 仓库镜像 -->
    <mirror>
        <id>alimaven</id>
        <mirrorOf>central</mirrorOf>
        <name>Aliyun Maven</name>
        <url>https://maven.aliyun.com/repository/public</url>
    </mirror>
</mirrors>

<repositories>
    <!-- 配置私有仓库 -->
    <repository>
        <id>my-private-repo</id>
        <url>http://my-company-repo.com/repository/maven-releases</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

<!--根据环境参数来调整构建配置的列表。settings.xml中的profile元素是pom.xml中profile元素的裁剪版本。-->
<!--它包含了id,activation, repositories, pluginRepositories和 properties元素。-->
<!--这里的profile元素只包含这五个子元素是因为这里只关心构建系统这个整体(这正是settings.xml文件的角色定位),而非单独的项目对象模型设置。-->
<!--如果一个settings中的profile被激活,它的值会覆盖任何其它定义在POM中或者profile.xml中的带有相同id的profile。  -->
<profiles>
    <!--根据环境参数来调整的构件的配置 -->
    <profile>
        <!--该配置的唯一标识符。  -->
        <id>test</id>
        <!--自动触发profile的条件逻辑。Activation是profile的开启钥匙。-->
        <!--如POM中的profile一样,profile的力量来自于它能够在某些特定的环境中自动使用某些特定的值;这些环境通过activation元素指定。-->
        <!--activation元素并不是激活profile的唯一方式。settings.xml文件中的activeProfile元素可以包含profile的id。-->
        <!--profile也可以通过在命令行,使用-P标记和逗号分隔的列表来显式的激活(如,-P test)。 -->
        <activation>
            <!--profile默认是否激活的标识 -->
            <activeByDefault>false</activeByDefault>
            <!--activation有一个内建的java版本检测,如果检测到jdk版本与期待的一样,profile被激活。 -->
            <jdk>1.7</jdk>
            <!--当匹配的操作系统属性被检测到,profile被激活。os元素可以定义一些操作系统相关的属性。 -->
            <os>
                <!--激活profile的操作系统的名字  -->
                <name>Windows XP</name>
                <!--激活profile的操作系统所属家族(如 'windows')   -->
                <family>Windows</family>
                <!--激活profile的操作系统体系结构   -->
                <arch>x86</arch>
                <!--激活profile的操作系统版本 -->
                <version>5.1.2600</version>
            </os>
            <!--如果Maven检测到某一个属性(其值可以在POM中通过${名称}引用),其拥有对应的名称和值,Profile就会被激活。-->
            <!--如果值字段是空的,那么存在属性名称字段就会激活profile,否则按区分大小写方式匹配属性值字段 -->
            <property>
                <!--激活profile的属性的名称 -->
                <name>mavenVersion</name>
                <!--激活profile的属性的值  -->
                <value>2.0.3</value>
            </property>
 
            <!--提供一个文件名,通过检测该文件的存在或不存在来激活profile。missing检查文件是否存在,如果不存在则激活profile。-->
            <!--另一方面,exists则会检查文件是否存在,如果存在则激活profile。 -->
            <file>
                <!--如果指定的文件存在,则激活profile。  -->
                <exists>/usr/local/hudson/hudson-home/jobs/maven-guide-zh-to-production/workspace/</exists>
                <!--如果指定的文件不存在,则激活profile。 -->
                <missing>/usr/local/hudson/hudson-home/jobs/maven-guide-zh-to-production/workspace/</missing>
            </file>
        </activation>
 
         <!--对应profile的扩展属性列表。Maven属性和Ant中的属性一样,可以用来存放一些值。这些值可以在POM中的任何地方使用标记${X}来使用,这里X是指属性的名称。-->
        <!--属性有五种不同的形式,并且都能在settings.xml文件中访问。   -->
        <!--1. env.X: 在一个变量前加上"env."的前缀,会返回一个shell环境变量。例如,"env.PATH"指代了$path环境变量(在Windows上是%PATH%)。  --> 
        <!--2. project.x:指代了POM中对应的元素值。      -->
        <!--3. settings.x: 指代了settings.xml中对应元素的值。   -->
        <!--4. Java System Properties: 所有可通过java.lang.System.getProperties()访问的属性都能在POM中使用该形式访问,   -->
        <!--   如/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0/jre。      -->
        <!--5. x: 在<properties/>元素中,或者外部文件中设置,以${someVar}的形式使用。  -->
        <properties>
            <!-- 如果这个profile被激活,那么属性${user.install}就可以被访问了 -->
            <user.install>usr/local/winner/jobs/maven-guide</user.install>
        </properties>
 
        <!--远程仓库列表,它是Maven用来填充构建系统本地仓库所使用的一组远程项目。  -->
        <repositories>
            <!--包含需要连接到远程仓库的信息  -->
            <repository>
                <!--远程仓库唯一标识 -->
                <id>codehausSnapshots</id>
                <!--远程仓库名称  -->
                <name>Codehaus Snapshots</name>
                <!--如何处理远程仓库里发布版本的下载 -->
                <releases>
                    <!--true或者false表示该仓库是否为下载某种类型构件(发布版,快照版)开启。   -->
                    <enabled>false</enabled>
                    <!--该元素指定更新发生的频率。Maven会比较本地POM和远程POM的时间戳。这里的选项是:-->
                    <!--always(一直),daily(默认,每日),interval:X(这里X是以分钟为单位的时间间隔),或者never(从不)。  -->
                    <updatePolicy>always</updatePolicy>
                    <!--当Maven验证构件校验文件失败时该怎么做:-->
                    <!--ignore(忽略),fail(失败),或者warn(警告)。 -->
                    <checksumPolicy>warn</checksumPolicy>
                </releases>
 
                <!--如何处理远程仓库里快照版本的下载。有了releases和snapshots这两组配置,POM就可以在每个单独的仓库中,为每种类型的构件采取不同的策略。-->
                <!--例如,可能有人会决定只为开发目的开启对快照版本下载的支持。参见repositories/repository/releases元素 -->
                <snapshots>  
                    <enabled />
                    <updatePolicy />
                    <checksumPolicy /> 
                </snapshots>
 
                <!--远程仓库URL,按protocol://hostname/path形式  -->
                <url>http://snapshots.maven.codehaus.org/maven2</url>
                <!--用于定位和排序构件的仓库布局类型-可以是default(默认)或者legacy(遗留)。-->
                <!--Maven 2为其仓库提供了一个默认的布局;然而,Maven 1.x有一种不同的布局。我们可以使用该元素指定布局是default(默认)还是legacy(遗留)。  -->
                <layout>default</layout>
            </repository>
        </repositories>
 
        <!--发现插件的远程仓库列表。仓库是两种主要构件的家。第一种构件被用作其它构件的依赖。这是中央仓库中存储的大部分构件类型。另外一种构件类型是插件。-->
        <!--Maven插件是一种特殊类型的构件。由于这个原因,插件仓库独立于其它仓库。pluginRepositories元素的结构和repositories元素的结构类似。-->
        <!--每个pluginRepository元素指定一个Maven可以用来寻找新插件的远程地址。 -->
        <pluginRepositories>
            <!--包含需要连接到远程插件仓库的信息.参见profiles/profile/repositories/repository元素的说明 -->
            <pluginRepository>
                <releases>
                    <enabled />
                    <updatePolicy />
                    <checksumPolicy />
                </releases>
 
                <snapshots>
                    <enabled />
                    <updatePolicy />
                    <checksumPolicy />
                </snapshots>
 
                <id />
                <name />
                <url />
                <layout />
            </pluginRepository>
 
        </pluginRepositories>
 
        <!--手动激活profiles的列表,按照profile被应用的顺序定义activeProfile。 该元素包含了一组activeProfile元素,每个activeProfile都含有一个profile id。-->
        <!--任何在activeProfile中定义的profile id,不论环境设置如何,其对应的 profile都会被激活。-->
        <!--如果没有匹配的profile,则什么都不会发生。例如,env-test是一个activeProfile,则在pom.xml(或者profile.xml)中对应id的profile会被激活。-->
        <!--如果运行过程中找不到这样一个profile,Maven则会像往常一样运行。  -->
        <activeProfiles>
            <activeProfile>env-test</activeProfile>
        </activeProfiles>
    </profile>
</profiles>

Maven 仓库是 Maven 项目构建和依赖管理的核心部分,它通过本地仓库和远程仓库的协同工作,使得依赖的管理变得高效而便捷。开发者通过配置不同类型的仓库,可以根据实际需要来下载依赖、存储构建结果,提升开发和项目管理的效率。

Maven 中的聚合与继承

聚合是指在 Maven 中可以通过一个父项目来管理和构建多个子项目的机制。通过聚合,可以在一个构建命令下同时构建多个子项目。这种方式有助于简化管理多个模块或项目的工作,特别是在需要同时运行多个项目的情况下。

聚合通常是通过在父项目的 pom.xml 文件中使用元素来实现

父项目并不包含实际代码,而是充当容器来管理和构建多个子项目

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>parent-project</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

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

继承是 Maven 中通过将通用配置(如依赖项、插件等)集中定义在一个父 pom.xml 中,多个子项目可以继承这个父 pom 来共享这些配置。继承的主要目的是减少重复配置,便于统一管理。

子项目通过在 pom.xml 文件中指定父 pom 来实现继承,子项目可以继承父项目中的依赖、插件等配置,并且可以在子项目中进行覆盖或扩展

父项目 pom.xml:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>parent-project</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.9</version>
        </dependency>
    </dependencies>
</project>

子项目 pom.xml:

<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>parent-project</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>child-project</artifactId>
</project>

在这里,子项目 child-project 继承了父项目的所有配置,比如 spring-core 依赖。

Maven 架构设计原理和思想

Maven 架构图示意图

+------------------+
|      Core        |  <-- Maven 核心,负责生命周期和插件管理
+------------------+
        |
+------------------+  
|     Plugin       |  <-- 各种插件,如编译、测试、打包、部署插件
+------------------+
        |
+------------------+
|   Repository     |  <-- 管理依赖库的下载、版本控制、本地缓存
+------------------+
        |
+------------------+
|    Settings      |  <-- 配置 Maven 的行为,如代理、仓库路径等
+------------------+

Maven 流程图

+--------------------------+
| 1. 读取 `pom.xml` 文件    |
+--------------------------+
            |
+--------------------------+
| 2. 下载依赖并缓存到本地  |
+--------------------------+
            |
+--------------------------+
| 3. 执行插件任务(如编译) |
+--------------------------+
            |
+--------------------------+
| 4. 打包并生成构建结果    |
+--------------------------+
            |
+--------------------------+
| 5. 安装到本地仓库或部署  |
+--------------------------+

Maven 构建项目生命周期图

+-------------------+
|    validate       |  验证项目是否正确,所有必要信息是否可用
+-------------------+
          |
+-------------------+
|    compile        |  编译项目源代码
+-------------------+
          |
+-------------------+
|    test-compile   |  编译测试代码
+-------------------+
          |
+-------------------+
|    test           |  运行单元测试
+-------------------+
          |
+-------------------+
|    package        |  将编译后的代码打包成可分发的格式(如 JAR、WAR)
+-------------------+
          |
+-------------------+
|    verify         |  运行集成测试,检查打包的有效性
+-------------------+
          |
+-------------------+
|    install        |  将包安装到本地 Maven 仓库
+-------------------+
          |
+-------------------+
|    deploy         |  将最终包复制到远程仓库,供他人共享
+-------------------+

清理(clean)生命周期

+-------------------+
|      pre-clean    |  执行清理前的预处理步骤
+-------------------+
          |
+-------------------+
|      clean        |  删除之前构建生成的所有文件
+-------------------+
          |
+-------------------+
|      post-clean   |  执行清理后的收尾步骤
+-------------------+

站点(site)生命周期

+-------------------+
|   pre-site        |  执行生成站点前的预处理
+-------------------+
          |
+-------------------+
|   site            |  生成项目的站点文档
+-------------------+
          |
+-------------------+
|   post-site       |  执行生成站点后的处理
+-------------------+
          |
+-------------------+
|   site-deploy     |  将站点文档部署到远程服务器
+-------------------+

Maven 的三大生命周期它们是相互独立的,可以仅仅调用clean来清理工作目录,仅仅调用site来生成站点,也可以直接运行 mvn clean install site 运行所有这三套生命周期。

补充:Maven 插件管理

Maven 的核心设计只是定义了一套抽象的构建生命周期,Maven 构建执行生命周期时会自动调用相关的插件。Maven 的核心并不直接执行具体的构建任务,而是通过插件来实现这些任务。Maven 的生命周期与插件绑定在一起,每个构建阶段都会触发相应插件的目标(goal)来完成实际的构建操作。

插件可以通过 pom.xml 文件中的节点进行配置。

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

Maven 的架构设计遵循了模块化、插件化和生命周期管理的思想,以实现高效的项目构建和依赖管理。Maven 的设计原理主要围绕以下几个核心思想:

  • 声明式构建:通过 pom.xml 文件,开发者只需要声明项目的依赖、插件和目标,Maven 会根据这些声明来自动执行构建任务,无需手动编写复杂的构建脚本。
  • 生命周期管理:Maven 定义了一系列的生命周期阶段,开发者可以通过指定执行的生命周期阶段来完成特定的构建任务。
  • 插件驱动:Maven 的几乎所有功能都由插件提供,插件可以在不同的生命周期阶段执行不同的任务,如编译、测试、打包等。
  • 依赖管理:Maven 通过依赖配置和仓库机制,自动下载、管理和更新项目的依赖库,从而大幅减少了依赖管理的复杂性。

2.3 总结

工具优点缺点应用场景特点
Maven1. 强大的依赖管理系统2. 预定义生命周期,标准化构建步骤3. 丰富的插件生态1. XML 配置冗长2. 构建速度较慢,尤其是多模块项目适用于依赖复杂、需要稳定构建流程的大型项目采用 XML 配置,构建步骤固定且标准化
Ant1. 灵活性高2. 手动控制构建步骤3. 无依赖生命周期,适合定制构建流程1. 没有内置依赖管理系统2. 需要手动编写复杂的构建脚本适用于小型项目或需要高度定制化的构建流程无生命周期概念,适合自定义任务的项目
Gradle1. 使用 DSL 语言,配置灵活2. 支持增量构建,构建速度快3. 强大的依赖管理系统1. 学习曲线较陡2. 配置自由度高,复杂项目需更多经验适用于需要快速构建、大规模和复杂依赖的项目使用 Groovy/Kotlin DSL,灵活性强,速度快

5 致谢

更多内容欢迎点击阅读原文,喜欢文章的话,也希望能给小编点个赞或者转发,你们的喜欢与支持是小编最大的鼓励,小巫编程室感谢您的关注与支持。好好学习,天天向上(good good study day day up)。

  • 官网 官网[1]
  • 中央仓库 中央仓库[2]

参考资料

[1] 官网: maven.apache.org/

[2] 中央仓库: maven.apache.org/