Maven知识点整理

Maven不仅是依赖管理工具,准确来说是一个项目管理工具,贯穿了整个项目生命周期,编译,测试,打包,发布...
依赖是使用Maven坐标来定位的,而Maven坐标 主要 由GAV(groupId, artifactId, version)构成。
Maven思想:约定大于配置。

依赖归类

使用<properties>来归类管理。
示例:
<dependencies><dependency><groupId>org.spring.framework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.spring.framework</groupId><artifactId>spring-beans</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.spring.framework</groupId><artifactId>spring-web</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.spring.framework</groupId><artifactId>spring-mock</artifactId><version>${spring.version}</version></dependency>
</dependencies><properties><spring.version>2.5</spring.version>
</properties>

 多模块 & 继承

在Maven的多模块工程中,都存在一个pom类型的工程作为根模块,该工程只包含一个pom.xml文件,在该文件中以模块(module)的形式声明它所包含的子模块,即多模块工程。

在子模块的pom.xml文件中,又以parent的形式声明其所属的父模块,即继承。

多模块的好处是你只需在根模块中执行Maven命令,Maven会分别在各个子模块中执行该命令,执行顺序通过Maven的Reactor机制决定。

1)如果保留webapp和core中对maven-multi-module的父关系声明,即保留 “<parent>...  </parent>”,而删除maven-multi-module中的子模块声明,即“<modules>...<modules>”,会发生什么情况?此时,整个工程已经不是一个多模块工程,而只是具有父子关系的多个工程集合。如果我们在maven-multi-module目录下执行“mvn clean install”,Maven只会在maven-multi-module本身上执行该命令,继而只会将maven-multi-module安装到本地Repository中,而不会在webapp和core模块上执行该命令,因为Maven根本就不知道这两个子模块的存在。另外,如果我们在webapp目录下执行相同的命令,由于由子到父的关系还存在,Maven会在本地的Repository中找到maven-multi-module的pom.xml文件和对core的依赖(当然前提是他们存在于本地的Repository中),然后顺利执行该命令。

(2)如果保留maven-multi-module中的子模块声明,而删除webapp和core中对maven-multi-module的父关系声明,又会出现什么情况呢?此时整个工程只是一个多模块工程,而没有父子关系。Maven会正确处理模块之间的依赖关系,即在webapp模块上执行Maven命令之前,会先在core模块上执行该命令,但是由于core和webapp模块不再继承自maven-multi-module,对于每一个依赖,他们都需要自己声明,比如我们需要分别在webapp和core的pom.xml文件中声明对Junit依赖。

依赖范围(scope)

引入依赖范围的原因示例:

1 例如Junit,将Junit的jar包打包到发布包是没有必要的。

2 例如servlet-api,为了编译通过,我们引入servlet-api jar包;但因为web容器也会提供servlet-api,如果将servlet-api打包至WAR包,就会造成依赖冲突。

示例:

<dependency>  <groupId>junit</groupId>  <artifactId>junit</artifactId>  <version>4.4</version>  <scope>test</test>  
</dependency><dependency>  <groupId>javax.servlet</groupId>  <artifactId>servlet-api</artifactId>  <version>3.1</version>  <scope>provided</scope>  
</dependency>  

 scope详解:

1 complie

默认值。参与当前项目的编译,后续的测试,运行,是一个比较强的依赖。

2 test

参与包括测试代码的编译,执行。比较典型的如junit。

3 runtime

被依赖项目无需参与项目的编译,不过后期的测试和运行周期需要其参与。
与compile相比,跳过编译而已.
oracle jdbc驱动架包就是一个很好的例子,一般scope为runntime。另外runntime的依赖通常和optional搭配使用,optional为true。我可以用A实现,也可以用B实现。

4 provided

打包的时候不包进去,别的设施(如Web Container)会提供。
事实上该依赖理论上可以参与编译,测试,运行等周期。
相当于compile,但是在打包阶段做了exclude的动作。

5 system

从参与度来说,也provided相同,不过被依赖项不会从maven仓库抓,而是从本地文件系统拿,一定需要配合systemPath属性使用。

 

依赖范围及作用
依赖范围(scope)主源码classpath可用测试源码classpath可用会被打包
compile 缺省值TRUETRUETRUE
testFALSETRUEFALSE
runtimeFALSETRUETRUE
providedTRUE(外界提供)TRUEFALSE

scope的依赖传递

A–>B–>C。当前项目为A,A依赖于B,B依赖于C。知道B在A项目中的scope,那么怎么知道C在A中的scope呢?答案是:
当C是test或者provided时,C直接被丢弃,A不依赖C;
否则A依赖C,C的scope继承于B的scope。

下面是一张nexus画的图。 

分类器(classifer)

GAV是Maven坐标最基本最重要的组成部分,但GAV不是全部。

还有一个元素叫做分类器(classifier),90%的情况你不会用到它,但有些时候,分类器非常不可或缺。
举个简单的例子,当我们需要依赖TestNG的时候,简单的声明GAV会出错,因为TestNG强制需要你提供分类器,以区别jdk14和jdk15,我们需要这样声明对TestNG的依赖:

<dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>5.7</version><classifier>jdk15</classifier>
</dependency>

你会注意到maven下载了一个名为testng-5.7-jdk15.jar的文件。

其命名模式实际上是<artifactId>-<version>-<classifier>.<packaging>。

理解了这个模式以后,你就会发现很多文件其实都是默认构件的分类器扩展,如 myapp-1.0-test.jar, myapp-1.0-sources.jar。

分类器还有一个非常有用的用途是:我们可以用它来声明对test构件的依赖,比如,我们在一个核心模块的src/test/java中声明了一些基础类,然后我们发现这些测试基础类对于很多其它模块的测试类都有用。没有分类器,我们是没有办法去依赖src/test/java中的内容的,因为这些内容不会被打包到主构件中,它们单独的被打包成一个模式为<artifactId>-<version>-test.jar的文件。

我们可以使用分类器来依赖这样的test构件:理解了分类器,那么可供依赖的资源就变得更加丰富。

<dependency><groupId>org.myorg.myapp</groupId><artifactId>core</artifactId><version>${project.version}</version><classifier>test</classifier>
</dependency>

 依赖管理(dependencyManagement)

实际的项目中,你会有一大把的Maven模块,而且你往往发现这些模块有很多依赖是完全项目的,A模块有个对spring的依赖,B模块也有,它们的依赖配置一模一样,同样的groupId, artifactId, version,或者还有exclusions, classifer。

细心的分会发现这是一种重复,重复就意味着潜在的问题,Maven提供的dependencyManagement就是用来消除这种重复的。

正确的做法是:

1. 在父模块中使用dependencyManagement配置依赖

2. 在子模块中使用dependencies添加依赖

dependencyManagement实际上不会真正引入任何依赖,dependencies才会。但是,当父模块中配置了某个依赖之后,子模块只需使用简单groupId和artifactId就能自动继承相应的父模块依赖配置。

依赖配置越复杂,依赖管理所起到的作用就越大,它不仅能够帮助你简化配置,它还能够帮你巩固依赖配置,也就是说,在整个项目中,对于某个构件(如mysql)的依赖配置只有一种,这样就能避免引入不同版本的依赖,避免依赖冲突。

示例:

父模块:

<project><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook</groupId><artifactId>a-parent</artifactId><version>1.0.0</version>...<dependencyManagement><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.2</version></dependency>...<dependencies></dependencyManagement>

子模块:

<project><modelVersion>4.0.0</modelVersion><parent><groupId>org.sonatype.mavenbook</groupId><artifactId>a-parent</artifactId><version>1.0.0</version></parent><artifactId>project-a</artifactId>...<dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency></dependencies>
</project>

传递依赖 & 依赖冲突

依赖冲突时,Maven采用“最近获胜策略(nearest wins strategy)”。

如果一个项目最终依赖于相同artifact的多个版本,在依赖树中离项目最近的那个版本将被使用。

示例:

github源代码:https://github.com/davenkin/maven-dependency-conflict-demo

resolve-web项目的依赖关系如下:

根据Maven的transitive依赖机制,resolve-web将同时依赖于project-common的1.0和2.0版本,这就造成了依赖冲突。而根据最近获胜策略,Maven将选择project-common的1.0版本作为最终的依赖。

 在resolve-web中执行"mvn dependency:tree -Dverbose"可以看到resolve-web的依赖关系:

[INFO] resolve-web:resolve-web:war:1.0-SNAPSHOT[INFO] +- junit:junit:jar:3.8.1:test[INFO] +- project-B:project-B:jar:1.0:compile[INFO] |  \- project-C:project-C:jar:1.0:compile[INFO] |     \- (project-common:project-commmon:jar:2.0:compile - omitted for conflict with 1.0)[INFO] +- project-A:project-A:jar:1.0:compile[INFO] |  \- project-common:project-commmon:jar:1.0:compile[INFO] \- javax.servlet:servlet-api:jar:2.4:provided

由上可知,project-common:project-commmon:jar:2.0被忽略掉了。此时在resolve-web的war包中将只包含project-common的1.0版本,于是问题来了。由于project-common的1.0版本中不包含sayGoodBye()方法,而该方法正是project-C所需要的,所以运行时将出现“NoSuchMethodError”。

 

对于这种有依赖冲突所导致的问题,我们有两种解决方法。

方法1:显式加入对project-common 2.0版本的依赖。先前的2.0版本不是离resolve-web远了点吗,那我们就直接将它作为resolve-web的依赖,这不就比1.0版本离resolve-web还近吗?在resove-web的pom.xml文件中直接加上对project-common 2.0 的依赖:

<dependency>       <groupId>project-common</groupId>      <artifactId>project-commmon</artifactId>  <version>2.0</version>   
</dependency>  

方法2:resolve-web对project-A的dependency声明中,将project-common排除掉。在resolve-web的pom.xml文件中修改对project-A的dependency声明:

<dependency>  <groupId>project-A</groupId>  <artifactId>project-A</artifactId>  <version>1.0</version>  <exclusions>  <exclusion>  <groupId>project-common</groupId>  <artifactId>project-commmon</artifactId>  </exclusion>  </exclusions>  
</dependency>

方法3:另外,我们还可以在project-A中将对project-common的依赖声明为optional,optional即表示非transitive,此时当在resolve-web中引用project-A时,Maven并不会将project-common作为transitive依赖自动加入,除非有别的项目(比如project-B)声明了对project-common的transitive依赖或者我们在resolve-web中显式声明对project-common的依赖(方法一)。

 Profile

Maven的Profile用于在不同的环境下应用不同的配置。一套配置即称为一个Profile。

 Maven提供了Activation机制来激活某个Profile,它既允许自动激活(即在某些条件满足时自动使某个Profile生效),也可以手动激活。

Profile可配置“<activeByDefault>true</activeByDefault>”,表明该Profile默认即是生效的。

<profiles><profile><id>apple</id><activation><activeByDefault>true</activeByDefault></activation><properties><fruit>APPLE</fruit></properties></profile><profile><id>banana</id><properties><fruit>BANANA</fruit></properties></profile></profiles>

为了打印出fruit这个属性,我们再向pom.xml中添加一个maven-antrun-plugin插件,我们可以通过该插件的echo任务来打印属性信息。我们将该打印配置在Maven的initialize阶段(任何阶段都可以):

<plugin><artifactId>maven-antrun-plugin</artifactId><executions><execution><phase>initialize</phase><goals><goal>run</goal></goals><configuration><tasks><echo>Fruit:${fruit}</echo></tasks></configuration></execution></executions></plugin>

配置完成之后,执行:“mvn initialize”,由于id为apple的Profile默认生效,此时将在终端输出“APPLE”字样:

......[INFO] Executing tasks[echo] Fruit:APPLE[INFO] Executed tasks......

如果要使用id为banana的Profile,我们可以显式地指定使用该Profile:"mvn initialize -Pbanana"

私服(Repository)

 Maven提高篇系列之(三)——使用自己的Repository(Nexus)

Plugin

Maven就其本身来说只是提供一个执行环境,它并不知道需要在项目上完成什么操作,真正操作项目的是插件(plugin)。

比如编译Java有Compiler插件,打包有Jar插件等。

所以要让Maven完成各种各样的任务,我们需要配置不同的插件,甚至自己编写插件。 

 

参考资料:

Maven提高篇系列之(一)——多模块 vs 继承

Maven提高篇系列之(三)——使用自己的Repository(Nexus)

Maven提高篇系列之(四)——使用Profile

Maven提高篇系列之(五)——处理依赖冲突

Maven提高篇系列之(六)——编写自己的Plugin

 Maven依赖中的scope详解

Maven最佳实践:管理依赖

转载于:https://www.cnblogs.com/ken-jl/p/8610775.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/571037.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

企业案例(二):增量恢复案例

1、环境准备 条件: 1.具备全量备份&#xff08;mysqldump&#xff09;。 2.除全量备份以外&#xff0c;还有全量备份之后产生的的所有binlog增量日志。 1.1、建立数据库和表 CREATE DATABASE dadong; USE dadong; CREATE TABLE test (id int(4) NOT NULL AUTO_INCREMENT,name c…

gdc服务器维修公司,gdc服务器阵列架坏了

gdc服务器阵列架坏了 内容精选换一换本章节指导用户批量创建4块云硬盘&#xff0c;并挂载至云服务器。进入云硬盘页面。关于创建云硬盘的详细操作&#xff0c;请参见“云硬盘用户指南”。本示例为批量创建4块云硬盘&#xff0c;具体参数如图1所示。云硬盘规格支持任意用户(root…

手机文件传云服务器,手机云服务器传文件

手机云服务器传文件 内容精选换一换华为云帮助中心&#xff0c;为用户提供产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题、视频帮助等技术文档&#xff0c;帮助您快速上手使用华为云服务。如果私钥文件丢失了&#xff0c;可以为服务器替换新的密钥对&a…

Experimental-work1

软件测试第一次实验报告 3015207191 软件工程3班 林家乐 一、安装Junit&#xff0c;Hamcrest和Eclemma: 1.安装Junit 下载junit.jar&#xff0c;在项目里添加junit.jar并build一下即可。使用时&#xff0c;在项目内包含Junit4的库&#xff0c;其中包含junit.jar即…

c 开发服务器的性能,高性能网络编程(三):下一个10年,是时候考虑C10M并发问题了...

1、前言在本系列文章的上篇中我们回顾了过云的10年里&#xff0c;高性能网络编程领域著名的C10K问题及其成功的解决方案(上篇请见&#xff1a;《高性能网络编程(二)&#xff1a;上一个10年&#xff0c;著名的C10K并发连接问题》)。本文将讨论单机服务器实现C10M(即单机千万并发…

无盘服务器磁盘缓存,云更新无盘服务器磁盘缓存器

云更新无盘服务器磁盘缓存器 内容精选换一换Linux操作系统下&#xff0c;用户由于误操作卸载弹性云服务器上的Tools&#xff0c;会对非PVOPS系统的磁盘和网卡产生影响&#xff0c;导致系统无法发现数据盘。此时&#xff0c;用户可通过新建一个弹性云服务器&#xff0c;将原数据…

云财经服务器维护,云财经服务器维护

云财经服务器维护 内容精选换一换云耀云服务器适用于对CPU、内存、硬盘空间和带宽无特殊要求&#xff0c;服务一般只需要部署在一台或少量的服务器上&#xff0c;一次投入成本少&#xff0c;后期维护成本低的场景。例如网站开发&#xff0c;Web应用。推荐使用云耀云服务器&…

服务器lsass系统错误,Win7系统提示Lsass.exe系统错误的原因及解决

最近很多使用Win7系统用户反馈说开机提示lsass.exe系统错误&#xff0c;也不知道怎么回事?lsass.exe是什么进程&#xff0c;可能是病毒吗?带着这些疑问&#xff0c;下面小编告诉大家Win7系统提示Lsass.exe系统错误的原因及解决方法&#xff0c;希望本教程内容可以帮助到大家。…

相对路径 web

http://www.jb51.net/web/21806.html 相对路径-以引用文件之网页所在位置为参考基础&#xff0c;而建立出的目录路径。因此&#xff0c;当保存于不同目录的网页引用同一个文件时&#xff0c;所使用的路径将不相同&#xff0c;故称之为相对。   绝对路径-以Web站点根目录为参考…

NFS服务端安装与配置

一、NFS介绍 NFS数据传输基于PRC协议&#xff0c;用于在网络上共享存储。 二、服务端配置NFS 1、服务端安装包 nfs-utils和rpcbind,使用yum安装nfs-utils时会一起安装rpcbind. yum install -y nfs-utils 2、创建配置文件 vim /etc/exports&#xff0c;写入以下内容&#xff1a…

【前端统计图】echarts实现单条折线图

五分钟上手&#xff1a;图片.png<!DOCTYPE html> <html><head><meta charset"utf-8"><title>五分钟上手之折线图</title><!-- 引入 echarts.js --><script src"js/echarts.min.js"></script><sc…

JAVA高并发的三种实现

提到锁&#xff0c;大家肯定想到的是sychronized关键字。是用它可以解决一切并发问题&#xff0c;但是&#xff0c;对于系统吞吐量要求更高的话&#xff0c;我们这提供几个小技巧。帮助大家减小锁颗粒度&#xff0c;提高并发能力。 初级技巧-乐观锁 乐观锁使用的场景是&#…

进程同步(multiprocess.Lock、multiprocess.Semaphore、multiprocess.Event) day38

进程同步(multiprocess.Lock、multiprocess.Semaphore、multiprocess.Event) 锁 —— multiprocess.Lock 通过刚刚的学习&#xff0c;我们千方百计实现了程序的异步&#xff0c;让多个任务可以同时在几个进程中并发处理&#xff0c;他们之间的运行没有顺序&#xff0c;一旦开启…

Python杨辉三角

杨辉三角&#xff0c;是二项式系数在三角形中的一种几何排列&#xff0c;在中国南宋数学家杨辉1261年所著的《详解九章算法》一书中出现。在欧洲&#xff0c;帕斯卡&#xff08;1623----1662&#xff09;在1654年发现这一规律&#xff0c;所以这个表又叫做帕斯卡三角形。帕斯卡…

PHP 数据库连接池实现(转)

转自https://blog.csdn.net/Marksinoberg/article/details/53857511 摘要xml 读取配置文件 简易方式常规方式PHP解析XML 配置文件解析数据库连接池测试 申请过多时拒绝请求已满后拒绝放入总结此文着实无聊&#xff0c;不要浪费时间往下看啦 摘要 之前总是以脚本面向过程的方式…

批量处理JDBC语句提高处理速度

当需要成批插入或者更新记录时。可以采用Java的批量更新机制&#xff0c;这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率JDBC的批量处理语句包括下面两个方法&#xff1a;– addBatch(String)&#xff1a;添加需要批量处理的SQL语句或…

TCP close_wait内幕

最近调试时遇到一个tcp连接 一发交易server端就从es连接状态变成close_wait状态的问题。 tcp连接需要三次握手&#xff0c;而被动关闭连接则是需要四次握手的&#xff0c;不能说单方面关闭连接就是成功关闭连接了。 首先我们要搞清楚close_wait出现的机制和原理。 close_wai…

p-value

p-value p-value翻译为假定值&#xff0c;假设几率。我们在生物信息中通常使用p值方法&#xff08;P-Value, Probability, Pr&#xff09;来做检验。那么p-value是什么呢&#xff1f;其实P-value就是一种概率&#xff0c;表示在原假设为真的前提下出现观察样本以及更极端情况的…

面向对象初调用:foolish 电梯

本周我们完成的任务是傻瓜电梯的调度&#xff0c;对于那十分十分详细的指导书&#xff0c;我感觉想要说明白题目要求&#xff0c;是做不到的&#xff0c;所以就把指导书贴出来给大家看了&#xff0c;&#xff0c;由于在下还不会网页制作&#xff0c;只能通过百度网盘了&#xf…

Maven学习之(一)基本配置

安装maven还几次没成功&#xff0c;不过最后还是查资料成功了&#xff0c;所以记录一下。 1.安装JDK&#xff0c;比较简单&#xff0c;就不说明了。 2.配置java的环境变量 JAVA_HOME &#xff08;最开始因为java配置成C:\Program Files (x86)\Java\jdk1.8.0_144\bin 所以出问题…