Maven学习使用三
本节主要学习maven的坐标,依赖,仓库
一、坐标
在平面图中我们通过(x,y)来确定其中的一个位置。在maven的世界中,任何一个构件都必须明确定义自己的坐标,一组maven坐标是通过一些元素定义的:groupId、artifactId、version、packaging、classifier。
groupId:定义当前maven项目隶属的实际项目。首先,maven项目和实际项目不一定是一对一的关系。比如SpringFramework这一实际项目,其对应的maven项目会有很多,比如spring-core、spring-context等。这是由于maven中模块的概念。因此一个实际项目大多会被划分成很多模块。
artifactId:定义实际项目中的一个maven项目(模块),推荐使用实际项目名称作为artifactId的前缀,好处是方便寻找实际构建。默认情况下,maven生成的构件,文件名会以artifactId作为开头,如spring-core-5.0.9.RELEASE.jar,使用实际项目名称作为前缀之后,方便从一个lib文件夹中找到某个项目的一组构件。比如有5个项目,每个项目都有一个core模块,如果没有前缀,我们会看到多个core-x.x.jar这样的文件。
version:该元素定义maven项目当前所处的版本。
packaging:定义maven项目的打包方式。
classifier:定义构件输出的一些附属构件。附属构件与主构件对应,如主构件是spring-core-5.0.9.RELEASE.jar,该项目还可能会通过使用一些插件生成如spring-aop-5.0.9.RELEASE-sources.jar这样的附属构件,其中包含了源代码。这时候sources就是这个附属构件的classifier。这样,附属构件也就有了自己唯一的坐标。
上述5个元素中,groupId、artifactId、version是必须定义的,packaging是可选的(默认是jar包),而classifier是不能直接定义的。
同时,项目构件的文件名是与坐标相对应的,一般规则为artifactId-version[-classifier].packaging,[-classifier]可选。还有就是,packaging并非一定与构件扩展名对应,比如packaging为maven-plugin的构件扩展名为jar。
二、依赖
1、依赖的配置
一个依赖声明可以包含如下元素:
<project>
...
<dependencies>
<dependency>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<type>...</type>
<scope>...</scope>
<optional>...</optional>
<exclusions>
<exclusion>
...
</exclusion>
...
</exclusions>
</dependency>
...
</dependencies>
...
<project>
根元素project下的dependencies可以包含一个或者多个dependency元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:
groupId、artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,maven根据基本坐标才能找到需要的依赖。
type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,默认值为jar。
scope:依赖的范围。
optional:标记依赖是否可选。
exclusions:用来排除传递性依赖。
大部分情况下声明依赖只包含基本坐标。
2、依赖的范围
maven有三种classpath(编译classpath、测试classpath、运行classpath),依赖范围就是用来控制依赖于三种classpath的关系,maven有以下几种依赖范围:
compile:编译依赖范围。如果没有用scope指定,就会默认使用该依赖范围。对编译、测试、运行三种classpath都有效。如spring-core,在编译、测试、运行都要用到该依赖。
test:测试依赖范围。只对测试classpath有效,在编译主代码或者运行项目时都无法使用此类依赖。如JUnit,只在编译测试代码运行测试代码才需要。
provided:以提供依赖范围。对于编译和测试classpath有效,但是运行时无效。如servlet-api,编译和测试时需要该依赖,但是运行时,由于容器已经提供,就不需要重入引入一遍。
runtime:运行时依赖范围。对测试和运行classpath有效,但在编译主代码无效。如jdbc驱动,项目主代码的编译只需要jdk提供的jdbc接口,只有在执行测试或运行项目的时才需要实现jdbc驱动。
system:系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显示地指定依赖文件的路径。(一般不用)
依赖范围与classpath的关系:
3、依赖的传递性
传递依赖:如果我们的项目引用了一个Jar包,而该Jar包又引用了其他Jar包,那么在默认情况下项目编译时,Maven会把直接引用和间接引用的Jar包都下载到本地。
然而依赖的传递也不是无休止的,假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围,如下图所示:最左边一行表示第一直接依赖范围,最上面一行表示第二直接依赖范围,中间交叉的就是传递性依赖范围。
可以发现规律,当第二直接依赖范围是compile时,传递性依赖范围于第一直接依赖的范围一致;当第二直接依赖范围是test时,依赖不会传递;当第二直接依赖是provided时,只传递第一直接依赖也为provided的依赖,且传递性依赖范围的是provided,当第二直接依赖范围时rutime时,传递性依赖范围于第一直接依赖范围一致,但compile例外,此时传递依赖范围为runtime。
4、依赖调解
maven的传递性依赖机制,一方面大大简化了依赖的声明,另一方面,大部分情况下我们只需要关心项目的直接依赖是什么,而不用考虑直接依赖会引入什么传递性依赖。但有时候,传递以来造成问题的时候,我们就需要知道传递性依赖是从那条依赖路劲引入的。
路径优先原则:比如项目A有如下依赖关系:A->B->C->X(1.0),A->D->X(2.0),因为X(1.0)的路径长度为3,而X(2.0)的路径为2,因此X(2.0)会被解析使用。
第一声明优先原则:如果依赖路径长度一样,那么谁在POM中的顺序靠前谁被解析使用。
5、排除依赖
如:项目A依赖于项目B,但是,不想引入传递性依赖C,而是自己显示地声明对于C1.0.0版本的依赖。代码中使用exclusions元素声明排除依赖,exclusions可以包含一个或者多个exclusion子元素,因此可以排除一个或者多个传递性依赖。
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.caofanqi.stumvn</groupId>
<artifactId>project-a</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>com.caofanqi.stumvn</groupId>
<artifactId>project-b</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>com.caofanqi.stumvn</groupId>
<artifactId>project-c</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.caofanqi.stumvn</groupId>
<artifactId>project-c</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
6、归类依赖
为了统一管理依赖的版本,应该把版本归类,定义常量,如下:
<properties>
<junit.version>4.10</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
7、优化依赖
mvn dependency:list 查看当前项目已经解析的依赖
mvn dependency:tree 查看当前项目的依赖树
mvn dependency:analyze 分析依赖。Unused undeclared dependencies ,指项目中使用到的,但是没有显示声明的依赖,这种依赖存在潜在的风险,所以显示声明任何项目中直接用到的依赖。
Unused declared dependencies ,指项目中未使用的,但是显示声明的依赖,对于这样的依赖,我们不应该简单的删除其声明,应该仔细分析,由于mvn dependency:analyze只会分析编译主代码和测试代码需要用到的依赖,一些执行测试和运行时需要的依赖它就发现不了。所以需要好好分析,确定真没用后,在删除。
三、仓库
1、仓库分类
本地仓库: ~/.m2/repository/ 每一个用户也可以拥有一个本地仓库
远程仓库:
中央仓库:由于最原始的本地仓库时空的,Maven必须至少知道一个可用的远程仓库,才能在执行maven命令时下载需要的构件。中央仓库就是这样一个默认的远程仓库,mavne的安装文件自带了中央仓库的配置。在$M2_HOME/lib/maven-model-builder-3.6.0.jar/org/apache/maven/model/pom-4.0.0.xml 中可以看到如下配置:
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
私服:是一种特殊的远程仓库,它是架设在局域网内的仓库
2、远程仓库的配置
在很多情况下,默认的中央仓库无法满足项目的需求,可能项目需要的构件存在于另外一个远程仓库中(一般配置公司私服),如JBoss Maven仓库,这是可以在POM中配置该仓库如下:
<project>
...
<repositories>
<repository>
<id>jboss</id>
<name>JBoss Repository</name>
<url>http://repository.jboss.com/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<layout>default</layout>
</repository>
</repositories>
...
</project>
在repositories元素下,可以使用repository子元素声明一个或者多个远程仓库。任何一个仓库声明的id必须时唯一的,需要注意的是,maven自带的中央仓库使用的id为central,如果其他的仓库声明也使用该id,就会覆盖中央仓库的配置。url指向了仓库的地址,一般来说都是基于http协议,可以在浏览器中打开仓库地址浏览构件。
releases和snapshots它们是用来控制maven对于发布版本构件和快照版构件的下载。enabled为true,表示开启下载支持。本例表示maven只会从jboss仓库下载发布版构件,不会下载快照版构件。
layout元素值default表示仓库的布局是maven2和maven3的布局,而不是maven1。
对于releases和snapshots,还有另外两个子元素updatePolicy和checksumPolicy。元素updatePolicy用来配置maven从远程仓库检查更新的频率,默认的值是daily,表示maven每天检查一次。其他可用值包括:nerver-从不检查更新;always-每次构件都检查;interval:X-每隔X分钟检查一次更新。checksumPolicy用来配置maven检查检验和文件的策略。当构件被部署到maven仓库时,会同时部署对应的校验和文件。在下载的时候,maven会验证校验和文件,如果校验和验证失败,当checksumPolicy的值为默认的warn时,maven会在执行构建时输出警告信息,其他可用值为:fail-maven遇到校验和错误就让构件失败;ignore-使maven完全忽略校验和错误。
但是这样每个项目都要配置,我们也可以在setting.xml文件中配置,这样就不用在pom中配置了
<settings>
...
<profiles>
...
<profile>
<id>nexusProfile</id>
<repositories>
<repository>
<id>nexus</id>
<name>Nexus Repository</name>
<url>http://localhost:8081/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
<!--snapshots默认是关闭的,需要手动开启 -->
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</profile>
...
</profiles>
<activeProfiles>
<!--只有激活才生效 -->
<activeProfile>nexusProfile</activeProfile>
</activeProfiles>
</settings>
3、远程仓库的认证
大部分的远程仓库无需认证就可以访问,但是有时候处于安全方面考虑,需要提供认证信息才能访问一些远程仓库。如公司内部有一个maven仓库服务器,为每个项目都提供独立的maven仓库,为了防止非法访问,管理员为每个仓库提供了一组用户名和密码。这是就需要配置认证信息。
在setting.xml文件中
<servers>
<server>
<id>deploymentRepo</id>
<username>repouser</username>
<password>repopwd</password>
</server>
<servers>
这里的id是关键的元素,server元素的id必须与POM中需要认证的repository元素的id完全一致。
4、部署至远程仓库
在公司内部,需要将一些开发中生成的构件部署到仓库中,供其他团队使用。编辑pom.xml,配置distributionManagement如下:
<project>
<distributionManagement>
<repository>
<id>releases</id>
<name>Team Nexus Repository</name>
<url>http://192.168.0.100:9091/nexus/content/groups/releases/</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<name>Nexus Snapshot Repository</name>
<url>http://192.168.0.100:9091/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
</project>
distributionManagement包含了repository和snspshotRepository子元素,前者表示发布版本构件的仓库,后者表示快照版本的仓库。这两个元素都需要配置id,name,url。id为该远程仓库的唯一标识,name是为了方便人阅读,关键的url标识该仓库的地址。
往远程仓库部署构件的时候,往往需要认证。配置方式如上面的3
配置正确后,在命令行运行 mvn clean deploy ,maven就会将项目构建输出的构件部署到配置对应的远程仓库,如果项目当前版本是快照版本,则部署到快照版本仓库,反之亦然。
5、镜像
如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像。因为中央仓库在国外,下载速度比较慢,可以配置国内的镜像。比如阿里云镜像。
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
mirrorOf的值为星号,表示该配置是所有maven仓库的镜像,任何对于远程仓库的请求都会被转至配置的url。如果镜像仓库需要认证,则配置一个和该id相同的<server>即可。
mirrorOf的配置有很多:
<mirrorOf>central</mirrorOf>:任何对于中央仓库的请求都会转至该镜像。
<mirrorOf>*</mirrorOf>:匹配所有的远程仓库。
<mirrorOf>external:*</mirrorOf> : 匹配所有的远程仓库,使用localhost的除外,使用file://协议的除外。也就是所,匹配所有不再本机上的远程仓库。
<mirrorOf>repo1,repo2</mirrorOf> :匹配仓库repo1和repo2,使用逗号分隔多个远程仓库。
<mirrorOf>*,!repo1</mirrorOf> :匹配所有的远程仓库,repo1除外,使用感叹号将仓库从匹配中排除。
关于镜像的常见用法是结合私服。对于公司内部maven用户来说,使用一个私服地址就等于使用了所有需要的外部仓库,这可以将配置计中到私服,从而简化maven本身的配置。
需要注意的是,由于镜像仓库完全屏蔽了被镜像仓库,当镜像仓库不稳定或者停止服务时,maven将仍无法访问被镜像仓库,因而将无法下载构件。
--------------------------------------------------------------------------------------------------------------------------------------
参考:《Maven实战》