百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

让我们彻底了解Maven(一)—— 基础和进阶

ztj100 2025-02-20 18:55 12 浏览 0 评论

Maven大家都很熟悉,但是我们很多人,对它其实都是似乎很熟,但是又好像不熟悉的感觉,包括我,今天咱们就一起来彻底了解Maven的所有功能,我们从入门,到原理剖析,再到实践操作,最后是私服的搭建以及配置,整体并彻底了解一下Maven。

1. Maven的安装以及配置

1.1 Maven的下载

下载路径:Maven官网


上面一个是Linux环境,一个Windows环境,大家依照自己的环境下载解压即可(后面我会以Windows环境为例)。

1.2 环境变量的配置

在系统环境中,新建一个MAVENHOME或M2HOME的环境变量,值写成解压路径。
找到Path变量并编辑,在其中新增一行,配置一下bin目录:

%M2_HOME%\bin

之所以要配置这一步,主要是因为软件的bin目录,通常会存放一些可执行的脚本/工具,如JDK的bin目录中,就存放着javac、javap、jstack……一系列工具。如果不在Path中配置bin,那想要使用这些工具,只能去到JDK安装目录下的bin目录,然后才能使用。
配置完毕以后就会全局生效。

1.3 指定Maven本地仓库位置

找到根目录下的conf/settings.xml,然后点击编辑,找到标签,将其挪动到注释区域外,然后配置本地仓库位置:

空的本地目录(最好别带中文)

1.4 配置阿里镜像仓库

由于Apache的官方镜像位于国外,平时拉取依赖比较慢,我们一般配置阿里的镜像仓库。搜索标签:

  
    alimaven  
    aliyun maven  
    http://maven.aliyun.com/nexus/content/groups/public/
    central          

到这里,整个Maven安装流程全部结束,最后在终端工具,执行mvn -v命令,就可以看到Maven的版本信息。

1.5 Maven的IDEA配置

安装好Maven完毕后,通过IDEA工具来创建Maven项目,我们需要配置本地Maven及仓库位置:

1.6 Maven依赖的查询网站

Maven仓库官网

1.7 依赖的范围管理

有时候我们需要对引入的依赖进行范围管理,比如测试依赖包JUnit,我们只需要在我们进行单元测试的时候有效,其他环境不需要。这时候我们就可以通过标签来控制。


    org.springframework.boot
    spring-boot-starter-test
    2.1.8.RELEASE
    test

作用范围有三种:

  1. 主代码范围有效。
  2. 测试代码范围有效。
  3. 是否参与打包(package命令范围内)

    同时,标签还可以通过自定义的方式来添加其他的scope范围,例如Maven插件中使用的scope值:

    some.group
    some-artifact
    1.0
    plugin

这里的plugin就是自定义的scope,表示该依赖只在Maven插件中生效。

2. Maven的工作原理

在Maven中,整体会分为工程、仓库两大类,工程是“依赖使用者”,仓库是“依赖提供者”,关系如下:


这里面:
中央仓库:指的是镜像源,里面拥有海量的公共jar包资源;
远程仓库:也就是私服仓库,主要存储公司内部的jar包资源;
本地仓库:自己电脑本地的仓库,会在磁盘上存储jar包资源。

Maven的工作流程如下:

  1. 项目通过GAV坐标引入依赖,首先会去本地仓库查找jar包;
  2. 如果在本地仓库中找到了,直接把依赖载入到当前工程的External Libraries中;
  3. 如果没找到,则去读取settings.xml文件,判断是否存在私服配置;
  4. 如果有私服配置,根据配置的地址找到远程仓库,接着拉取依赖到本地仓库;
  5. 如果远程仓库中没有依赖,根据私服配置去中央仓库拉取,然后放到私服、本地仓库;

3. Maven的生命周期

在IDEA中我们可以看到Maven的九种Lifecycle命令,如下:


双击其中任何一个,都会执行相应的Maven构建动作,因为里面封装了Maven提供的命令。下面解释一下每个命令的作用:

  • clean:清除当前工程编译后生成的文件(即删除target整个目录);
  • validate:对工程进行基础验证,如工程结构、pom、资源文件等是否正确;
  • compile:对src/main/java目录下的源码进行编译(会生成target目录);
  • test:编译并执行src/test/java/目录下的所有测试用例
  • package:将当前项目打包,普通项目打jar包,webapp项目打war包;
  • verify:验证工程所有代码、配置进行是否正确,如类中代码的语法检测等;
  • install:将当前工程打包,然后安装到本地仓库,别人可通过GAV导入;
  • site:生成项目的概述、源码测试覆盖率、开发者列表等站点文档(需要额外配置);
  • deploy:将当前工程对应的包,上传到远程仓库,提供给他人使用(私服会用)。

Maven总共划分了三套生命周期:


主要看default,该生命周期涵盖了构建过程中的检测、编译、测试、打包、验证、安装、部署每个阶段。

要注意的是,同一个生命周期,执行当前命令的时候,前面的命令都会自动执行。
比如执行mvn test命令,它会先执行validate、compile这两个阶段,然后才会真正执行test阶段。

多命令同时执行:

mvn clean install

执行过程:先执行clean周期里的pre-clean、clean,再执行default周期中,validate~install这个闭区间内的所有阶段。

还有个小问题点:
就是我们的IDEA为什么还会有个Plugins的,如下:


这是因为:Maven插件会实现生命周期中的每个阶段,而每个阶段具体执行的操作,这会交给插件去干。当你双击Lifecycle中的某个生命周期阶段,实际会调用Plugins中对应的插件。

4. Maven的高级操作

4.1 依赖冲突

依赖冲突是指:在Maven项目中,当多个依赖包,引入了同一份类库的不同版本时,可能会导致编译错误或运行时异常。这种情况下,想要解决依赖冲突,可以靠升级/降级某些依赖项的版本,从而让不同依赖引入的同一类库,保持一致的版本号,还可以通过隐藏依赖、或者排除特定的依赖项来解决问题。

在这个之前我们得要了解一下什么是依赖传递性。

1. 依赖的传递性

依赖具有传递性,当我们引入了一个依赖的时候,就会自动引入该依赖引入的所有依赖,依次往下引入所有依赖。比如我引入spring-boot-starter-web,会自动引入:



总之就是当引入的一个包,如果依赖于其他包(类库),当前的工程就必须再把其他包引入进来。

2. 自动解决冲突

在绝对大多数情况下,依赖冲突问题并不需要我们考虑,Maven工具会自动解决,根据如下几个原则:

  1. 层级优先原则:Maven会根据依赖树的层级,来自动剔除相同的包,层级越浅,优先级越高,无效的包会自动变成灰色。
  2. 声明优先原则:上条原则是基于层级深度,来自动剔除冲突的依赖,假设同级出现两个相同的依赖,这时候同层级出现包冲突时,先声明的会覆盖后声明的,为此后者会被剔除。
  3. 配置优先原则:同级出现不同版本的相同类库时,后配置的会覆盖先配置的。

Maven会依据上述三条原则,帮我们智能化自动剔除冲突的依赖。

3. 主动排除依赖

所谓的排除依赖,即是指从一个依赖包中,排除掉它依赖的其他包,如果出现了Maven无法自动解决的冲突,就可以基于这种手段进行处理。如下:


    org.springframework
    spring-web
    5.1.8.RELEASE
    
        
        
            org.springframework
            spring-beans
        
    

4.2 Maven的分模块开发

分模块开发,即是指创建多个Maven工程,组成一个完整项目。通常会先按某个维度划分出多个模块,接着为每个模块创建一个Maven工程,比较常见的拆分维度有:

  • 接入维度:按不同的接入端,将项目划分为多个模块,如APP、WEB、小程序等;
  • 业务维度:根据业务性质,将项目划分为多个业务模块,如前台、后台、用户等;
  • 功能维度:共用代码做成基础模块,业务做成一个模块、API做成一个模块……。

多模块开发的好处有:

  • 简化项目管理,拆成多个模块后,每个模块可以独立编译、打包、发布等;
  • 提高代码复用性,不同模块间可以相互引用,可以建立公共模块,减少代码冗余度;
  • 方便团队协作,多人各司其职,负责不同的模块,Git管理时也能减少交叉冲突;
  • 构建管理度更高,更方便做持续集成,可以根据需要灵活配置整个项目的构建流程;

4.3 Maven聚合工程

聚合工程,即是指:一个项目允许创建多个子模块,多个子模块组成一个整体,可以统一进行项目的构建。
在此之前我们先理解一下父子工程:
父工程:不具备任何代码、仅有pom.xml的空项目,用来定义公共依赖、插件和配置;
子工程:编写具体代码的子项目,可以继承父工程的配置、依赖项,还可以独立拓展。

而Maven聚合工程,就是基于父子工程结构,来将一个完整项目,划分出不同的层次。

1. 聚合工程的创建

首先要创建一个空的Maven项目,作为父工程,这时可以在IDEA创建Maven项目时,把打包方式选成POM,也可以创建一个普通的Maven项目,然后把src目录删掉,再修改一下pom.xml:


pom

然后创建子工程



继续往下走即可。


这时候我们看父工程的pom.xml中,会用一个标签,来记录自己名下的子工程列表,而子工程的pom头,也多了一个标签包裹,如下:


注意:子工程下面也可以继续创建子工程,但是不建议,这样会比较混乱。

2. 聚合工程的依赖管理

一般情况下,为了防止依赖冗余,我们会将公共的依赖、配置、插件等,都可以配置在父工程里。然后会发现子工程都会继承了父依赖。
为了防止不同子工程引入不同版本的依赖,最好的做法是在父工程中,统一对依赖的版本进行控制,规定所有子工程都使用同一版本的依赖。可以使用标签来管理。

要注意如下两个的区别:
:定义强制性依赖,写在该标签里的依赖项,子工程必须强制继承;
:定义可选性依赖,该标签里的依赖项,子工程可选择使用。

注意:子工程在使用中已有的依赖项时,不需要写版本号,版本号在父工程中统一管理,这就满足了前面的需求。好处在于:以后为项目的技术栈升级版本时,不需要修改每个子工程的POM,只需要修改父POM文件即可,提高了维护性。

3. 聚合工程处理依赖冲突

大多数情况下,Maven会基于那三条原则,自动帮你剔除重复的依赖,如果无法剔除,就得使用“隐藏依赖”技术了,如下:
假如现有两个子工程001,002。
修改001的pom.xml,如下:


    org.springframework
    spring-aop
    5.1.8.RELEASE
    true

此时我们发现多了一个标签,该标签即是“隐藏依赖”的开关:
true:开启隐藏,当前依赖不会向其他工程传递,只保留给自己用;
false:默认值,表示当前依赖会保持传递性,其他引入当前工程的项目会间接依赖。

当开启隐藏后,其他工程引入当前工程时,就不会再间接引入当前工程的隐藏依赖,因此来手动排除聚合工程中的依赖冲突问题。

4. 父工程的依赖传递

有一个问题,比如项目需要用到Spring-Cloud-Alibaba的多个依赖项,如Nacos、Sentinel……等,根据前面所说的原则,由于这些依赖项可能会在多个子工程用到,最好的方式是定义在父POM的标签里,可是CloudAlibaba依赖这么多,一个个写未免太繁杂、冗余了吧?
如下:


    com.alibaba.cloud
    spring-cloud-alibaba-dependencies
    ${spring-cloud-alibaba.version}
    pom
    import

标签为import,通常用在聚合工程的父工程中,不过必须配合pom使用,表示把
spring-cloud-alibaba-dependencies的所有子依赖,作为当前项目的可选依赖向下传递。而当前父工程下的所有子工程,在继承父POM时,也会将这些可选依赖继承过来。

5. 聚合工程的构建

Maven聚合工程可以对所有子工程进行统一构建。


尾巴上带有root标识的工程,意味这是一个父工程,当你双击父工程的某个Lifecycle命令,它找到父POM的标签,再根据其中的子工程列表,完成对整个聚合工程的构建工作。

这里就有一个问题,我们怎么解决maven子工程之间的相互依赖,这个随着项目的复杂度提升,引用不当,就会出现,最好的办法就是解决这种问题,因为这种本质是不合理的,有如下解决方式:

  1. 将相互依赖的部分抽出来,单独形成一个模块,让这两个来分别依赖这个模块。
  2. 使用插件build-helper-maven-plugin。

6. 聚合打包跳过测试

有时候我们不想执行测试用例,但是如果直接双击父工程里的package命令,但test命令在package之前,按照之前聊的生命周期原则,就会先执行test,再进行打包。
解决办法:


先选中test命令,接着点击上面的闪电图标,这时test就会画上横线,表示该阶段会跳过。

同时还可以在pom.xml里,配置插件来精准控制,比如跳过某个测试类不执行,配置规则如下:


    
        
            maven-surefire-plugin
            2.22.1
            
                true
                
                    
                    **/XXX*Test.java
                
                
                    
                    **/XXX*Test.java
                
            
        
    

4.4 Maven属性

我们可以通过在POM的标签中,自定义属性,来进行版本管理,如下:


4.5 Maven的多环境配置

实际工作会分为开发、测试、生产等环境,不同环境的配置信息也略有不同,而大家都知道,我们可以通过spring.profiles.active属性,来动态使用不同环境的配置,而Maven为何又整出一个多环境配置出来呢?

带着问题我们先搭建一个SpringBoot版的Maven聚合工程。

首先创建一个只有POM的父工程。



    org.springframework.boot
    spring-boot-starter-parent
    2.1.5.RELEASE
    



4.0.0
com.zhuzi
maven_zhuzi
1.0-SNAPSHOT
pom



    8



    
    
        org.springframework.boot
        spring-boot-starter-web
    




    
    
        org.springframework.boot
        spring-boot-maven-plugin
    

接着来创建子工程。



    maven_zhuzi
    com.zhuzi
    1.0-SNAPSHOT

4.0.0


boot_zhuzi_001
boot_zhuzi_001
Demo project for Spring Boot

然后再做Maven多环境配置,找到父工程的POM进行修改,如下:


    
    
        dev
        
            dev
        
    
    
    
    
        prod
        
            prod
        
        
        
            true
        
    
    
    
    
        test
        
            test
        
    

配置完这个后,刷新当前Maven工程,IDEA中就会出现这个:


默认是prod,因为POM中用标签做了指定,然后在子工程的application.yml中,完成Spring的多环境配置,如下:

# 设置启用的环境
spring:
  profiles:
    active: ${profile.active}

---
# 开发环境
spring:
  profiles: dev
server:
  port: 80
---
# 生产环境
spring:
  profiles: prod
server:
  port: 81
---
# 测试环境
spring:
  profiles: test
server:
  port: 82
---

或者采用不同环境的yml来做区分配置信息。这时候会发现spring.profiles.active属性以前我们会写上固定的值,而现在写的是${profile.active}。这表示会从pom.xml中,读取profile.active属性值,而父POM中配了三组值:dev、prod、test,所以当前子工程的POM,也会继承这组配置,而目前默认勾选在prod上,所以最终表达的含义是spring.profiles.active=prod。

要注意的是,要想yml读取到pom文件的值,我们还得在父pom加一个依赖和插件:



    org.springframework.boot
    spring-boot-configuration-processor
    2.1.5.RELEASE
    true




    org.apache.maven.plugins
    maven-resources-plugin
    3.2.0
    
        UTF-8
        true
    

最后我们来启动子工程,操作流程如下:

  1. 在Maven工具的Profiles中勾选dev,并刷新当前项目;
  2. 接着找到子工程的启动类,并右键选择Run启动子项目。

底层它的执行流程是这样:

  • 启动时,pom.xml根据勾选的Profiles,使用相应的dev环境配置;
  • yml中${profile.active}会读到profile.active=dev,使用dev配置组;
  • application.yml中的dev配置组,server.port=80,所以最终通过80端口启动。

有人可能就要问,既然都是修改文件启动,这不就是从修改yml改成了修改pom呗,这样想就错了,因为你们肯定也发现了,我从头到尾一直是在父工程上的pom,修改环境配置,只需要修改一次,假如现在有20个微服务,如果去修改yml,就得每个都得修改,那还不把人累死,而修改父工程pom,只需要修改一次。

今天和大家就分享到这里吧,明天给大家分享下Maven私服的搭建过程,内容有点多,分两次写吧。

相关推荐

告别手动操作:一键多工作表合并的实用方法

通常情况下,我们需要将同一工作簿内不同工作表中的数据进行合并处理。如何快速有效地完成这些数据的整合呢?这主要取决于需要合并的源数据的结构。...

【MySQL技术专题】「优化技术系列」常用SQL的优化方案和技术思路

概述前面我们介绍了MySQL中怎么样通过索引来优化查询。日常开发中,除了使用查询外,我们还会使用一些其他的常用SQL,比如INSERT、GROUPBY等。对于这些SQL语句,我们该怎么样进行优化呢...

9.7寸视网膜屏原道M9i双系统安装教程

泡泡网平板电脑频道4月17日原道M9i采用Win8安卓双系统,对于喜欢折腾的朋友来说,刷机成了一件难事,那么原道M9i如何刷机呢?下面通过详细地图文,介绍原道M9i的刷机操作过程,在刷机的过程中,要...

如何做好分布式任务调度——Scheduler 的一些探索

作者:张宇轩,章逸,曾丹初识Scheduler找准定位:分布式任务调度平台...

mysqldump备份操作大全及相关参数详解

mysqldump简介mysqldump是用于转储MySQL数据库的实用程序,通常我们用来迁移和备份数据库;它自带的功能参数非常多,文中列举出几乎所有常用的导出操作方法,在文章末尾将所有的参数详细说明...

大厂面试冲刺,Java“实战”问题三连,你碰到了哪个?

推荐学习...

亿级分库分表,如何丝滑扩容、如何双写灰度

以下是基于亿级分库分表丝滑扩容与双写灰度设计方案,结合架构图与核心流程说明:一、总体设计目标...

MYSQL表设计规范(mysql表设计原则)

日常工作总结,不是通用规范一、表设计库名、表名、字段名必须使用小写字母,“_”分割。...

怎么解决MySQL中的Duplicate entry错误?

在使用MySQL数据库时,我们经常会遇到Duplicateentry错误,这是由于插入或更新数据时出现了重复的唯一键值。这种错误可能会导致数据的不一致性和完整性问题。为了解决这个问题,我们可以采取以...

高并发下如何防重?(高并发如何防止重复)

前言最近测试给我提了一个bug,说我之前提供的一个批量复制商品的接口,产生了重复的商品数据。...

性能压测数据告诉你MySQL和MariaDB该怎么选

1.压测环境为了尽可能的客观公正,本次选择同一物理机上的两台虚拟机,一台用作数据库服务器,一台用作运行压测工具mysqlslap,操作系统均为UbuntuServer22.04LTS。...

屠龙之技 --sql注入 不值得浪费超过十天 实战中sqlmap--lv 3通杀全国

MySQL小结发表于2020-09-21分类于知识整理阅读次数:本文字数:67k阅读时长≈1:01...

破防了,谁懂啊家人们:记一次 mysql 问题排查

作者:温粥一、前言谁懂啊家人们,作为一名java开发,原来以为mysql这东西,写写CRUD,不是有手就行吗;你说DDL啊,不就是设计个表结构,搞几个索引吗。...

SpringBoot系列Mybatis之批量插入的几种姿势

...

MySQL 之 Performance Schema(mysql安装及配置超详细教程)

MySQL之PerformanceSchema介绍PerformanceSchema提供了在数据库运行时实时检查MySQL服务器的内部执行情况的方法,通过监视MySQL服务器的事件来实现监视内...

取消回复欢迎 发表评论: