Maven入门

Maven入门

我们在学习了很多Java的基础语法后总是会有这样的疑问:到底如何才能把我所学到的一个个语法转变成实际可用的应用?到底接下来还应该学习什么?这个问题的答案哼复杂,但我会试着逐渐的向你解释这一切

首先我们需要学习的是Maven,Java程序员使用最多的依赖管理工具。我们都知道只依靠一个人的力量是难以办成大事的,为了简化解决问题的流程,我们必须大量使用前人留下的各种库或者说为程序添加各种依赖,可这种添加该如何进行呢,比如在网上乱七八糟的资料中费尽千辛万苦的找到相应的源代码,在费劲巴拉的下载下来,然后在拷贝到你的项目中吗?这样做行不行,当然没有任何问题,但是请不要忘了,在一个大型项目中会用到数以百计的依赖项,且对这些依赖项的版本有严格要求,此外还存在着数百个依赖项互相依赖的问题。仔细想象,单靠人力一点点排查处理是不是有点太过复杂了呢,所以程序员们创造了Maven。

Maven译为专家,高手,是Apache旗下的一个开源的纯Java项目,基于项目对象模型(POM)的概念,用于对Java项目构建与依赖管理,当然,随着发展,Maven当前也被应用于其他语言的项目中

Maven可以帮我们完成代码的编译,测试,打包,安装,部署等操作,轻松管理项目的依赖项

Maven项目的创建

Maven本身存在着一套基本的命令行语法,但是基本没什么人用,所以我就不做过多的介绍了,我们直接使用idea来完成Maven的使用

想要创建一个Maven项目非常的简单,在创建新项目时idea直接提供了以Maven的架构创建项目的选择。

image

我们来看看idea帮我们生成的架构

image

为了方便处理,Maven事先约定了一套固定的架构,各个文件夹中.idea是idea的信息,无需理会,src下放置主要的源代码,其中main下为项目的实际源代码,而test下则为测试用代码,此外,main和test下各有一个resources文件夹用来存放需要用到的静态资源(包括一些实现准备好的图片文本数据等)

Target是编译的输出目录所有编译时产生的文件将存放在此处(只有在第一次编译后target才会出现)

.gitignore与git的使用有关,这里不多解释,接下来就是今天的重头戏之一,pom.xml

Maven导入依赖

之前提到过,Maven可以很方便的导入依赖,那么这一切该如何实现呢,答案是通过编写该项目的pom,让我们来看一看刚才创建的项目的pom中的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<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>
<!-->上面的这一部分是用来规定Maven的版本,使用的xml的版本等的内容,不用管<-->
<groupId>org.example</groupId>
<artifactId>MavenTest</artifactId>
<version>1.0-SNAPSHOT</version>
<!-->上面这三部分是每个项目的唯一标识,标识作者的groupid,标识项目名称的artifactid以及说明当前版本的version,你可以根据项目的实际情况修改<-->
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-->这里规定使用jdk21,并使用utf-8作为文件编码格式,同样是自动生成的,不用管<-->
</project>

上面应当是你pom的初始形式,我们应当会在后面详细的解释一下xml的语法,但这里暂时不去展开

所有的依赖项应当写在一对<>dependencies标签中,就像这样

1
2
3
<dependencies>
<!-->这里用来写需要的依赖项<-->
</dependencies>

而每个依赖性的格式都是相同的,如下面引入lombok这一依赖,就像上面提到的标识自己的项目一样,我们需要提供我们需要的依赖的作者,名称与版本

1
2
3
4
5
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>

最后经过一番修改,引入了Mybatis和lombok两项依赖的pom文件如下

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
<?xml version="1.0" encoding="UTF-8"?>
<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>

<groupId>org.example</groupId>
<artifactId>MavenTest</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies>

</project>

在每次修改pom文件后idea都会提示你进行同步,在一键同步后对应的依赖就被成功导入

Maven的依赖导入是怎样实现的

你可以猜猜Maven的依赖导入是怎样实现的,是不是想过各种各样的复杂技术,实际上这种东西简单的令人难以置信–Maven创建了一个无比巨大的仓库,在这个仓库中包含着数以千万计的项目,然后如果你需要某个项目作为依赖项,就直接从这个仓库中下载并保存在一个名为.m2的文件夹中(这个文件夹一般在C盘下当前用户的文件夹中)

当然,当你使用过一次该项依赖后在下次使用时就不会从远程仓库下载而是直接从本地获取

注意,由于Maven的仓库位于国外,考虑到我国国情,下载慢甚至是下载失败都是有可能的,解决这一问题的方法除了翻墙之外还可以考虑使用镜像站

可以配置IDEA自带的Maven插件远程仓库镜像地址,我们打开IDEA的安装目录,找到安装根目录/plugins/maven/lib/maven3/conf文件夹,找到settings.xml文件,打开编辑,找到mirros标签,添加以下内容:

1
2
3
4
5
6
<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>

镜像仓库中的内容与国外仓库每日同步,所以不用担心不能获得需要的依赖

依赖的精细化管理

当一个项目的体量足够大的时候我们就不能简单的闭着眼一通导入,还必须考虑各种复杂的情况,Maven对每个依赖还提供了一些属性用来确保依赖的合理性

optional

这一属性用于指示某个依赖是可选的,或者说不是必须的.我们都能理解一个情况,实际上我们所用的大多数依赖项也有自己的依赖,但这些依赖的依赖很可能对我们而言不是必须的,这个时候可以使用optional属性

1
2
3
4
5
6
7
8
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>example-library</artifactId>
<version>1.0.0</version>
<optional>true</optional>
</dependency>
</dependencies>

举一个简单的例子,假设现在有三个项目ABC,其中A使用了B,B使用了C,如果B在引入C时添加了<optional>true</optional>,表明C对B的功能来说并非必须,A在继承B的内容时会忽略C的内容(如果需要C,需要在pom中主动声明),而如果没有添加,则为了保证程序的正常运行,在引入B的同时还会引入C

exclusions

既然父项目可以选择不将自己的父项目继承给子项目,子项目自然也可以拒绝父项目的给予.这就是exclusions的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>example-library</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>org.unwanted</groupId>
<artifactId>unwanted-library</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

不需要提供具体的版本,凡是该项目,一律拒绝继承,这种设计实际上是为了程序员能准确的控制某个依赖的版本.例如当前项目同时需要BC两个依赖项,而B依赖于1.0版本的A,C依赖于2.0版本的A,如果同时引入这两个项目,会导致A中的同名方法的实现完全不同造成错误.为了避免这一情况,我们直接在引入BC时选择拒绝A,此后主动的通过显示声明引入A,确保A的版本可控

scope

这个属性于前面两个稍有不同,规定了依赖的作用域.什么是作用域?简单来说其实很多依赖是没有必要一直存在的,比如测试用框架JUnit,只在测试时发挥效果,所以没有必要让他在所有时刻都发挥效果.再比如实现Java于mysql数据库交互的jdbc,实质上是有C++完成的,不需要参与编译的过程,只需要参与运行的过程,为了应对这些情况,就有了我们的scope,scope可选的值有

Scope 值 说明 是否传递依赖 参与阶段
compile 默认值,表示依赖在编译、测试和运行时均可用。通常用于代码中直接使用的依赖(例如工具类库)。 编译、测试、运行
provided 表示依赖需要在编译时可用,但运行时由运行环境提供。常见于 Servlet API 或 JDK 类库,编译时需要但无需打包到最终应用中。 编译、测试(运行时由容器提供)
runtime 表示依赖在运行时需要,但编译时不需要(如 JDBC 驱动)。 测试、运行
test 表示依赖仅在测试阶段使用(如 JUnit 或 Mockito)。 测试
system 类似于 provided,但需要显式指定依赖的路径(systemPath),而且路径不能通过 Maven 仓库解析。通常只在特殊情况下使用,如没有 Maven 仓库的本地库。 编译、测试
import 仅用于依赖类型为 pom 的 BOM(Bill of Materials)文件,表示将该依赖的 <dependencyManagement> 内容导入到当前项目中。 N/A 不直接影响生命周期,仅用于依赖版本管理

我知道现在的你不一定能够看得懂这个,但不用着急,只需要知道有这么回事就好了

子项目

注意,子项目与父项目的关系并不是简单的依赖于被依赖的关系,它们之间共用相同的版本号,对外界来说实际上是一个项目,而普通的依赖于被依赖项显然是两个独立的项目.实际上在大型项目的开发中,通常都会将大项目拆分为诸多的向项目进行开发以实现更为细致的管理

创建子项目非常简单,在idea左边栏中右键当前项目,选择新建模块即可,我们来看一看子项目的pom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>org.example</groupId>
<artifactId>MavenTest</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<artifactId>childTest</artifactId>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

</project>

我们可以看到子项目值规定了父项目以及子项目的名称,子项目默认继承父项目的全部依赖(有optional选项的除外),也可以进一步根据需要添加子项目专属的一些依赖

Maven的其他功能

首先看看idea上Maven提供的功能

image

我们一项一项的来解释

  • clean清空,这个命令将会清空整个target文件夹,确保在下次编译时缓存全部更新
  • validate应当翻译为可用性检查,负责检查所有的pom文件的配置是否符合规范
  • compile编译,将所有的.java文件编译为.class类文件
  • test测试,执行test文件夹中的测试代码测试所有的代码是否正常(具体的测试方法过两天会讲)
  • package打包,将编译后的文件与所有必要的内容整体打包为一个jar包便于管理与分发
  • verify辨识或者说是集成测试,主要是对全局功能的测试,而上面的test主要是分单元测试
  • install下载安装,将当前的项目放入到本地的Maven仓库中,以后将可以通过Maven直接从本地仓库获取该项目作为依赖
  • site生成站点文档,这和文档会介绍项目的结构功能质量(这个功能的实现需要你在源代码中提供相应的特定格式的注释(javadoc))
  • deploy部署,用来将项目上传到远程仓库,这个仓库需要你提前配置

注意,上面提到的这些功能实际上都是通过一个个插件提供的,在有必要的情况下,我们可以给Maven添加更多的插件实现更丰富的功能

结语

OK,当前系列第一篇完成,马上出第二篇

738606cd c446 46eb ab9c 97cf88791ca7


Maven入门
http://soulmate.org.cn/2025/01/18/Maven入门/
作者
Soul
发布于
2025年1月18日
许可协议