Maven
发表于:2023-09-17 | 分类: 工具
字数统计: 5.2k | 阅读时长: 20分钟 | 阅读量:

1. Maven 简介

Maven 是 Apache 软件基金会组织维护的一款专门为 Java 项目提供构建依赖管理支持的工具。

Maven 作为依赖管理工具

随着框架的封装程度越来越高,项目中使用的jar包也越来越多。jar 包所属技术的官网通常是英文界面,网站的结构又不尽相同,甚至需要通过特殊的工具下载。第三方网站提供下载。问题是不规范,在使用过程中会出现各种问题。

  • jar包的名称
  • jar包的版本
  • jar包内的具体细节

使用 Maven 后,依赖对应的 jar 包能够自动下载,方便、快捷又规范。

框架中使用的 jar 包,不仅数量庞大,而且彼此之间存在错综复杂的依赖关系。依赖关系的复杂程度,已经上升到了完全不能靠人力手动解决的程度。另外,jar 包之间有可能产生冲突。进一步增加了我们在 jar 包使用过程中的难度。

使用 Maven 则几乎不需要管理这些关系,极个别的地方调整一下即可,极大的减轻了我们的工作量。

依赖管理中要解决的具体问题:

  • jar 包的下载:使用 Maven 之后,jar 包会从规范的远程仓库下载到本地
  • jar 包之间的依赖:通过依赖的传递性自动完成
  • jar 包之间的冲突:通过对依赖的配置进行调整,让某些jar包不会被导入

Maven 作为构建管理工具

当我们使用 IDEA 进行开发时,构建是 IDEA 替我们做的。脱离 IDE 环境仍需构建

00666aca63d45807532bd4dfc0a3e51d.png

构建过程包含的主要的环节:

  • 清理:删除上一次构建的结果,为下一次构建做好准备
  • 编译:Java 源程序编译成 *.class 字节码文件
  • 测试:运行提前准备好的测试程序
  • 报告:针对刚才测试的结果生成一个全面的信息
  • 打包
    • Java工程:jar包
    • Web工程:war包
  • 安装:把一个 Maven 工程经过打包操作生成的 jar 包或 war 包存入 Maven 仓库
  • 部署
    • 部署 jar 包:把一个 jar 包部署到 Nexus 私服服务器上
    • 部署 war 包:借助相关 Maven 插件(例如 cargo),将 war 包部署到 Tomcat 服务器上

2. Maven 环境配置

下载界面:Maven – Download Apache Maven

其中 tar.gz 是Linux系统使用的版本,zip是Windows适用版本,source表示可以看源码

需要将下载的核心程序压缩包解压到非中文、无空格的目录,在解压目录中,我们主要关注Maven的核心配置文件:conf/settings.xml

  1. 指定本地仓库:

本地仓库默认值:用户的家目录/.m2/repository。家目录往往是C盘,建议将Maven的本地仓库放在其他盘符。

1
2
3
4
5
6
7
<!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository
<localRepository>/path/to/local/repo</localRepository>
-->
<localRepository>H:\Maven-repository</localRepository>
  1. 配置阿里云提供的镜像仓库

Maven 下载 jar 包默认访问境外的中央仓库。改成阿里云提供的镜像仓库,可以让 Maven 下载 jar 包的时候速度更快。

1
2
3
4
5
6
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
  1. 配置 Maven 工程的基础 JDK 版本
1
2
3
4
5
6
7
8
9
10
11
12
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
  1. 检查 JAVA_HOME 配置是否正确

Maven 是一个用 Java 语言开发的程序,它必须基于 JDK 来运行,需要通过 JAVA_HOME 来找到 JDK 的安装位置。

可以在命令行中使用指令验证:echo %JAVA_HOME%

  1. 配置 Path 环境变量

1da20272411bc458f01ee4fa5c0c7c6b.png
6bcaf916419fa52d222b2ecaa5345f57.png

配置环境变量的规律:
XXX_HOME 通常指向的是 bin 目录的上一级
PATH 指向的是 bin 目录

可以在命令行中使用指令验证:mvn -v

3. 概念引入

核心概念:坐标

在数学中可以使用x,y,z三个向量作为坐标系确定空间中唯一的点;Maven中也可以使用三个向量在 Maven 仓库中定位一个 jar 包

  • groupId:公司或组织的 id
  • artifactId:一个项目或者是项目中的一个模块的 id
  • version:版本号

举例:

1
2
3
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>

对应的 jar 包在 Maven 本地仓库中的位置:

1
Maven本地仓库根目录\javax\servlet\servlet-api\2.5\servlet-api-2.5.jar

使用命令生成Maven工程

2bbd24c273f1126b172e20025a5bda1e.png

运行 mvn archetype:generate 命令后:

1
2
3
4
5
6
7
8
9
10
11
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 7:【直接回车,使用默认值】

Define value for property 'groupId': com.test

Define value for property 'artifactId': pro01

Define value for property 'version' 1.0-SNAPSHOT: :【直接回车,使用默认值】

Define value for property 'package' com.test: :【直接回车,使用默认值】

Confirm properties configuration: groupId: com.test artifactId: pro01 version: 1.0-SNAPSHOT package: com.test Y: :【直接回车,表示确认。如果前面有输入错误,想要重新输入,则输入 N 再回车。】

version 中的 SNAPSHOT 表示快照版本,正在迭代过程中,不稳定的版本;RELEASE 表示正式版本

Maven 默认生成的工程,对 junit 依赖的是较低的 3.8.1 版本,我们改成较适合的 4.12 版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!-- 当前Maven工程的坐标 -->
<groupId>com.test</groupId>
<artifactId>pro01</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 当前Maven工程的打包方式,可选值有下面三种: -->
<!-- jar:表示这个工程是一个Java工程 -->
<!-- war:表示这个工程是一个Web工程 -->
<!-- pom:表示这个工程是“管理其他工程”的工程 -->
<packaging>jar</packaging>

<name>pro01-maven-java</name>
<url>http://maven.apache.org</url>

<properties>
<!-- 工程构建过程中读取源码时使用的字符集 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 当前工程所依赖的jar包 -->
<dependencies>
<!-- 使用dependency配置一个具体的依赖 -->
<dependency>
<!-- 在dependency标签内使用具体的坐标依赖我们需要的一个jar包 -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!-- scope标签配置依赖的范围 -->
<scope>test</scope>
</dependency>
</dependencies>

核心概念:POM

POM:Project Object Model,项目对象模型。和 POM 类似的是:DOM(Document Object Model),文档对象模型。它们都是模型化思想的具体体现。

POM 理念集中体现在 Maven 工程根目录下 pom.xml 这个配置文件中。所以这个 pom.xml 配置文件就是 Maven 工程的核心配置文件。其实学习 Maven 就是学这个文件怎么配置,各个配置有什么用。

核心概念:约定的目录结构

Maven 为了让构建过程能够尽可能自动化完成,所以必须约定目录结构的作用。例如:Maven 执行编译操作,必须先去 Java 源程序目录读取 Java 源代码,然后执行编译,最后把编译结果存放在 target 目录。

8fe7dfd5c9e429285137b6ebf45105fc.png

另外还有一个 target 目录专门存放构建操作输出的结果。

Maven 对于目录结构这个问题,没有采用配置的方式,而是基于约定。这样会让我们在开发过程中非常方便。如果每次创建 Maven 工程后,还需要针对各个目录的位置进行详细的配置,那肯定非常麻烦。

目前开发领域的技术发展趋势就是:约定大于配置,配置大于编码。

执行 Maven 的构建命令

运行 Maven 中和构建操作相关的命令时,必须进入到 pom.xml 所在的目录。

mvn -v 命令和构建操作无关,只要正确配置了 PATH,在任何目录下执行都可以。而构建相关的命令要在 pom.xml 所在目录下运行——操作哪个工程,就进入这个工程的 pom.xml 目录。

  • mvn clean:清理操作;删除 target 目录

  • 主程序编译:mvn compile

    主体程序编译结果存放的目录:target/classes

  • 测试程序编译:mvn test-compile

    测试程序编译结果存放的目录:target/test-classes

  • mvn test:测试操作

    测试的报告存放的目录:target/surefire-reports

  • mvn install

    安装的效果是将本地构建过程中生成的 jar 包存入 Maven 本地仓库。这个 jar 包在 Maven 仓库中的路径是根据它的坐标生成的。

安装操作会将 pom.xml 文件转换为 XXX.pom 文件一起存入本地仓库。所以我们在 Maven 的本地仓库中想看一个 jar 包原始的 pom.xml 文件时,查看对应 XXX.pom 文件即可,它们是名字发生了改变,本质上是同一个文件。

创建 Maven 版的 Web 工程

使用 mvn archetype:generate 命令生成 Web 工程时,需要使用一个专门的 archetype。

注意:如果在上一个工程的目录下执行 mvn archetype:generate 命令,那么 Maven 会报错:不能在一个非 pom 的工程下再创建其他工程

生成工程的命令:

1
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.4

参数 archetypeGroupId、archetypeArtifactId、archetypeVersion 用来指定现在使用的 maven-archetype-webapp 的坐标。

运行完指令后的操作和创建普通工程雷同,也是输入工程的坐标

生成的 pom.xml 文件中默认的打包方式是 war

1
<packaging>war</packaging>

可以将运行 mvn package 命令后生成 war 包复制到 Tomcat/webapps 目录下运行

让 Web 工程依赖 Java 工程

只有 Web 工程依赖 Java 工程,没有 Java 工程依赖 Web 工程。本质上来说,Web 工程依赖的 Java 工程其实就是 Web 工程里导入的 jar 包。执行打包命令后, Java 工程会变成 jar 包,放在 Web 工程的 WEB-INF/lib 目录下。

  • mvn dependency:list:查看当前 Web 工程所依赖的 jar 包的列表
  • mvn dependency:tree:以树形结构查看当前 Web 工程的依赖信息

显示格式:groupId:artifactId:打包方式:version:依赖的范围

依赖范围

标签的位置:dependencies/dependency/scope

标签的可选值:compile/test/provided/system/runtime/import

范围 main目录(空间) test目录(空间) 开发过程(时间) 部署到服务器(时间)
compile(默认值) 有效 有效 有效 有效
test 无效 有效 有效 无效
provided 有效 有效 有效 无效

compile:通常使用的第三方框架的 jar 包这样在项目实际运行时真正要用到的 jar 包都是以 compile 范围进行依赖的。比如 SSM 框架所需jar包。

test:测试过程中使用的 jar 包,以 test 范围依赖进来。比如 junit。

provided:在开发过程中需要用到的“服务器上的 jar 包”通常以 provided 范围依赖进来。比如 servlet-api、jsp-api。而这个范围的 jar 包之所以不参与部署、不放进 war 包,就是避免和服务器上已有的同类 jar 包产生冲突,同时减轻服务器的负担。

依赖的传递性

原则:在 A 依赖 B,B 依赖 C 的前提下,C 是否能够传递到 A,取决于 B 依赖 C 时使用的依赖范围。

  • B 依赖 C 时使用 compile 范围:可以传递
  • B 依赖 C 时使用 test 或 provided 范围:不能传递,所以需要这样的 jar 包时,就必须在需要的地方明确配置依赖才可以。

依赖的排除

配置依赖的排除其实就是阻止某些 jar 包的传递,避免 jar 包传递过来和其他 jar 包冲突。

配置方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>test.maven</groupId>
<artifactId>pro01</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
<!-- 使用excludes标签配置依赖的排除 -->
<exclusions>
<!-- 在exclude标签中配置一个具体的排除 -->
<exclusion>
<!-- 指定要排除的依赖的坐标(不需要写version) -->
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

继承

Maven工程之间,A 工程(子工程)继承 B 工程(父工程)

本质上是 A 工程的 pom.xml 中的配置继承了 B 工程中 pom.xml 的配置。

可以在父工程中统一管理项目中的依赖信息,具体来说是管理依赖信息的版本。

创建父工程:和pro01一样,创建好后,需要修改打包方式:

1
2
<!-- 当前工程作为父工程,它要去管理子工程,所以打包方式必须是 pom -->
<packaging>pom</packaging>

只有打包方式为 pom 的 Maven 工程能够管理其他 Maven 工程。打包方式为 pom 的 Maven 工程中不写业务代码,它是专门管理其他 Maven 工程的工程。

创建子工程:在父工程的根目录(与src目录同级)运行指令

创建完成后,父工程和子工程的pom.xml文件都会有修改

父工程:modules 和 module 标签是聚合功能的配置

1
2
3
4
5
<modules>  
<module>pro04-maven-module</module>
<module>pro05-maven-module</module>
<module>pro06-maven-module</module>
</modules>

子工程:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 使用parent标签指定当前工程的父工程 -->
<parent>
<!-- 父工程的坐标 -->
<groupId>test.com</groupId>
<artifactId>pro03</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<!-- 子工程的坐标 -->
<!-- 如果子工程坐标中的groupId和version与父工程一致,那么可以省略 -->
<!-- <groupId>test.com</groupId> -->
<artifactId>pro04</artifactId>
<!-- <version>1.0-SNAPSHOT</version> -->

父工程中配置依赖的统一管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 使用dependencyManagement标签配置对依赖的管理 -->
<!-- 被管理的依赖并没有真正被引入到工程 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>

子工程中引用被父工程管理的依赖:

1
2
3
4
5
6
7
8
9
<!-- 子工程引用父工程中的依赖信息时,可以把版本号去掉。	-->
<!-- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定。 -->
<!-- 具体来说是由父工程的dependencyManagement来决定。 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
</dependencies>

父工程声明自定义属性:

1
2
3
4
5
6
7
<!-- 通过自定义属性,统一指定Spring的版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<!-- 自定义标签,维护Spring版本数据 -->
<test.version>4.3.6.RELEASE</test.version>
</properties>

在需要的地方使用${}的形式来引用自定义的属性名:

1
2
3
4
5
6
<!-- 真正实现“一处修改,处处生效”。-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${test.version}</version>
</dependency>

聚合

使用一个“总工程”将各个“模块工程”汇集起来,作为一个整体对应完整的项目。

概念的对应关系:

从继承关系角度来看:

  • 父工程
  • 子工程

从聚合关系角度来看:

  • 总工程
  • 模块工程

好处:

  • 一键执行 Maven 命令:很多构建命令都可以在“总工程”中一键执行。

    以 mvn install 命令为例:Maven 要求有父工程时先安装父工程;有依赖的工程时,先安装被依赖的工程。我们自己考虑这些规则会很麻烦。但是工程聚合之后,在总工程执行 mvn install 可以一键完成安装,而且会自动按照正确的顺序执行。

  • 配置聚合之后,各个模块工程会在总工程中展示一个列表,让项目中的各个模块一目了然。

依赖循环问题:

如果 A 工程依赖 B 工程,B 工程依赖 C 工程,C 工程又反过来依赖 A 工程,那么在执行构建操作时会报错:循环引用

4. IDEA环境使用Maven

创建Project时使用Maven创建
4f801b79196fd68d1f17d1e6dc55c45a.png

一般不勾选idea自动生成的,直接点next,在新窗口中确定工程坐标的三个向量。

创建Project 后,DEA 会自动弹出提示,我们选择『Enable Auto-Import』,意思是启用自动导入。因为 Project、Module 新创建或 pom.xml 每次修改时都应该让 IDEA 重新加载 Maven 信息。这对 Maven 目录结构认定、Java 源程序编译、依赖 jar 包的导入都有非常关键的影响。

配置Maven信息

每次创建 Project 后都需要设置 Maven 家目录位置,否则 IDEA 将使用内置的 Maven 核心程序(不稳定)并使用默认的本地仓库位置。

93da6bba5c74cd9d36fabea945ea1730.png

创建Web模块工程:

和创建普通Java工程步骤一样,在创建完成后需要添加web工程,注意需要修改web.xml的路径:

d4e39af1d831f0ad00291217787622c4.png

其中 src\main\webapp 时按照Maven工程的目录结构设置

为了防止每次创建工程idea默认在c盘创建本地库,可以在idea自带Maven的settings.xml做修改

在idea中执行Maven命令

d8599f5a151d4b8e3cb6dbfca00d779e.png
de18e16d0c91ea494f097d7c4c0b6bca.png

1
2
3
# -D 表示后面要附加命令的参数,字母 D 和后面的参数是紧挨着的,中间没有任何其它字符
# maven.test.skip=true 表示在执行命令的过程中跳过测试
mvn clean install -Dmaven.test.skip=true

5. 其它核心概念

  1. 生命周期:

为了让构建过程自动化完成,Maven 设定了三个生命周期,生命周期中的每一个环节对应构建过程中的一个操作。

生命周期名称 作用 各个环节
Clean 清理操作相关 pre-clean clean post-clean
Site 生成站点相关 pre-site site post-site deploy-site
Default 主要构建过程 validate
generate-sources
process-sources
generate-resources
process-resources 复制并处理资源文件,至目标目录,准备打包。
compile 编译项目 main 目录下的源代码。
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources 复制并处理资源文件,至目标测试目录。
test-compile 编译测试源代码。
process-test-classes
test 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。
prepare-package
package 接受编译好的代码,打包成可发布的格式,如JAR。
pre-integration-test
integration-test
post-integration-test
verify
install 将包安装至本地仓库,以让其它项目依赖。
deploy 将最终的包复制到远程的仓库,以让其它开发人员共享;或者部署到服务器上运行(需借助插件,例如:cargo)。
  • 前面三个生命周期彼此是独立的。
  • 在任何一个生命周期内部,执行任何一个具体环节的操作,都是从本周期最初的位置开始执行,直到指定的地方。
  1. 插件和目标:

Maven 的核心程序仅仅负责宏观调度,不做具体工作。具体工作都是由 Maven 插件完成的。一个插件可以对应多个目标,而每一个目标都和生命周期中的某一个环节对应。

  1. 仓库:
  • 本地仓库:在当前电脑上,为电脑上所有 Maven 工程服务
  • 远程仓库:需要联网
    • 局域网:我们自己搭建的 Maven 私服,例如使用 Nexus 技术。
    • Internet
      • 中央仓库
      • 镜像仓库:内容和中央仓库保持一致,但是能够分担中央仓库的负载,同时让用户能够就近访问提高下载速度,例如:Nexus aliyun

建议:不要中央仓库和阿里云镜像混用,否则 jar 包来源不纯,彼此冲突。

上一篇:
Git
下一篇:
MyBatisPlus