Maven 是 Apache 软件基金会开发的一个项目管理和构建自动化工具,主要用于 Java 项目。它可以:
一个 Maven 工程遵循约定的目录结构,这种标准化的目录布局是 Maven 实现自动化构建过程中不可或缺的关键环节。Maven 推荐采用统一的目录结构,以确保项目的一致性和构建过程的高效性。
project-name/
├── src/
│ ├── main/
│ │ └── java/ # 主代码
│ └── test/
│ └── java/ # 测试代码
├── pom.xml # Maven 配置文件
传统 Java 项目可能要手动:
Maven 全部帮你做了!它像一个「项目自动管家」,你只需写好配置文件(pom.xml
),Maven 会自动根据配置从仓库中拉取依赖。
下载地址:https://maven.apache.org/download.cgi
你可以下载最新版本的 Maven,也可以选择其他版本
然后里面选择自己对应的版本下载即可:
下载之后,解压到非中文的文件目录,如下:
Maven 会在每次从远程仓库获取第三方依赖后,将其缓存至本地指定的存储路径中。这一机制有效避免了在每次构建新项目时重复从远程仓库拉取相同依赖的情况,从而显著提升了开发效率并减少了不必要的网络开销。
<localRepository>D:\Environment\RepositorylocalRepository>
本地仓库默认值:用户/.m2/repository。由于本地仓库的默认位置是在用户的家目录下,而家目录往往是在 C 盘,也就是系统盘。将来 Maven 仓库中 jar 包越来越多,仓库体积越来越大,可能会拖慢 C 盘运行速度,影响系统性能。
Maven 下载 jar 包默认访问境外的中央仓库,而国外网站速度很慢。改成阿里云提供的镜像仓库,访问国内网站,可以让 Maven 下载 jar 包的时候速度更快。配置的方式是:
<mirror>
<id>tencentid>
<name>tencent mavenname>
<url>http://mirrors.cloud.tencent.com/nexus/repository/maven-public/url>
<mirrorOf>centralmirrorOf>
mirror>
<mirror>
<id>nexus-aliyunid>
<mirrorOf>centralmirrorOf>
<name>Nexus aliyunname>
<url>http://maven.aliyun.com/nexus/content/groups/publicurl>
mirror>
pom.xml
(Project Object Model)是 Maven 项目的核心配置文件,其主要功能在于对项目的全面描述与定义,包括但不限于以下内容:
通过 pom.xml
,Maven 实现了项目结构的标准化与构建过程的自动化,为开发者提供了高效且可维护的项目管理工具。
<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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>demoname>
<description>Demo project for Spring Bootdescription>
<packaging>jarpackaging>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.10.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
project>
每一个依赖(或项目)都有三个关键标识:
groupId
:组织名,一般是域名反写,如 org.springframework
artifactId
:模块名,如 spring-core
version
:版本号,如 5.3.10
合起来可以唯一确定一个库,如下:
<groupId>javax.servletgroupId>
<artifactId>servlet-apiartifactId>
<version>2.5version>
<scope>compilescope>
项目首次运行时,会从远程仓库拉取依赖,并保存到本地仓库
上面坐标对应的 jar 包在 Maven 本地仓库中的位置:
Maven本地仓库根目录\javax\servlet\servlet-api\2.5\servlet-api-2.5.jar
在 Maven 中,依赖范围(Dependency Scope)决定了一个依赖在构建过程中的可用性和传播性。
Maven 的生命周期如下:编译、测试、运行、打包。
不同的范围影响了该依赖在不同生命周期阶段的行为,即
标签。
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
Maven 提供了几种常用的依赖范围类型,每种类型有不同的含义和使用场景。
编译:将 Java 源代码文件(.java
文件)转换为 字节码(.class
文件),这是 Java 虚拟机(JVM)能够执行的代码。
测试: 执行单元测试(Unit Test)和集成测试(Integration Test)等。
运行:项目最终的运行时环境中。
打包:将当前项目打包成 Jar 或者 War 包。
描述:compile
范围是 Maven 依赖的默认范围。如果你没有显式指定依赖的范围,它会自动使用 compile
范围。compile
范围表示该依赖在编译、测试、运行时都需要。
可用阶段:
常见场景:适用于大多数的应用程序库,通常是核心依赖。
示例:
<dependency>
<groupId>com.examplegroupId>
<artifactId>some-libraryartifactId>
<version>1.0version>
dependency>
描述:provided
范围表示该依赖在编译和测试时需要,但在运行时由容器或运行时环境提供。典型的例子是 Web 应用程序中的 Servlet API,它在 Web 容器(如 Tomcat)中已经提供,因此不需要在最终的构件中包含该依赖。
可用阶段:
常见场景:Web 项目中的容器依赖(如 servlet-api
)或者一些 Java EE API。
示例:
<dependency>
<groupId>javax.servletgroupId>
<artifactId>servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
描述:runtime
范围表示该依赖在运行时需要,但在编译时不需要。通常这种范围适用于某些在运行时才需要的库,比如数据库驱动程序或者日志框架。
可用阶段:
常见场景:用于运行时需要的库,通常是数据库连接池、日志框架等。
示例:
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.49version>
<scope>runtimescope>
dependency>
描述:test
范围表示该依赖仅在测试时需要。它不会在项目的运行时或者生产环境中包含。通常用于单元测试框架(如 JUnit 或 TestNG)和一些测试工具。
可用阶段:
常见场景:单元测试框架(JUnit、TestNG)、模拟库(Mockito)等。
示例:
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
描述:system
范围表示该依赖位于项目外部(如本地文件系统中)。它不是从远程仓库下载的,而是通过文件路径直接引用。system
范围的依赖必须指定 systemPath
属性,该路径指向文件的实际位置。
可用阶段:
示例:
<dependency>
<groupId>com.examplegroupId>
<artifactId>some-libraryartifactId>
<version>1.0version>
<scope>systemscope>
<systemPath>${project.basedir}/lib/some-library.jarsystemPath>
dependency>
注意:不推荐使用
system
范围,因为它不具备跨平台的特性,依赖文件路径是硬编码的,缺乏可移植性。
描述:import
范围主要用于导入 BOM(Bill of Materials)类型的依赖,它不会像普通的依赖那样将 JAR 文件直接添加到构建中,而是通过继承管理的一组版本。常用于依赖管理(dependencyManagement
)的场景,通常在多模块项目中,或者用于引入其他项目的版本管理。
可用阶段:
常见场景:用于在父项目中管理子模块的依赖版本,或引用第三方库的版本管理。
示例:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>4.3.7.RELEASEversion>
<scope>importscope>
<type>pomtype>
dependency>
dependencies>
dependencyManagement>
Maven 支持依赖传递,即当一个项目依赖于某个库时,若该库本身依赖其他库,这些依赖会自动成为当前项目的依赖,直到最终没有更多的传递依赖为止。
假设我们有以下的项目依赖关系:
A
依赖项目 B
。B
依赖项目 C
。A
和 B
不需要直接依赖 C
,就可以引入 mysql-connector-java
和 lombok
依赖通过执行依赖树:
mvn dependency:tree
输出结果
[INFO] com.example:project-A:jar:1.0
[INFO] +- com.example:library-B:jar:1.0:compile
[INFO] | +- com.example:library-C:jar:1.0:compile
[INFO] | | +- mysql:mysql-connector-java:jar:8.0.23:compile
[INFO] | | \- org.projectlombok:lombok:jar:1.18.20:provided
[INFO] \- (omitted for brevity)
如果 A
项目现在不需要使用 mysql 的依赖或者单独使用其他版本的依赖,应该如何处理呢?
Maven 提供了依赖排除的工程,避免版本冲突或者不必要的依赖,可以在 pom
文件中进行配置。
<groupId>com.examplegroupId>
<artifactId>project-AartifactId>
<version>1.0version>
<dependencies>
<dependency>
<groupId>com.examplegroupId>
<artifactId>library-BartifactId>
<version>1.0version>
<exclusions>
<exclusion>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
exclusion>
exclusions>
dependency>
dependencies>
通过执行依赖树:
mvn dependency:tree
输出结果
[INFO] com.example:project-A:jar:1.0
[INFO] +- com.example:library-B:jar:1.0:compile
[INFO] | +- com.example:library-C:jar:1.0:compile
[INFO] | \- org.projectlombok:lombok:jar:1.18.20:provided
[INFO] \- (omitted for brevity)
MySQL 依赖没有出现在依赖树中,因为我们通过
明确排除了 MySQL
依赖。
如果 A
工程依赖 B
工程,B
工程依赖 C
工程,C
工程又反过来依赖 A
工程,那么在执行构建操作时会报下面的错误:
[ERROR] [ERROR] The projects in the reactor contain a cyclic reference:
Maven 的 继承 是一种在多模块项目或父子项目之间共享配置的机制。通过继承,子项目可以继承父项目的依赖、插件、属性、版本信息等配置,减少冗余的配置,提高可维护性。父子项目的关系是通过 parent
元素在 pom.xml
中实现的。
父项目(Parent Project):一个包含通用配置和版本管理的项目,其他子项目可以继承这些配置。
子项目(Child Project):继承父项目的 pom.xml
中定义的配置,包括依赖、插件、构建配置等
pom.xml
父项目 pom.xml
中可以定义通用的配置,例如依赖版本、插件配置、构建配置等。然后,子项目通过
元素继承这些配置。
父项目的 pom.xml
示例:
<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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>parent-projectartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>pompackaging>
<modules>
<module>child-projectmodule>
modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.3.9version>
dependency>
dependencies>
dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
pluginManagement>
build>
project>
在这个父项目中:
dependencyManagement
:管理所有子项目中共享的依赖版本。pluginManagement
:定义了子项目共享的插件和插件的配置。pom.xml
子项目通过
元素来继承父项目的配置。子项目的 pom.xml
中需要指明父项目的 groupId
、artifactId
和 version
。
子项目的 pom.xml
示例:
<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.0modelVersion>
<parent>
<groupId>com.examplegroupId>
<artifactId>parent-projectartifactId>
<version>1.0-SNAPSHOTversion>
parent>
<artifactId>child-projectartifactId>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
plugin>
plugins>
build>
project>
在这个子项目中:
spring-core
的版本。maven-compiler-plugin
插件的版本和配置。Maven 使用插件来执行不同生命周期阶段的任务,如编译、测试、打包等。每个生命周期都有多个阶段,而插件负责在这些阶段执行实际的任务。Maven 默认有多个生命周期,比如 清理生命周期、默认生命周期 和 站点生命周期。通过 build
标签中的
元素,你可以为不同的生命周期阶段指定插件。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-surefire-pluginartifactId>
<version>2.22.2version>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-jar-pluginartifactId>
<version>3.1.0version>
<configuration>
<finalName>my-appfinalName>
configuration>
plugin>
plugins>
build>
在上面的示例中,指定了三个常用的插件:
maven-compiler-plugin
:用于指定编译的 JDK 版本。maven-surefire-plugin
:用于运行单元测试。maven-jar-plugin
:用于创建 JAR 文件,并指定输出文件名。Spring Boot 提供了非常灵活的构建和打包方式,你可以通过 Maven 插件来定制 Spring Boot 项目的打包过程。Spring Boot 使用 spring-boot-maven-plugin
插件来创建可执行的 JAR 或 WAR 文件。
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>2.5.2version>
<configuration>
<finalName>my-springboot-appfinalName>
configuration>
plugin>
plugins>
build>