- maven是什么
- 为什么需要maven
- maven的安装
- maven的基本概念
- 父子maven
- idea中的maven1
- 写一个maven插件
maven是什么
maven 是一个 java 生态下的的项目信息管理、项目构建、依赖管理工具。
为什么需要maven?
想想当不使用maven时,我们是如何开发java项目的?通常我们是使用 IDE(如eclipse、idea)来开发的,由idea来执行编译、打包、依赖管理;这里会存在很多的不便:
- 我们需要手动下载jar包,然后再通过IDE来管理
- IDE 中编译、测试、代码生成等工作都是相互独立的,难以一键完成所有的工作
- 更重要的是对于不同的 IDE,这些设置往往是不相同的,不利于团队协作。
而 maven 通过提供统一的项目结构与构建流程,提供了独立于 IDEA 的构建框架,可以在任何环境下对项目进行构建、打包和部署。
除此之外,使用 maven 还有一个额外的好处,即 Maven 对于项目目录结构、测试用例命名等内容都有既定的规则,遵循这些成熟的规则,可以免去用户在不同项目间切换的成本。
maven 安装
在安装 Maven前,首先确保正确安装了 JDK,Maven3.9 以后要求 JDK 1.8 及以上的版本。
以 windows 平台为例,在 Maven 官网下载页面中,选择 bin.zip 格式;
将下载文件压缩到某个目录下,这里选择D:\bin\apache-maven-3.0
,然后配置环境变量:
- 在系统变量中新建一个变量,变量名为
M2_HOME
,变量值为Maven的安装目录D:\bin\apache-maven-3.0。单击“确定”按钮, - 接着在系统变量中找到一个名为Path的变量,在变量值的末尾上%M2_HOME%\bin;。注意:多个值之间需要有分号隔开,然后单击“确定”按钮
💡配置环境变量的作用是,当我们再命令行中输入 maven 命令时,操作系统会按照当前目录、path 环境变量的顺序寻找寻找可执行文件。
.m2
目录
maven 依赖管理的工作机制是从本地仓库找需要的依赖,如果没有再从远程仓库中下载到本地仓库,再链接到项目中。
如果没有特殊设置,maven 本地仓库在C:\Users\Administrator.m2 目录下。
如果我们再开发过程中,想要引用自己开发的 jar 包(未发布到中央仓库)也可以放在这个本地仓库目录下。
设置 maven 仓库镜像
设置用户目录下 maven 地址:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository/>
<interactiveMode/>
<usePluginRegistry/>
<offline/>
<pluginGroups/>
<servers/>
<mirrors>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/central</url>
</mirror>
<mirror>
<id>repo1</id>
<mirrorOf>central</mirrorOf>
<name>central repo</name>
<url>http://repo1.maven.org/maven2/</url>
</mirror>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>apache snapshots</mirrorOf>
<name>阿里云阿帕奇仓库</name>
<url>https://maven.aliyun.com/repository/apache-snapshots</url>
</mirror>
</mirrors>
<proxies/>
<activeProfiles/>
<profiles>
<profile>
<repositories>
<repository>
<id>aliyunmaven</id>
<name>aliyunmaven</name>
<url>https://maven.aliyun.com/repository/public</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>MavenCentral</id>
<url>http://repo1.maven.org/maven2/</url>
</repository>
<repository>
<id>aliyunmavenApache</id>
<url>https://maven.aliyun.com/repository/apache-snapshots</url>
</repository>
</repositories>
</profile>
</profiles>
</settings>
最佳实践
在 maven 设置中,一些不是必须,但十分有用的实践。
设置MAVEN_OPTS环境变量
运行mvn命令实际上是执行了Java
命令,既然是运行Java
,那么运行Java
命令可用的参数当然也应该在运行mvn命令时可用。这个时候,MAVEN_OPTS
环境变量就能派上用场。
通常需要设置MAVEN_OPTS
的值为-Xms128m、-Xmx512m,因为Java默认的最大可用内存往往不能够满足Maven运行的需要,比如在项目较大时,使用Maven生成项目站点需要占用大量的内存,如果没有该配置,则很容易造成 java.lang.OutOfMemeoryError。因此,一开始就配置该变量是推荐的做法
不要使用IDE内嵌的Maven
idea 通常会内嵌 maven,而这个 maven 版本会比较新,往往和本地安装的 maven 版本不一致,所以推荐在IDE 中配置使用本地的 maven。
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <!-- pom当前模型版本,不要动他 -->
<!-- 一、基础配置 -->
<groupId>...</groupId>
<artifactId>...</artifactId> >
<version>...</version>
<packaging>...</packaging> <!-- 打包方式 -->
<dependencies>...</dependencies> <!-- 依赖列表 -->
<parent>...</parent> <!-- 继承的maven父项目 -->
<dependencyManagement>...</dependencyManagement> <!-- 依赖管理 -->
<modules>...</modules> <!-- 子模块列表 -->
<properties>...</properties> <!-- 自定义属性列表 -->
<!-- 二、项目构建 -->
<build>...</build> <!-- 构建设置 -->
<reporting>...</reporting> <!-- 报告设置 -->
<!-- 其他项目信息 -->
<name>...</name>
<description>...</description>
<url>...</url>
<inceptionYear>...</inceptionYear>
<licenses>...</licenses>
<organization>...</organization>
<developers>...</developers>
<contributors>...</contributors>
<!-- 环境配置-->
<issueManagement>...</issueManagement>
<ciManagement>...</ciManagement> <!-- 持续集成管理 -->
<mailingLists>...</mailingLists>
<scm>...</scm>
<prerequisites>...</prerequisites>
<repositories>...</repositories> <!-- 配置maven仓库,通常设置为阿里云 -->
<pluginRepositories>...</pluginRepositories>
<distributionManagement>...</distributionManagement>
<profiles>...</profiles> <!-- 配置文件列表 -->
</project>
**必要的项目坐标:**POM 文件必须具有 project 元素和四个必需字段,用于描述项目的基本信息。
modelVersion
:模型版本,不用动它groupId
artifactId
version
<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>
<!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.companyname.project-group,maven会将该项目打成的jar包放本地路径:/com/companyname/project-group -->
<groupId>com.companyname.project-group</groupId>
<!-- 项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
<artifactId>project</artifactId>
<!-- 版本号 -->
<version>1.0</version>
</project>
Packaging
元素用于定义项目构建格式,通常设为 war
或jar
。
build 配置
build 元素用于配置项目目录结构和管理插件。
build 元素从概念中可以分为两个部分:
BaseBuild
type
maven archetype 是 maven 项目模板工具,开发者使用预先定义好的 archetype 可以快速创建项目,并保持团队内部一致。
你也可以创建自己的 archetype,maven 也提供了一些 archetype,常见的如下:
Archetype** **ArtifactIds | Description |
---|---|
maven-archetype-archetype | 用于构造自定义原型项目的原型 |
maven-archetype-j2ee-simple | J2EE 项目原型 |
maven-archetype-plugin | 自定义插件原型 |
maven-archetype-quickstart | 一个快速 maven 项目原型 |
maven-archetype-webapp | webapp maven 项目原型 |
依赖管理
依赖管理是 maven 的核心功能之一,maven 具有强大且灵活的依赖管理机制。maven 中处理依赖的方式包括:直接引入依赖、传递依赖(引入依赖的依赖)、依赖继承、聚合(多模块)。
引入依赖
在 maven 中引入依赖需要在pom.xml
中dependencies
中配置,dependencies
是 pom 的基石,用于定义项目所依赖的外部库或模块。在dependencies
列表下的dependency
中我们指定需要引入的库的坐标、版本号,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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
...
</dependencies>
...
</project>
上边例子中我们引入了 Junit 库,注意 junit 库本身所依赖的其他库也会被引入。
注意到 dependency
元素下除了库的坐标外还有一些其他信息:
scope
**scope**
:依赖关系,可以理解为引入的依赖的类型,maven 对于不同的依赖关系会进行不同的处理。Maven定义了几种依赖关系,分别是:
注:上文中提到的编译、测试、运行、打包等涉及到 maven 项目构建过程,maven 项
optional
optional
:当该项目本身是依赖项时,将依赖项标记为可选
exclusions
exclusions
:有时我们引入一个依赖时并不想引入它的传递依赖,这时可以通过exclusions
元素排除它,比如有一个项目依赖于 Spring Boot Starter Web,而 Spring Boot Starter Web中包含了 Tomcat 作为默认的嵌入式容器。但是我们想要需要Jetty 作为 web 容器,这时可以使用 exclusions
来排除掉 Tomcat。
<project>
<groupId>com.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.6</version>
<!-- 排除Spring Boot Starter Web中的Tomcat -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加Jetty作为外部的Web容器依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<version>2.5.6</version>
</dependency>
</dependencies>
</project
传递依赖
传递性依赖是指:当我们在 maven 中引入一个依赖,比如spring-framework
,spring-framework
所依赖的 其他 jar 包也会自动引入。
注意依赖的范围会影响传递性依赖的结果。
Maven Dependency Mediation
(依赖调解)
maven 提供的传递性依赖机制,简化了依赖使用,大部分情况下,我们只需要关系项目的直接依赖,但有时候传递性依赖造成问题时候,我们就需要知道该传递性依赖是如何引入。
思考这样一个问题,项目中有这样的依赖关系: A->B->C->X(1.0)、A->D->X(2.0),X是A的传递性依赖,但是两条传递路径上由两个版本的 X,最终会引入哪个呢?这涉及到了 Maven Dependency Mediation
(依赖调解)的知识:
- 第一原则:路径最近者优先。该例子中X(1.0)的路径长度为3,而X(2.0)的路径长度为2,因此X(2.0)会被解析使用。
- 第二原则:第一声明者优先。在依赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用,顺序最靠前的那个依赖优胜
继承
支持继承是 Maven 在管理依赖方面一个突出的能力,当我们有一些公用的一些依赖、构建配置等,我们可以构建一个父 maven 项目,在父 maven 项目中引入这些公用的内容,子项目通过直接继承简化重复声明的不便。
注意,对于父项目 pom 或聚合项目 pom 文件中
packaging
属性通常设为 pom,从而限定在打包阶段不会执行打包。
比如我们开发 springboot 项目,通常会继承springboot-parent
,它提供了一系列常用的配置和依赖管理,包含大量常用库的版本定义、默认插件配置、默认属性和环境配置等。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
dependency management
dependency management
元素提供了一套用于集中管理项目依赖版本的机制,用于定义项目或子模块中的依赖版本和范围,但不实际引入这些依赖。
dependencyManagement
元素通常出现在父 POM 或聚合项目的 POM 文件中,当子模块引用一个在 dependencyManagement
中定义的依赖时,只需要引用依赖的坐标(不必指定版本),Maven 将自动使用 dependencyManagement
中定义的版本,从而避免在每个子模块中重复指定版本号,从而减少版本冲突。
<!-- 父pom -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.6</version> <!-- 仅仅是声明版本号 -->
</dependency>
</dependencies>
</dependencyManagement>
<!-- 在子模块中使用时 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 不需要指定版本号 -->
</dependency>
</dependencies>
🐽与 paren 不同,parent 属性直接继承父项目的所有配置,dependency management 只用于设置版本信息,但不实际引入这些依赖
聚合
简单的项目我们通常直接采用单模块构建即可,而对于大型项目,将所有代码组织到一个模块中并不利于项目管理和多人协作。
聚合或者称为多模块,是指 maven 提供的一种管理多个模块的一张机制,Maven 聚合项目是指一个 Maven 项目下包含多个子模块,每个子模块可以是一个独立的项目单元,而聚合项目可以统一管理这些子模块,这种结构有助于组织复杂的项目,提高代码的维护性。
聚合项目本身作为一个 maven 项目,也必须有自己的 pom.xml
,而同时其pom.xml
又有特殊之处:
- 聚合项目的 pom 中其打包方式packaging的值必须为pom,否则就无法构建。
- 在聚合项目的 pom 文件中使用
modules
中声明子模块列表 ,
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.codehaus.mojo</groupId>
<artifactId>my-parent</artifactId>
<version>2.0</version>
<packaging>pom</packaging>
<modules>
<module>my-project</module>
<module>another-project</module>
<module>third-project</module>
</modules>
</project>
注意,每个 module 的值都是一个当前 pom 的相对目录,通常来说我们把聚合项目 pom.xml 放在最外侧,比如:
.
|-- my-module
| -- pom.xml
|-- another-project
| --pom.xml
|-- third-project
| --pom.xml
-- pom.xml
但当我们的项目目录结构调整后,聚合项目 pom 中也要对应调整
// 目录结构
.
|-- my-module
| `-- pom.xml
`-- parent
`-- pom.xml
// 聚合pom.xml中modules
<modules>
<module>../my-module</module>
</modules>
聚合项目使用很简单,思考 maven 对于聚合项目做了那些事情?最重要的是我们可以在聚合项目目录下执行构建命令,这样所有的子模块都会进行构建,而 maven 会构建一个反应堆构建顺序(Reactor Build Order),分析各个子模块之间的依赖关系,决定出合理的模块构建顺序。
关于 ruoyi 多模块项目打包的思考
在学习 ruoyi 项目的时候,之前一直对于打包构建的方式不太理解,通过学习 maven,解答了自己的一些疑问,可能其他人也有类似误解,这里也记录下来:
❓问题 1: 比如说admin模块并没有依赖system模块,打包后为什么直接用admin下的target目录中的jar就可以部署呢?
回答:其实admin下是主模块,包含了启动类,同时admin通过引入framework依赖,间接的引入了system模块;
❓问题 2: 关于目录结构:springboot项目bean扫描默认是扫描启动类所在包下的所有子包中的bean,对于ruoyi多模块项目,实际文件目录结构并不是启动类在根目录下?
首先区分两个概念,源码中的文件路径和编译后的类路径:
包名和目录结构虽然在源码中的表现形式是文件路径(如src/main/java/com/sky/...
),但在编译后的class
文件中,它们被组织在一个类路径层级下。因此,Spring 在运行时扫描的是类路径,并不是直接扫描文件系统。
生命周期与插件
Maven另外两个核心概念是生命周期和插件,两者协同工作,密不可分。
maven 生命周期是对项目的构建流程进行抽象和统一,这个生命周期包含了项目的清理-初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤。
Maven的生命周期是抽象的,生命周期本身不做任何实际的工作,在Maven的设计中,实际的任务(如编译源代码)都交由插件来完成。
maven 插件机制是为大多数构建步骤提供并绑定了默认插件,例如,针对编译的插件有maven-compiler-plugin,同时用户也可以自己配置了插件的行为。生命周期和插件的关系如图所示:
理解上边这幅图很重要: maven 构建项目的流程流程就是按顺序执行 phase
,当遇到某个 phase
中绑定插件的goal
,就立即执行该 goal
。
生命周期
初学者往往会以为Maven的生命周期是一个整体,其实不然,Maven拥有三套相互独立的生命周期,它们分别为clean
、default
和site
。**clean**
生命周期的目的是清理项目,**default**
生命周期的目的是构建项目,而site**生命**
周期的目的是建立项目站点。
每个生命周期包含一些阶段(phase
),这些阶段是有顺序的,并且后面的阶段依赖于前面的阶段。
以clean生命周期为例,它包含的阶段有pre-clean、clean和post-clean。
比如我们在命令行执行$ maven clean
命令,pre-clean和clean阶段会得以顺序执行,而 post-clean 不会执行。
celan 生命周期包含的 phase 如下:
Phase | Description |
---|---|
pre-clean | 执行实际项目清理之前需要完成的工作 |
clean | 删除前一次构建生成的所有文件 |
post-clean | 执行项目清理后需要完成的工作 |
default 生命周期包含的phase如下(只列出核心阶段):
插件 goal
我们知道 maven 中实际干活的是插件,为了便于代码的复用和项目管理, maven 设计了插件 goal
的机制,一个goal
代表了一个特定的任务。比如maven-dependency-plugin
有十多个目标,每个目标对应了一个功能,比如分析项目依赖的 goal 为dependency:analyze
、列出项目依赖树的 goal 为 dependency:tree。
注意生命周期默认绑定有插件 goal,比如说当我们执行maven compile
,实际上执行的是compiler:compile
从代码的角度来理解插件 goal 更简单(源码角度也类似这样):可以将插件本身是一个 java 类,而插件 goal 其实是 类中的某个方法。
插件
插件是 maven 中非常重要的部分,maven 本质上是一个执行插件的框架,所有的工作都是由插件完成的。
maven 通过将生命周期与插件 goal 进行绑定,以完成特定的构建任务:
内置绑定插件
maven 内置了一些核心插件,并进行了默认绑定,当在命令行调用生命周期阶段时,对应的插件目标就会执行。
clean 生命周期和 site 生命周期相对简单。default 生命周期与插件目标的默认绑定关系相对复杂一些,**对于不同的 packaging 类型,如 jar 或 war, 默认绑定的插件也不同。 **
比如在 maven3.92 中,对于打包类型为jar
类型,默认绑定如下:
序号 | |||
---|---|---|---|
1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 |
而对于打包类型为war
,默认绑定为:
序号 | |||
---|---|---|---|
1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 |
pom.xml 中 build 配置
常用的 maven 插件总结 | 海汼部落 高品质的 大数据技术社区
❓问题 1:maven build配置中executions有哪些属性,是做什么用的
ChatGPT: 在 Maven 构建配置中,executions 元素用于定义对插件目标执行的细节,包括插件目标的调用时机、顺序、配置参数等。executions 元素可以包含以下属性:
- id:该属性用于唯一标识一个插件目标执行的实例。如果没有提供 id,Maven 会自动生成一个唯一的标识符。
- phase:指定插件目标执行的阶段。通过指定 phase 属性,可以告知 Maven 何时执行插件目标。常见的阶段包括 compile, test, package, install, deploy 等。
- goals:指定要执行的插件目标名称。通常,在 goals 属性中指定一个或多个目标名称,用逗号分隔。
- configuration:定义插件目标的配置参数。在 configuration 元素中可以设置插件目标的具体属性值,以满足项目的需求。
通过在 Maven 插件配置中使用 executions 元素,可以详细地控制插件目标的执行过程和行为,使得插件能够按照项目的特定需求和规范进行执行。
参考
《maven 实战》