一、坐标
找到唯一包需要指定的元素
二、依赖传递
A依赖B,B依赖C,那么A引用B的时候,不需要再将C依赖添加进Pom.xml
三、排除依赖
传递性依赖会给项目隐式地引入很多依赖,这极大地简化了项目依赖的管理,但是有时候这种特性也会带来问题。比如有种情况:
当前项目依赖A,A由于某些原因依赖了另外一个类库的SNAPSHOT版本,那么这个SNAPSHOT就会成为当前项目的传递性依赖,二SNAPSHOT的不稳定性将直接影响到当前的项目,此时就需要排除该SNAPSHOT,并且在当前项目中声明该类库的某个正式发布的版本
排除依赖很简单,看一下写法:
<dependency>
<groupId>com.alibaba.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>3.2.7</version>
<exclusions>
<exclusion>
<groupId>apache-lang</groupId>
<artifactId>commons-lang</artifactId>
</exclusion>
</exclusions>
</dependency>
四、Maven仓库(repository )
Maven仓库可简单分成两类: 本地仓库与远程仓库. 当Maven根据坐标寻找构件时, 它会首先检索本地仓库, 如果本地存在则直接使用, 否则去远程仓库下载.
本地仓库: 默认地址为~/.m2/, 一个构件只有在本地仓库存在之后, 才能由Maven项目使用。
远程仓库: 远程仓库又可简单分成: 中央仓库和私服以及第三方库(第三方库比较常用,如阿里云仓库: http://maven.aliyun.com/nexus/content/groups/public ,中央仓库比较慢,我们一般情况下都会镜像到国内的库)。
中央仓库:http://repo1.maven.org/maven2/
由于原始的本地仓库是空的, Maven必须至少知道一个远程仓库才能在执行命令时下载需要的构件, 中央仓库就是这样一个默认的远程仓库,仓库ID:maven依赖之central.
一方库、二方库、三方库说明:
一方库:本工程中的各模块的相互依赖
二方库:公司内部的依赖库,一般指公司内部的其他项目发布的jar包
三方库:公司之外的开源库, 比如apache、ibm、google等发布的依赖
1、私服
私服是一种特殊的远程仓库, 它设在局域网内, 通过代理广域网上的远程仓库, 供局域网内的Maven用户使用.
当需要下载构件时, Maven客户端先向私服请求, 如果私服不存在该构件, 则从外部的远程仓库下载, 并缓存在私服上, 再为客户提供下载服务
将包上传到私服(发布)
1.1、配置发布源
<distributionManagement>
<snapshotRepository>
<id>${repository.debug.id}</id>
<name>${repository.debug.name}</name>
<url>${repository.debug.url}</url>
</snapshotRepository>
<repository>
<id>${repository.release.id}</id>
<name>${repository.release.name}</name>
<url>${repository.release.url}</url>
</repository>
</distributionManagement>
repository:即产出物或下载源的仓库
repository
表示发布版本构件的仓库,snapshotRepository
代表快照版本的仓库.id
为该远程仓库唯一标识,url
表示该仓库地址.- 配置正确后, 执行
$ mvn clean deploy
则可以将项目构建输出的构件部署到对应配置的远程仓库.
顺便记录下,hubble-sdk部署到私服执行命令:mvn clean deploy -Dmaven.test.skip=true
有时候加clean的时候会报错,可以直接杀掉java进程即可,参考:快速解决Maven工程运行报错Failed to clean project: Failed to delete_Jia_Li_z的博客-CSDN博客
1.2、配置权限
需要修改的conf/setting.xml(conf下的setting.xml是全局配置),增加权限配置:
<!-- 私服发布的用户名密码 -->
<servers>
<server>
<id>没搞明白这是干嘛的</id>
<username>用户名</username>
<password>密码</password>
</server>
<server>
<id>xxx</id>
<username>用户名</username>
<password>密码</password>
</server>
</servers>
2、仓库配置
当不使用仓库时,默认使用的仓库地址是:https://repo.maven.apache.org/maven2
如下不配置仓库地址:
项目执行Maven install时:
五、profile标签
1、介绍
maven中profile元素的作用意义和用法_brave_zhao的博客-CSDN博客_maven中profile
Profile能让你为一个特殊的环境自定义一个特殊的构建;profile使得不同环境间构建的可移植性成为可能。
Maven中的profile是一组可选的配置。有了profile,你就可以为不同的环境定制构建。profile可以在pom.xml中配置,并给定一个id。然后你就可以在运行Maven的时候使用的命令行标记告诉Maven运行特定profile中的目标。一个Profiles下面允许出现的元素:
如果指定线上环境:mvn clean package -Dmaven.test.skip=true -P product
-P:指定构建使用的运行环境配置(profile),通常每个项目(尤其是web项目)会在不同的环境下运行,如:开发环境、测试环境、生产环境等,不同的环境的配置是不完全相同的,使用Maven的profiefs可以方便的构建出适合不同运行环境的包,运行时使用的环境变量由-P参数指定。
<profiles>
<profile>
<id>test</id>
<activation>
<activeByDefault>true</activeByDefault> <!-- 默认是使用这个profile构建 -->
</activation>
<properties>
<profile.env.name>config_dev.properties</profile.env.name>
</properties>
</profile>
<profile>
<id>product</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<properties>
<profile.env.name>config_pro.properties</profile.env.name>
</properties>
</profile>
</profiles>
2、profile的定义位置
我们可以有多个地方定义profile。定义的地方不同,它的作用范围也不同。
- 针对于特定项目的profile配置我们可以定义在该项目的pom.xml中。
- 针对于特定用户的profile配置,我们可以在用户的settings.xml文件中定义profile。该文件在用户家目录下的“.m2”目录下。
- 全局的profile配置。全局的profile是定义在Maven安装目录下的“conf/settings.xml”文件中的。
六、mvn clean package执行时常用的参数
mvn clean package -U -pl xxx -P xxx -Dmaven.test.skip=true -e clean
运行mvn命令时常用的参数有,-B -e -U -pl -D -P,这些参数是Maven自身的,在命令行、持续集成环境都适用。
-B:使Maven在批处理模式下运行,避免需要人工参与交互而造成挂起,在持续集成等不希望人工参与的环境很有用。
-e:在运行maven出现异常时打印完整的异常栈,对问题的分析很有帮助
-U:强制Maven检查所有快照版(snapshot)依赖的更新,默认情况下Maven每天检查快照版(snapshot)依赖的是否更新,在实际开发中快照版的更新频率远高于这个频率,-U参数能保证每一次运行都是基于最新状态的,在持续集成环境这一点尤其重要。
-D:设置maven运行时的系统变量,和Java的-D参数类似,如:maven.repo.local、maven.test.skip
-P:激活指定的profile,指定构建使用的运行环境配置(profile),通常每个项目(尤其是web项目)会在不同的环境下运行,如:开发环境、测试环境、生产环境等,不同的环境的配置是不完全相同的,使用Maven的profiefs可以方便的构建出适合不同运行环境的包,运行时使用的环境变量由-P参数指定。
-pl:在一个项目有多个模块,而又不想同时构建所有模块儿时,通过-pl参数指定要构建的模块儿,个人觉得这个参数用的场景不多,在模块儿很多的情况下,更应该从模块儿的上进行拆分,减少模块儿间的依赖。
综上所述,建议的Maven构建命令:jar和pom工程:mvn clean install/deploy -B -e -U -P,web工程:mvn clean package -B -e -U -P
七、Maven打包
1、打包
mvn clean package依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)等7个阶段。
mvn clean install依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)、install等8个阶段。
mvn clean deploy依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)、install、deploy等9个阶段。
由上面的分析可知主要区别如下,
package命令完成了项目编译、单元测试、打包功能,但没有把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库
install命令完成了项目编译、单元测试、打包功能,同时把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库,但没有布署到远程maven私服仓库
deploy命令完成了项目编译、单元测试、打包功能,同时把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库。
2、构件发布到远程
本地Maven仓库的构件只能供当前用户使用,在分发到远程Maven仓库(通过命令:mvn clean deploy)之后,所有能访问该仓库的用户都能使用你的构件。我们需要配置POM的distributionManagement标签来指定Maven分发构件的位置
关于distributionManagement:
2.1、发布源配置
Maven区别对待release版本的构件和snapshot版本的构件,snapshot为开发过程中的版本,实时,但不稳定,release版本则比较稳定。Maven会根据你项目的版本来判断将构件分发到哪个仓库。 配置如下:
<distributionManagement>
<snapshotRepository>
<id>${repository.debug.id}</id>
<name>${repository.debug.name}</name>
<url>${repository.debug.url}</url>
</snapshotRepository>
<repository>
<id>${repository.release.id}</id>
<name>${repository.release.name}</name>
<url>${repository.release.url}</url>
</repository>
</distributionManagement>
2.2、权限配置
分发构件到远程仓库需要认证,如果你没有配置任何认证信息,你往往会得到401错误。这个时候,如下在settings.xml中配置认证信息:
<servers>
<server>
<id>maven-snapshot</id>
<username>xxx</username>
<password>xxx</password>
</server>
<server>
<id>maven-release</id>
<username>xxx</username>
<password>xxx</password>
</server>
</servers>
需要注意的是,settings.xml中server元素下id的值必须与POM中repository或snapshotRepository下id的值完全一致(精确到对哪个发布源的认证)。将认证信息放到settings下而非POM中,是因为POM往往是它人可见的,而settings.xml是本地的。 私服中的每个库(不同的库不同的id地址标识,如下所示,所以server的id代表的即仓库地址,亲测,修改了id之后,deploy的时候就报401错误,即无权限)都可以配单独的账号密码,更加细化权限的控制。
八、Maven快照版本和发布版本
在使用maven过程中,我们在开发阶段经常性的会有很多公共库处于不稳定状态,随时需要修改并发布,可能一天就要发布一次,遇到bug时,甚至一天要发布N次。我们知道,maven的依赖管理是基于版本管理的,对于发布状态的artifact,如果版本号相同,即使我们内部的镜像服务器上的组件比本地新,maven也不会主动下载的。如果我们在开发阶段都是基于正式发布版本来做依赖管理,那么遇到这个问题,就需要升级组件的版本号,可这样就明显不符合要求和实际情况了。但是,如果是基于快照版本,那么问题就自热而然的解决了,而maven已经为我们准备好了这一切。
maven中的仓库分为两种:
- snapshot快照仓库:不稳定
- release发布仓库:稳定
snapshot快照仓库用于保存开发过程中的不稳定版本,release正式仓库则是用来保存稳定的发行版本。定义一个组件/模块为快照版本,只需要在pom文件中在该模块的版本号后加上-SNAPSHOT即可(注意这里必须是大写),如下:
<groupId>cc.mzone</groupId>
<artifactId>m1</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>
maven2会根据模块的版本号(pom文件中的version)中是否带有-SNAPSHOT来判断是快照版本还是正式版本。如果是快照版本,那么在mvn deploy时会自动发布到快照版本库中,会覆盖老的快照版本,而在使用快照版本的模块,在不更改版本号的情况下,直接编译打包时,maven会自动从镜像服务器上下载最新的快照版本。如果是正式发布版本,那么在mvn deploy时会自动发布到正式版本库中,而使用正式版本的模块,在不更改版本号的情况下,编译打包时如果本地已经存在该版本的模块则不会主动去镜像服务器上下载。
九、repository标签
repository:即Maven中的仓库。
<repositories>
<!-- 指定中间件jrog仓库 -->
<repository>
<snapshots/>
<id>iqiyi-maven-middleware</id>
<name>iqiyi-maven-middleware</name>
<url>http://jfrog.cloud.qiyi.domain/iqiyi-maven-cloudservice</url>
</repository>
</repositories>
Maven中要配置库,可以有多种方式。
- 当前项目生效:在项目中的pom.xml文件中,通过<repositories>配置库,这样配置的库仅适用于当前项目。
- 特定运行环境:也可以通过<profiles>中的<repositories>配置在特定环境下的特殊库,这可以在项目的pom.xml文件中实现,也可以在Maven的settings.xml中实现。
- 此外,一个项目发布后,其往往要被部署到一个库中,作为库的构件以供其他项目引用。通过<distributionManagement>中的<repositories>配置要发布的库。
十一、Maven仓库优先级
十二、Maven镜像(<mirrors>)
1、介绍
如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像。换句话说,任何一个可以从仓库Y获得的构件,都能够从它的镜像中获取。
参考:https://my.oschina.net/sunchp/blog/100634/
2、为什么要镜像
目的是提升访问速度。
没有配置mirror :
配置了mirror:
repository与mirror的理解:
1.repository是指在局域网内部搭建的repository,它跟central repository, jboss repository等的区别仅仅在于其URL是一个内部网址。
2.mirror则相当于一个代理,它会拦截去指定的远程repository下载构件的请求,然后从自己这里找出构件回送给客户端。配置mirror的目的一般是出于网速考虑。
<mirrors>
<mirror>
<id>xxx</id>
<mirrorOf>*</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>xxxurl</url>
</mirror>
<mirror>
<id>xxx</id>
<mirrorOf>*</mirrorOf>
<name>jcenter</name>
<url>xxxurl</url>
</mirror>
</mirrors>
高级的镜像配置:
1.<mirrorOf>*</mirrorOf>
匹配所有仓库请求,即将所有的仓库请求都转到该镜像上
2.<mirrorOf>external:*</mirrorOf>
匹配所有远程仓库,使用localhost的除外,使用file://协议的除外。也就是说,匹配所有不在本机上的远程仓库。
3.<mirrorOf>repo1,repo2</mirrorOf>
将仓库repo1和repo2的请求转到该镜像上,使用逗号分隔多个远程仓库。
4.<mirrorOf>*,!repo1</miiroOf>
匹配所有远程仓库,repo1除外,使用感叹号将仓库从匹配中排除。
mirrors可以配置多个mirror:
每个mirror有id,name,url,mirrorOf属性,id是唯一标识一个mirror就不多说了,name貌似没多大用,相当于描述,url是官方的库地址,mirrorOf代表了一个镜像的替代位置,例如central就表示代替官方的中央库。
多个mirror的使用顺序:
我本以为镜像库是一个分库的概念,就是说当a.jar在第一个mirror中不存在的时候,maven会去第二个mirror中查询下载。但事实却不是这样,当第一个mirror中不存在a.jar的时候,并不会去第二个mirror中查找,甚至于,maven根本不会去其他的mirror地址查询。
后来终于知道,maven的mirror是镜像,而不是“分库”,只有当前一个mirror无法连接的时候,才会去找后一个,类似于备份和容灾。
还有,mirror也不是按settings.xml中写的那样的顺序来查询的。
所谓的第一个并不一定是最上面的那个。
当有id为B,A,C的顺序的mirror在mirrors节点中,maven会根据字母排序来指定第一个,所以不管怎么排列,一定会找到A这个mirror来进行查找,当A无法连接,出现意外的情况下,才会去B查询。
十三、Maven 多模块开发
好处:代码重用,如service模块,新起一个模块,直接引入service模块即可。
见另一篇博客:Maven学习记录之多模块开发 - 巍巍的个人页面 - OSCHINA - 中文开源技术交流社区
参考资料:
打包:https://blog.csdn.net/zhaojianting/article/details/80324533
原理:https://blog.csdn.net/zjf280441589/article/details/53044308/
Maven实战:pom.xml与settings.xml:https://www.cnblogs.com/xrq730/p/5530069.html
十四、Maven依赖关系中Scope的作用
Maven scope作用_心飞扬向何方的博客-CSDN博客_maven scope作用
- compile:缺省值,适用于所有阶段,会随着项目一起发布。即打包的时候会把这个包打进去。
- runtime:runtime表示该依赖不会参与到项目的编译,但是会参与测试,运行周期。与compile相比,就是跳过了编译而已。如JDBC驱动,适用运行和测试阶段。
- test:scope为test表示依赖项目仅仅参与测试相关的工作,包括测试代码的编译,执行。比较典型的如junit。
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
-
- provided:意味着打包的时候可以不用包进去,比如lombok,例比如项目的使用方提供SDK包下引用的日志依赖、web 容器提供servlet相关依赖。事实上该依赖理论上可以参与编译,测试,运行等周期。相当于compile,但是在打包阶段做了exclude的动作。
- 注意:provided不会进行依赖传递,所以,hubble-sdk-api中不能有provided的scope
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope><!-- 注意:如果是公共依赖服务,千万不要加provided,这会导致依赖不会被专递,也就是说引用了公共服务的服务,不会引入provided的包 -->
</dependency>
- system:system 元素与 provided 元素类似,但是被依赖项不会从 maven 仓库中查找,而是从本地系统中获取,systemPath 元素用于制定本地系统中 jar 文件的路径。例如:
-
<dependency> <groupId>org.open</groupId> <artifactId>open-core</artifactId> <version>1.5</version> <scope>system</scope> <systemPath>${basedir}/WebContent/WEB-INF/lib/open-core.jar</systemPath> </dependency>
-
import: 导入的范围,它只在使用dependencyManagement中,表示从其他pom中导入dependecy的配置
maven在编译的时候,src/main/java下是不引用<scope>test</scope>的jar,而编译src/test/java下的测试这会引用<scope>test</scope>的jar
如下图所示:在src/main/java中报错了,但是在src/test/java下没有报错
十五、Linux安装Maven
下载maven并配置环境变量,maven就生效了
配置maven环境变量
vi /etc/profile
添加环境变量
export MAVEN_HOME=/var/local/apache-maven-3.5.2
export MAVEN_HOME
export PATH=$PATH:$MAVEN_HOME/bin
编辑之后记得使用source /etc/profile命令是改动生效。
参考: