欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Maven 详解 —— Maven 必知必会

程序员文章站 2022-07-15 09:57:17
...

1. Maven 是什么?

Maven 是 Apache 软件基金会组织维护的一款自动化构建工具,专注服务于 Java 平台的项目构建和依赖管理

2. 为什么要用 Maven?

2.1 方便添加第三方 jar 包

在今天的 JavaEE 开发领域,有大量的第三方框架和工具可以供我们使用。要使用这些 jar 包最简单的方法就是复制粘贴到 WEB-INF/lib 目录下。但是这会导致每次创建一个新的工程就需要将 jar 包重复复制到 lib 目录下,从而造成工作区中存在大量重复的文件, 让我们的工程显得很臃肿。而使用 Maven 后每个 jar 包本身只在本地仓库中保存一份,需要 jar 包的工程只需要以坐标的方式简单的引用一下就可以了。不仅极大的节约了存储空间,让项目更轻巧,更避免了重复文件太多而造成的混乱。

2.2 方便解决 jar 包之间的依赖关系

jar 包往往不是孤立存在的,很多 jar 包都需要在其他 jar 包的支持下才能正常工作,这就是 jar 包之间的依赖关系。而这些依赖关系,我们不可能全部知道,特别是使用一个之前从未使用过的 jar 包是,我们更难以知道它所要依赖的其他 jar 包。如果我们少引入了一个相关依赖的包,我们的程序将不可能正常工作。当然。你可能会说,提供这些 jar 包的作者会留有所需依赖 jar 包的信息。那么再考虑一下,我们一个大型项目中所需要的 jar 包可能有上百个,这个时候,你还要自己手动的一个个去查看并添加这些 jar 包所依赖的其他包吗?

而我们的 Maven可以自动的将当前 jar 包所依赖的相关其他 jar 包全部导入进来,而不用我们去查看并添加,极大了节省了我们的时间和精力。

2.3 方便获取第三方 jar 包

在我们的 Java 项目开发所要使用到的 jar 包种类繁多,而不同的 jar 包可能有不同的官网以及不同的获取方式。有时候得为查找一个 jar 包耗费大量时间和精力。并且,有可能辛苦所获取的 jar 包里并没有自己所需要的类或方法,因为有时候获取 jar 包的方式不规范,会造成获取到的 jar 包也是不规范的。

而 Maven 可以提供一个完全统一的且规范的 jar 包管理体系。我们所需要的 jar 包可以直接以坐标的方式获取,Maven 会自动从*仓库中下载这些 jar 包,并且还会自动下载这些 jar 包所依赖的相关其他 jar 包。

2.4 将项目拆分成多个工程模块

现在 JavaEE 项目的开发规模越来越大,而如果一个项目非常庞大,就不适合使用package来划分模块,最好是每一个模块对应一个工程,利于分工协作。

而借助于maven就可以将一个项目拆分成多个工程,便于我们的协同开发。

3. 自动化构建

3.1 什么是构建?

首先要知道,构建并不是创建,创建了一个项目并不等于构建了一个项目。

构建的概念: 构建以我们编写的 Java 代码、框架配置文件、页面、图片等资源作为“原材料”,去“生产”出一个可运行的项目的过程。

3.2 构建过程的几个主要环节

  1. 清理: 删除以前的编译结果,为重新编译做好准备。
  2. 编译: 将 Java 源程序编译为字节码文件。
  3. 测试: 针对项目中的关键点进行测试,确保项目在迭代开发过程中关键点的正确性。
  4. 报告: 在每一次测试后以标准的格式记录和展示测试结果。
  5. 打包: 将一个包含诸多文件的工程封装为一个压缩文件用于安装或部署。 Java 工程对应 jar 包, Web
    工程对应 war 包。
  6. 安装: 在 Maven 环境下特指将打包的结果——jar 包或 war 包安装到本地仓库中。
  7. 部署: 将打包的结果部署到远程仓库或将 war 包部署到服务器上运行

3.3 自动化构建

其实构建过程中的这些环节,在 IDE 中都可以找到相应的操作,那为什么还要自动化构建呢?

因为这些操作都是固定且繁琐的,如果每次都需要我们自己去一步步操作,会很耗费时间。

这个时候,就可以使用 Maven 自动化构建了!

Maven 自动化构建可以自动的从构建过程的起点一直执行到最后的终点。

4. 约定的目录结构

约定的目录结构对于 Maven 实现自动化构建而言是必不可少的。比如自动编译, Maven 必须
能找到 Java 源文件, 下一步才能编译, 而编译之后也必须有一个准确的位置保持编译得到的字节码文件。
我们在开发中如果需要让第三方工具或框架知道我们自己创建的资源在哪,那么基本上就是两种方式:
①通过配置的形式明确告诉它
②基于第三方工具或框架的约定
Maven 对工程目录结构的要求就属于后面的一种,通过约定好的目录结构, Maven 就可以知道各种资源在哪。

Maven 约定的目录结构如下:

Maven 详解 —— Maven 必知必会

5. 仓库、坐标、pom

5.1 仓库

5.1.1 什么是仓库?

仓库是 Maven 用来管理插件和依赖,以及我们自己开发的项目的模块。

所有 Maven 项目都可以通过坐标从仓库中统一获取依赖 jar 包。

5.1.2 仓库的分类

  1. 本地仓库
    为当前电脑上的所有 Maven 工程服务。

  2. 远程仓库
    ①私服:架设在当前局域网环境下,为当前局域网范围内所有 Maven 工程服务。
    ②*仓库:架设在因特网上,为全世界所有 Maven 工程服务。

    ③*仓库的镜像: 架设在各个大洲,为*仓库分担流量。减轻*仓库的压力,同时更快的响应用户请求。

5.2 坐标

我们已经知道了所有 Maven 项目都可以通过坐标从仓库中统一获取依赖 jar 包,那坐标是什么样的?

<groupId>com.yuanxion.maven</groupId>
<artifactId>Hello</artifactId>
<version>0.0.1-SNAPSHOT</version>

其中:
groupid: 公司或组织的域名倒序+当前项目名称
artifactId: 当前项目的模块名称
version: 当前模块的版本

使用如上三个向量就可以在 Maven 的仓库中唯一的定位一个 Maven 工程。

那么如何通过这三个向量在仓库中查找对于 jar 包?

  1. 首先将三个向量连接起来。
    com.yuanxion.maven + Hello + 0.0.1-SNAPSHOT
  2. 然后根据连起来的字符串作为目录结构去仓库中查找
    com/yuanxion/maven/Hello/0.0.1-SNAPSHOT/Hello-0.0.1-SNAPSHOT.jar

5.3 pom

pom:Project Object Model (项目对象模型)

将 Java 工程的相关信息封装为对象作为便于操作和管理的模型。

主要使用是,在 Maven 工程中,通过在 pom.xml 文件中提供坐标,获取项目相关所需的依赖 jar 包。

pom 是 Maven 工程的核心配置。

6. 依赖管理

依赖管理是 Maven 中最关键的部分, 我们使用 Maven 最主要的就是使用它的依赖管理功能,下面看一下依赖管理相关的知识。

6.1 什么是依赖?

当 A jar 包用到了 B jar 包中的某些类时, A 对 B 就产生了依赖 。

6.2 Maven 中怎么使用依赖?

在 pom 文件中使用 dependency 标签指定被依赖的 jar 包的坐标就可。
示例:

<dependency>
	<groupId>com.yuanxion.maven</groupId>
	<artifactId>Hello</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<scope>compile</scope>
</dependency>

有多个依赖时,可以在 dependency 标签最外层再加一个 dependencies标签。

示例:

<dependencies>
    
	<dependency>
		<groupId>com.yuanxion.maven</groupId>
		<artifactId>Hello</artifactId>
		<version>0.0.1-SNAPSHOT</version>
		<scope>compile</scope>
	</dependency>
    
    <dependency>
		<groupId>com.yuanxion.maven</groupId>
		<artifactId>Hey</artifactId>
		<version>0.0.1-SNAPSHOT</version>
		<scope>compile</scope>
	</dependency>
    
</dependencies>

6.3 依赖的范围

在上面的示例中,我们可以看到除了所需依赖 jar 包的坐标之外,还有一个 scope 配置,这个是用来设置依赖的范围的。

scope 配置中常用的 3 个可选值:compile、 test、 provided

三者的有效性的区别:

compile test provided
主程序 ×
测试程序 ×
参与部署 × ×

6.3 依赖的传递性

A 依赖 B,B 依赖 C,那么 A 能否使用 C 呢?
这要看 B 依赖 C 的范围,如果 B 依赖 C 的范围是 compile ,那么 A 也能使用 C;否则不能使用。

6.4 依赖的排除

如果我们的工程中引入了依赖 A,而 A 又依赖 B,那么 Maven 会自动的将 A 依赖的 B 自动导入当前工程。

但是有少数情况下,这个 B 可能是一个不稳定版,或者对当前工程有不好的影响,那么这个时候,我们就得将这个 B 排除掉。

排除的方法:在引入依赖 A 的 dependency 标签内,用 exclusion 标签标记出来;有多个需要排除的依赖可在exclusion 标签外层再加一个 exclusions 标签。

示例:

<dependency>
	<groupId>com.yuanxion.maven</groupId>
	<artifactId>AAA</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<scope>compile</scope>
    <exlusions>
        <exclusion>
            <groupId>com.yx.maven</groupId>
			<artifactId>BBB</artifactId>
        </exclusion>
    <exlusions>
</dependency>

6.5 统一管理所依赖 jar 包的版本

对同一个框架的一组 jar 包最好使用相同的版本,并且统一版本还可以方便升级框架。

  1. 首先统一版本号

    <properties>
        <yx.Spring.version>4.3.4.RELEASE</yx.Spring.version>
    </properties>
    

    其中<yx.Spring.version> 是自定义标签。

  2. 在需要使用版本的地方使用:${标签名}

    <dependencies>
        
    	<dependency>
    		<groupId>com.yuanxion.maven</groupId>
    		<artifactId>Hello</artifactId>
    		<version>${yx.Spring.version}</version>
    	</dependency>
        
    </dependencies>
    

另外,这个 properties 里还可以设置项目中一些其它的相关配置,比如字符集:

<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

6.6 解决 jar 包冲突(依赖的原则)

1. 路径最短者优先
项目A 依赖 B,B 又依赖 C。
并且 B 和 C 都依赖 D,不过依赖的版本不一样,B 依赖是 D-1.0 版本,C 依赖的是 D-2.0 版本。
因为项目 A 是先依赖的 B ;然后通过 B 依赖 C 的依赖传递,A 也依赖了 C 。
A 到 B 的路径相当于是 1,A 到 D-1.0 的路径相当于就是 2 ;

A 到 C 的路径相当于是 2,A 到 D-2.0 的路径相当于就是 3 ;
所以 A 会根据最短路径优先的原则,会使用 D-1.0 。

Maven 详解 —— Maven 必知必会

2. 路径相同时,先声明者优先

这里“声明”的先后指的是在 pom 中的 dependency 标签的先后顺序。
比如:
项目A 依赖 B,B 依赖 D-1.0;
项目A 依赖 C,C 依赖 D-2.0。
因为 D-1.0 与 D-2.0 的路径相同,路径最短者优先原则就没用了。

这个时候应该看 B 和 C 在 pom 中声明的顺序,先声明的优先。

<dependencies>
   
	<dependency>
		<groupId>com.yuanxion.maven</groupId>
		<artifactId>B</artifactId>
		<version>${yx.Spring.version}</version>
	</dependency>
    
    <dependency>
		<groupId>com.yuanxion.maven</groupId>
		<artifactId>C</artifactId>
		<version>${yx.Spring.version}</version>
	</dependency>
    
</dependencies>

上面的声明中,B 比 C 先声明,所以使用 B 的依赖:D-1.0 。

7. Maven 生命周期

7.1 什么是 Maven 的生命周期?

Maven 生命周期定义了各个构建环节的执行顺序,有了这个清单, Maven就可以自动化的执行构建命令了。

Maven 有三套相互独立的生命周期:

  1. Clean Lifecycle 在进行真正的构建之前进行一些清理工作。
  2. Default Lifecycle 构建的核心部分,编译,测试,打包,安装,部署等等。
  3. Site Lifecycle 生成项目报告,站点,发布站点。

这些生命周期是相互独立的,我们可以只调用 clean 来清理工作目录,或者只调用 site 来生成站点 ,当然也可以运行 mvn clean install site 一起运行这三套生命周期。

每套生命周期都由一组阶段(Phase)组成,我们平时在命令行输入的命令总会对应于一个特定的阶段。比如,运行 mvn clean,这个 clean 是 Clean 生命周期中的一个阶段。

7.2 Clean 生命周期的各阶段

  1. pre-clean :执行一些需要在 clean 之前完成的工作;
  2. clean :移除所有上一次构建生成的文件;
  3. post-clean :执行一些需要在 clean 之后立刻完成的工作。

7.3 Site 生命周期的各阶段

  1. pre-site :执行一些需要在生成站点文档之前完成的工作
  2. site : 生成项目的站点文档
  3. post-site : 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
  4. site-deploy :将生成的站点文档部署到特定的服务器上

这里面经常用到的是 site 阶段和 site-deploy 阶段,用于生成和发布 Maven 站点,这是 Maven 相当强大
的功能,可以自动生成文档及统计数据。

7.4 Default 生命周期的各阶段

Default 生命周期是 Maven 生命周期中最重要的一个,绝大部分工作都发生在这个生命周期中。

下面这些阶段中黑体加粗的是比较重要和常用的阶段:

  1. validate
  2. generate-sources
  3. process-sources
  4. generate-resources
  5. process-resources :复制并处理资源文件,至目标目录,准备打包。
  6. compile :编译项目的源代码。
  7. process-classes
  8. generate-test-sources
  9. process-test-sources
  10. generate-test-resources
  11. process-test-resources :复制并处理资源文件,至目标测试目录。
  12. test-compile :编译测试源代码。
  13. process-test-classes
  14. test :使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。
  15. prepare-package
  16. package :接受编译好的代码,打包成可发布的格式,如 JAR。
  17. pre-integration-test
  18. integration-test
  19. post-integration-test
  20. verify
  21. install :将包安装至本地仓库,以让其它项目依赖。
  22. deploy :将最终的包复制到远程的仓库,以让其它开发人员与项目共享或部署到服务器上运行。

7.5 生命周期与自动化构建

运行任何一个阶段的时候,它前面的所有阶段都会被运行。

比如我们运行 mvn install 的时候,代码将前面的 compile,test,package 都运行了。 这就是 Maven 为什么能够自动执行构建过程的各个环节的原因。

此外, Maven 的插件机制是完全依赖 Maven 的生命周期的,因此理解生命周期至关重要。

8. 插件和目标

Maven 的核心只定义了抽象的生命周期,具体的任务都是交由插件完成的。

每个插件都能实现多个功能,每个功能就是一个插件目标。

Maven 的生命周期与插件目标相互绑定,以完成某个具体的构建任务。
比如
compile 就是插件 maven-compiler-plugin 的一个目标;
pre-clean 是插件 maven-clean-plugin 的一个目标。

9. 继承

9.1 为什么需要继承?

因为非 compile 范围的依赖信息是不能在“依赖链”中传递的, 所以有需要的工程只能单独配置。 例如: 很多模板都需要测试的 junit ,junit 的范围是 test,不能传递依赖,而此时如果需要将各个模板中的 junit 版本统一,那么手动设置和修改各个模板中的版本号是非常耗时的。

而这个时候使用继承机制,就可以将这样的依赖信息统一提取到父工程模块中进行统一管理。

9.2 继承的使用步骤

9.2.1 创建父工程

创建父工程和创建一般的 Java 工程操作一样,唯一需要注意的地方是: 打包方式要设置为 pom。

9.2.2 在子工程中引用父工程

<parent>
    <!-- 父工程坐标 -->
	<groupId>com.atguigu.maven</groupId>
	<artifactId>Parent</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<!-- 指定从当前子工程的pom.xml文件出发,查找父工程的pom.xml的路径 -->
	<relativePath>../Parent/pom.xml</relativePath>
</parent>

此时如果子工程的 groupId 和 version 如果和父工程重复,则可以省略。

9.2.3 在父工程中管理依赖

将 父工程中 pom 文件中的 dependencies 标签,用 dependencyManagement 标签括起来。

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.9</version>
			<scope>test</scope>
		</dependency>
    </dependencies>
</dependencyManagement>

9.2.4 在子工程中指定需要的依赖

无需范围和版本号,会从父工程统继承过来。

<dependencies>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
	</dependency>
</dependencies>

如果子工程中的依赖指定了版本号,则会使用子工程指定的版本号。

10. 聚合

10.1 为什么要使用聚合?

将多个工程拆分为模块后, 需要手动逐个安装到仓库后依赖才能够生效。 修改源码后也需要逐个手动进行 clean 操作。 而使用了聚合之后就可以批量进行 Maven 工程的安装、清理工作。 节省时间,提升效率。

10.2 聚合的配置

在总的聚合工程中使用 modules/module 标签组合, 指定模块工程的相对路径即可 。

<modules>
	<module>../Hello</module>
	<module>../HelloFriend</module>
	<module>../MakeFriends</module>
</modules>

11. 其他类似的版本控制工具?

Ant : 具有编译、测试、打包功能,不过没有依赖管理功能。

Gradle : Maven 的功能都有,并且支持 Groovy 语言。

Gradle 和 Maven 的区别: Gradle 使用 Groovy 这种特定领域语言(DSL)来管理构建脚本,而不再使用 XML 这种标记性语言。因为项目如果庞大的话,XML 很容易就变得臃肿。

12. Maven 依赖查询网站

https://mvnrepository.com/


看完之后,如果还有什么不懂的,可以在评论区留言,会及时回答更新。

点个关注,不再迷路

这里是猿兄,为你分享程序员的世界。

非常感谢各位大佬们能看到这里,如果觉得文章还不错的话,
求点赞???? 求关注???? 求分享????求评论????
这些对猿兄来说真的 非常有用!!!

注: 如果猿兄这篇博客有任何错误和建议,欢迎大家留言,不胜感激!