Maven In Action - 坐标、依赖、仓库

坐标、依赖、仓库 - Maven In Action

  • 1. Maven坐标
  • 2. 依赖
    • 2.1 依赖的配置
    • 2.2 依赖范围
    • 2.3 传递性依赖和依赖范围
    • 2.4 依赖调解
  • 3. 仓库
    • 3.1 仓库的分类
    • 3.2 setting.xml示例
    • 3.3 快照版本
    • 3.4 从仓库解析依赖的机制
    • 3.5 仓库镜像
  • 参考

1. Maven坐标

Maven坐标为各种构建引入了秩序,任何一个构建都必须明确自己的坐标,而一组Maven坐标是通过一些元素定义的,他们是groupId, artifactId, version, packaging, classifer。

  • groupId: 定义当前Maven项目隶属的实际项目
    • 首先:Maven项目和实际项目不一定是一一对应的关系;
    • 其次:groupId不应该对应项目隶属的组织或公司;
    • 最后:groupId的表示方式和Java包名的表示方式类似,通常与域名反向一一对应。
  • artifactId: 定义实际项目中的一个Maven项目(模块),推荐的做法是使用实际项目名称作为artifactId的前缀
  • version: 定义Maven项目当前所处的版本
  • packaging: 定义Maven项目的打包方式
    • 首先: 打包方式通常与所生成构件的文件扩展名对应
    • 其次:打包方式会影响到构建的生命周期
    • 最后: 不定义packaging,默认使用jar
  • classifer: 帮助定义构件输出的一些附属构件

Example: pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>1.5.9.RELEASEversion>
        <relativePath/>
    parent>

    <groupId>com.tm.sbiagroupId>
    <artifactId>sbia-cart-webartifactId>
    <version>1.0-SNAPSHOTversion>
    <modelVersion>4.0.0modelVersion>

    <name>sbia-cart-webname>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
    dependencies>

    <distributionManagement>
        <repository>
            <id>toman.releasesid>
            <name>Toman maven2 repository-releasesname>
            <url>http://artifactory.toman.com/libs-releases-localurl>
        repository>
        <snapshotRepository>
            <id>toman.snapshotsid>
            <name>Toman maven2 repository-snapshotsname>
            <url>http://artifactory.toman.com/libs-snapshots-localurl>
        snapshotRepository>
    distributionManagement>
project>

2. 依赖

2.1 依赖的配置

根元素project下的dependencies可以包含一个或多个dependency元素,以声明一个或多个依赖项目。每个依赖都可以包含的元素有:

  • groupId, artifactId, version: 依赖的基本坐标
  • type: 依赖的类型,对应于坐标定义的packaging。大部分情况下该元素无需声明,默认为jar
  • scope: 依赖的范围
  • optional: 标记依赖是否可选
  • exclusions: 用来排除传递性依赖

2.2 依赖范围

依赖范围用来控制依赖与三种classpath(编译classpath, 测试classpath, 运行classpath)的关系

依赖范围(Scope) 编译classpath有效 测试classpath有效 运行classpath有效 例子
complie Y Y Y spring-core
test Y Junit
provided Y Y servlet-api
runtime Y Y JDBC驱动实现
system Y Y 本地的,Maven仓库之外的类库文件

2.3 传递性依赖和依赖范围

依赖范围不仅可以控制依赖和三种classpath的关系,还对传递性依赖产生影响
如下图,最左侧一列表示第一直接依赖范围,最上面一行标识第二直接依赖范围,中间交叉的单元格则表示传递性依赖范围

第一直接依赖范围(列)/ 第二直接依赖范围(行) compile test provided runtime
compile compile runtime
test test test
provided provided provided provided
runtime runtime runtime

规律:
当第二直接依赖依赖的范围是compile时,传递性依赖的范围与第一直接依赖的范围一致;
当第二直接依赖的范围是test时,依赖不会得以传递;
当第二直接依赖的范围是provided时,只传递第一直接依赖范围也为provided的依赖,且传递性依赖的范围同样为provided;
当第二直接依赖的范围是runtime时,传递性依赖的范围与第一直接依赖的范围一致,当compile例外,此时传递性依赖的范围为runtime。

假设有这样一个依赖关系,项目A依赖于项目B,项目B依赖于项目X和Y,B对于X和Y的依赖都是可选依赖:A–>B、B–>X(可选)、B–>Y(可选)。根据传递性依赖的定义,如果所有这三个依赖的范围都是compile,那么X、Y就是A的compile范围传递依赖。然而由于X、Y是可选依赖、依赖将不会得以传递。

为什么要使用可选依赖这一特征呢?可能项目B实现了两个特征,其中的特征一依赖于X,特征二依赖于Y,而且这两个特征是互斥的,用户不能同时使用两个特性。比如B是一个持久层隔离工具包支持多种数据库,包括MySQL、PostgreSQL等,在构建这个工具包的时候,需要这两种数据库的驱动程序,而在使用这个工具包的时候,只会依赖一个数据库。

理想情况下是不应该使用可选依赖的,使用可选依赖的原因是某一个项目实现了多个特性,而这是不符合单一职责原则的。

2.4 依赖调解

Maven依赖调解的第一原则:路径最近者优先;
A -> B -> C -> X(1.0)
A -> D -> X(2.0)
依据第一原则 X的版本为2.0

Maven依赖调解的第二原则:第一声明者优先;
A -> B -> Y(1.0)
A -> C -> Y(2.0)
B的依赖声明在C之前
依据第二原则Y的版本为1.0

3. 仓库

得益于坐标机制,任何Maven项目使用任何一个构件的方式都是完全相同的。在此基础上,Maven可以在某个位置统一存储所有Maven项目共享的构件,这个统一的位置就是仓库。

任何一个构件都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径,这便是Maven的仓库布局方式。

3.1 仓库的分类

对于Maven来说仓库只分为两类:本地仓库和远程仓库。当Maven根据坐标寻找构件的时候,首先会查看本地仓库,如果本地仓库存在此构件,则直接使用;如果本地仓库不存在此构件,或者需要查看是否有更新的构件版本,Maven就会去远程仓库查找,发现需要的构件后,下载到本地仓库再使用。

中央仓库是Maven自带的远程仓库,包含了绝大部分开源的构件。在默认配置下,当本地仓库没有Maven需要的构件的时候,他就会尝试从中央仓库下载。

私服是另一种特殊的远程仓库,为了节省宽带和时间,应该在局域网内架设一个私有的仓库服务器,用其代理所有的外部远程仓库。内部的项目还能部署到私服上供其他项目使用。

3.2 setting.xml示例

对于release和snapshots来说,除了enabled, 他们还包含另外两个子元素updatePolicy和checksumPolicy。
updatePolicy用来配置Maven仓库从远程仓库检查更新的频率,可选值:

  • daily - 每天检查一次,默认值
  • never - 从不检查更新
  • always - 每次构件都检查更新
  • interval: X - 每隔X分钟检查一次更新。X为任意整数

元素checksumPolicy用来配置Maven检查检验和文件的策略。当构件被部署到Maven仓库时,会同时部署对应的校验和文件。checksumPolicy可选值:

  • warn: 执行构建时输出警告信息。默认值
  • fail: 遇到校验和错误就让构建失败
  • ignore: 使Maven完全忽略校验和错误

Maven自带的中央仓库使用的id为central,如果其他的仓库声明也使用该id,就会覆盖中央仓库的配置。


<settings
        xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"
        xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    
    <localRepository>/maven/repository/locallocalRepository>
    <interactiveMode>trueinteractiveMode>
    <offline>falseoffline>

    <profiles>
        <profile>
            <id>tomanRepositoryid>
            <repositories>
                <repository>
                    <snapshots>
                        <enabled>falseenabled>
                    snapshots>
                    
                    <id>centralid>
                    <name>libs-releasesname>
                    <url>http://artifactory.toman.com/libs-releasesurl>
                repository>
                <repository>
                    <snapshots/>
                    <id>snapshotsid>
                    <name>libs-snapshotsname>
                    <useUniqueVersions>falseuseUniqueVersions>
                    <url>http://artifactory.toman.com/libs-snapshotsurl>
                repository>
            repositories>
            <pluginRepositories>
                <pluginRepository>
                    <snapshots>
                        <enabled>falseenabled>
                    snapshots>
                    <id>centralid>
                    <name>plugins-releasesname>
                    <url>http://artifactory.toman.com/plugins-releasesurl>
                pluginRepository>
                <pluginRepository>
                    <snapshots/>
                    <id>snapshotsid>
                    <name>plugins-snapshotsname>
                    <useUniqueVersions>falseuseUniqueVersions>
                    <url>http://artifactory.toman.com/plugins-snapshotsurl>
                pluginRepository>
            pluginRepositories>
        profile>
    profiles>

    <servers>
        <server>
            
            <id>toman.releasesid>
            <username>tomanusername>
            <password>toman's passwordpassword>
        server>

        <server>
            <id>toman.snapshotsid>
            <username>tomanusername>
            <password>toman's passwordpassword>
        server>
    servers>

    <activeProfiles>
        <activeProfile>tomanRepositoryactiveProfile>
    activeProfiles>
settings>

3.3 快照版本

快照(SNAPSHOT)是一种特殊的版本,指定了某个当前的开发进度的副本。主要解决开发过程中频繁构建的问题,当项目经过完善的测试需要发布的时候,就应该将快照版本更改为发布版本

3.4 从仓库解析依赖的机制

当本地仓库没有依赖构件的时候,Maven会自动从远程仓库下载;当依赖版本为快照版本的时候,Maven会自动找到最新的快照。其背后的解析机制如下:

  1. 当依赖的范围为system的时候,Maven直接从本地文件系统解析构件;
  2. 根据依赖坐标计算仓库路径后,尝试从本地仓库寻找构件,如果发现相应的构件,则解析成功;
  3. 在本地仓库不存在相应构件的情况下,如果依赖的版本是显式的发布版本构件,则遍历所有的远程仓库,发现后下载并解析使用;
  4. 如果依赖的版本是RELEASE或LASTEST,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/maven-metadata.xml,将其与本地仓库对应的元数据合并后,计算出RELEASE或LASTEST的真实值,然后基于这个真实值检查本地和远程仓库,如步骤2,3;
  5. 如果依赖的版本是SNAPSHOT,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/version/maven-metadata.xml,将其与本地仓库对应的元数据合并后,得到最新快照版本的值,然后基于该值检查本地仓库,或者从远程仓库下载;
  6. 如果最后解析得到的构件版本是时间戳格式的快照,则复制其时间戳格式的文件至非时间戳格式,如SNAPSHOT,并使用该非时间戳格式的构件。

在依赖中使用LATEST和RELEASE是不推荐的做法。

3.5 仓库镜像

如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像。

参考

Maven实战(Maven In Action)
Maven快照机制(SNAPSHOT)

你可能感兴趣的:(Maven)