Maven 是一个广泛应用于 Java 项目构建和管理的自动化工具。它由 Apache Software Foundation 开发和维护,旨在简化和标准化软件项目的构建、依赖管理、报告和部署过程。
Maven 能为我们做什么
以前的项目如果你想使用三方 jar 包需要到其官网去下载,然后放入在即的项目中,这种工作繁琐且容易出错,效率低下,而且每个项目都有一份,造成大量重复文件。Maven 的第一个功能就是将 Jar 包放到仓库中统一管理,我们只需要引入 Jar 包的坐标即可自行引入。
Jar 包之间往往是相互依赖的,很多 Jar 包依赖其他 Jar 才能正常工作,这称为依赖关系。如果我们手动导入将是大量的工作,而且还需要注意版本问题,非常繁琐。Maven 的第二个功能就是依赖管理,我们只需引入最外层的依赖,其内部的其他依赖无需关注,会自动引入,方便快捷。
现在的项目越来越复杂,模块划分也很多,一个项目可能被划分成了 N 个模块,模块多的话已经不可能通过 package 来进行划分了,maven 的第三个功能就是项目模块拆分。
Maven通过定义清晰的构建生命周期和阶段,如clean、compile、test、package、install、deploy等,可以自动执行编译源代码、运行单元测试、生成文档、打包(如生成JAR、WAR、EAR文件)、安装到本地仓库、发布到远程仓库等任务。开发者只需运行相应的Maven命令,即可完成整个构建过程,无需手动执行每一个步骤。这是第四个功能。
项目对象模型
POM 是 Maven 的核心概念,它是以 XML 格式定义的项目配置文件(通常名为 pom.xml
),位于项目的根目录下。POM 包含了项目的基本信息(如项目名、版本、组ID、描述等)、构建规则、依赖关系、插件配置、构建生命周期阶段等关键数据。POM为项目提供了统一的、结构化的描述方式,使得项目在整个生命周期中的行为能够被清晰地定义和控制。
核心概念
Maven 最著名的就是 Maven 的依赖管理,它使得我们不必在到开源项目的官网一个个的下载开源组件,然后放到 classpath,一个依赖声明可以包含如下元素
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.2.7</version>
<type>jar</type>
<scope>compile</scope>
<optional>false</optional>
<exclusions>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</exclusion>
</exclusions>
</dependency>
- groupId: 代表项目的所属组织或团体的唯一标识符。groupId 旨在区分不同组织或个人提供的项目,防止命名冲突。它反映了项目在软件生态系统中的位置,类似于包名在 Java 中的作用。
- artifactid:artifactId 是项目本身的唯一标识符,定义实际项目中的一个maven模块。
- version:version 表示项目的版本号,用来区分同一项目在不同时间点的发布版本。
以上三个元素构成了基本的坐标三要素,通过三要素能唯一确定一个项目标识。去 Maven 仓库寻找依赖时就是通过三元素来检索的。
除了三元素还包括其他一些元素
- type:用于指定依赖项的类型,常见的类型包括jar(默认)、war、pom等
- scope:依赖范围,用于控制依赖与三种classpath的关系,包含6种:compile、provided、runtime、test、system和import;
compile:默认范围,编译范围的依赖,它在编译和打包的时候都会把依赖打包进去。
test:测试依赖范围,它在编译和打包的时候不会把依赖包打包进去。
provided:在编译和测试范围有效,最后生成war包是不会打包进去。
runtime:运行时依赖,编译的时候不依赖。
import:导入依赖范围。import范围仅在dependencyManagement部分使用,用于导入其他项目的dependencyManagement部分的配置。
system:系统依赖范围。表示依赖项已经由系统(如本地文件系统、JDK等)提供,且Maven不会尝试从仓库中获取。
- optional:在 Maven 项目中,optional 是一个标记在 元素内的属性,它用于指示依赖是否可选。当某个依赖被标记为 optional 时,意味着该依赖不会传递给依赖该项目的其他项目,即便这个被标记为 optional 的依赖是项目的一部分。
- exclusions:排除传递性依赖
依赖传递
Maven 依赖传递机制会自动加载我们引入的依赖包的依赖,而不必去手动指定。如引入了spring-core 而 spring-core 又依赖 spring-loging,有了依赖传递机制,在项目中添加了 spring-core 依赖时就不用再去考虑它依赖了什么,也不用担心引入多余的依赖。
在依赖传递过程中,可能出现多个依赖引入了同一库的不同版本。Maven使用一套确定的规则来解决这种冲突,原则如下:
- 第一声明优先:当多个路径(直接依赖和传递依赖)都引入了同一个库的不同版本时,Maven会选择路径中最短的那个(即离依赖项目最近的那个声明)。也就是说,直接依赖的版本优先级高于传递依赖的版本。
- 覆盖原则:在项目自身的
dependencyManagement
部分或父POM中明确指定的版本会覆盖通过传递得到的版本。
在遇到依赖冲突时,可以结合 Maven 的 dependency:tree 命令来查看依赖树,查明冲突来源,然后采取相应的策略进行解决。
生命周期
Maven 生命周期是 Maven 构建过程的核心概念,它为项目构建提供了一个标准化和自动化的框架。Maven 定义了一系列有序的构建阶段(phase),这些阶段构成了不同的生命周期。当运行 Maven 命令时,Maven 会按照特定生命周期的顺序执行相应的阶段,从而完成整个构建过程。
Maven 拥有三套独立的生命周期:clean、default和site。
- clean:clean生命周期的目的是清理项目
- default:default生命周期定义了真正构建时所需执行的所有步骤,如mvn clean instal
- validate:验证项目是否正确且所有必要的信息都可用。
- compile:编译项目的源代码。
- test:编译测试源代码并在断言框架下运行测试。
- package:把编译好的代码打包成可发布的格式,如jar、war、ear等。
- integration-test:如果有集成测试,运行集成测试。
- verify:验证集成测试的结果,确保包质量。
- install:将打包好的工件安装到本地Maven仓库中。
- deploy:将最终的工件部署到远程仓库中。
- site:site生命周期的目的是建立和发布项目站点,maven能够基于pom所包含的信息,自动生成一个友好的站点,方便团队交流和发布项目信息,如执行:mvn clean deploy site-deploy
Maven仓库
Maven 仓库可分为两类:本地仓库和远程仓库。当 Maven 根据坐标加载构件时,首先会检索本地仓库,如果本地仓库没有才去远程仓库下载。远程仓库可分为中央仓库和私服。
模块继承
在面向对象中,可以通过类继承实现复用,在 Maven 中同样可以创建 POM 的父子结构,通过在父 POM 中声明一些配置供子 POM 继承来实现复用消除重复。
<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>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
- dependencyManagement:能让子 POM 继承父 POM 配置的同时,又能保证子模块的灵活性。在父 POM dependencyManagement 元素配置的依赖声明不会实际引入子模块中,但能约束子模块 dependencies 下依赖的使用。子 pom 中可以省去 version。
- pluginManagement:与 dependencyManagement 类似,配置的插件不会造成实际插件的调用行为,只有当子 POM中 配置了相关 plugin 元素,才会影响实际插件行为。
往期经典推荐:
SpringBoot项目并发处理大揭秘,你知道它到底能应对多少请求洪峰?_一个springboot能支持多少并发-CSDN博客