一个大型的软件应用通常包含多个模块,并且通常的场景是多个团队开发同一应用的不同模块。举个例子,设想一个团队开发应用的前端,项目为 app-ui(app- ui.jar:1.0) ,而另一个团队开发应用的后台,使用的项目是 data-service(data-service.jar:1.0) 。
现在可能出现的情况是开发 data-service 的团队正在进行快节奏的bug修复或者项目改进,并且他们几乎每隔一天就要发布库到远程仓库。
现在如果 data-service 团队每隔一天上传一个新版本,那么将会出现下面的问题:
为了解决这种情况, 快照(SNAPSHOT) 的概念派上了用场。
快照 (SNAPSHOT)** 是一种特殊的版本,指定了某个当前的开发进度的副本。不同于常规的版本,Maven每次构建都会在远程仓库中检查新的快照** 。
现在 data-service 团队会每次发布更新代码的快照到仓库中,比如说 data-service:1.0-SNAPSHOT 来替代旧的快照jar包。
注意:每次更新jar包时,版本好不变,且后缀必须带上-SNAPSHOT。
版本(Version) 的情况下,如果Maven以前下载过指定的版本文件,比如说 data-service:1.0 , Maven 将不会再从仓库下载新的可用的 1.0 文件。若要下载更新的代码, data-service 的版本需要升到 1.1 。
快照(Snapshot) 的情况下,每次 app-ui 团队构建他们的项目时, Maven 将自动获取最新的快照( data- service:1.0-SNAPSHOT )。
备注: 版本(Version) 存放在Release发布仓库。 快照(Snapshot) 存放在Snapshot快照仓库。
注意: 版本(Version) 的概念,只要不带有 -SNAPSHOT 的关键字时,都会认为这是一个在 Release发布仓库 的jar包。其中在 Release发布仓库 的jar包命名除了具体的版本号之后还可以带上比如:1.0-Release、1.0-rc1等等的字样。
Maven 中的仓库分为两种, Snapshot快照仓库 和 Release发布仓库 。 Snapshot快照仓库 用于保存开发过程中的不稳定版本, Release正式仓库 则是用来保存稳定的发行版本。定义一个组件/模块为 快照版本 ,只需要在 pom.xml 文件中在该模块的版本号后加上 -SNAPSHOT 即可(注意这里必须是大写),如下所示:
<dependency> <groupId>com.jsoft.test</groupId> <artifactId>testcommon</artifactId> <version>0.1-SNAPSHOT</version> <packaging>jar</packaging> </dependency>
Maven 会根据模块的版本号( pom.xml 文件中的 version) 中是否带有 -SNAPSHOT 来判断是 快照版本 还是 正式版本 。如果是 快照版本 ,那么在 mvn deploy 时会自动发布到 快照版本库 中,而使用 快照版本 的模块,在 不更改版本号 的情况下,直接编译打包时, Maven 会 自动从镜像服务器上下载最新的快照版本 。如果是 正式发布版本 ,那么在 mvn deploy 时会自动发布到 正式版本库 中,而使用 正式版本 的模块,在 不更改版本号 的情况下,编译打包时如果本地已经存在该版本的模块则 不会主动去镜像服务器上下载 。
所以,我们在开发阶段,可以将公用库的版本设置为快照版本,而被依赖组件则引用快照版本进行开发,在公用库的快照版本更新后,我们也不需要修改 pom.xml 文件提示版本号来下载新的版本,直接 Maven 执行相关编译、打包命令即可重新下载最新的快照库了,从而也方便了我们进行开发。
虽然, 快照 的情况下, Maven 在日常工作中会自动获取最新的快照,你也可以在任何 Maven 命令中使用 -U参数 强制 Maven 下载最新的快照构建。命令如下:
mvn clean package -U
SNAPSHOT版本的依赖因为存在变更传达的透明性的优势而被赏识,甚至被“溺爱”,有很多团队索性直接使用SNAPSHOT到生产环境中,这样对于变更直接生效,很方便。但是作为技术人员的我们其实应该很严谨地看待变更传达的透明性,变更就意味着风险,透明性更是把风险彻底隐藏了起来,生产环境中存在这样的现象更是心惊胆战。例如:A-->B.1.0.3-SNAPSHOT,B对一个A使用的功能实现进行了调整,直接发布到仓库,A重新构建或许就会失败,更糟糕的是构建成功,运行时异常。这个时候A甚至完全没有代码变更就突然失败了,会带来更多的困惑。
这也是maven经常遭人诟病的一个因素,对于同一份代码,构建结果却不具备确定性,让很多人沮丧。当然这个不完全是因为依赖的问题,也有maven插件的问题,maven之前的版本寻找插件策略的方式也存在不确定性,maven在版本2的时候,会去寻找最新的插件版本(如果没配置的话)来执行构建,经常会找到SNAPSHOT版本的插件,所以依赖了一个不稳定的插件来执行构建,不确定性就大大增加。不过maven在3版本就改变了这个策略,会寻找最新稳定版的插件来执行构建,使得构建具备了确定性,稳定性也好多了。说明maven本身也在SNAPSHOT的问题上狠狠摔了一跤。
归根到底,这些问题的根源就是SNAPSHOT是变化的,是不稳定的,而应用(软件)依赖于变化并且不稳定的SNAPSHOT的依赖会导致自身也在变化和不稳定中,这是稳定性的一个大忌,依赖不稳定的服务或者依赖,上述的maven2的问题就是一个典型反例。
那么什么时候使用SNAPSHOT?什么时候使用RELEASE?这个可以从他们各自的特性上来看,SNAPSHOT版本的库是一直在变化的,或者说随时都会变化的,这样虽然可以获取到最新的特性,但是也存在不稳定因素,依赖一个不稳定的模块或者库会让模块自身也变得不稳定,尤其是自身对被依赖模块的变化超出掌控的情况。即使可以掌控被依赖模块的变化,也会带来不稳定的因素,因为每次变更都有引入bug的可能性。如果这么说,那么我们是不是要摒弃SANPSHOT了呢?答案肯定是否定的。
想象下,什么情况下,模块会一直变化或者变化比较剧烈?开发新特性的时候,所以对于团队之间协同开发的时候,模块之间出现依赖,变化会非常剧烈,如模块A依赖模块B,模块A必然需要最方便地获取模块B的特性,在开发期间,方便性比稳定性更重要。可以反证下,假设模块B使用RELEASE版本1.0.0,模块A依赖1.0.0,现在模块A出现了bug,需要修复下,那么A就要提供一个版本号1.0.1,这样所有依赖A模块都需要更新版本号,因为开发期间这种事情是如此多,所以会带来巨变。反观SNAPSHOT方案,如果模块B的版本是1.0.0-SNAPSHOT,模块A完全不需要修改版本号即可获取模块B的新特性。当开发进入预发布阶段,为了生产环境的稳定性,依赖应该是RELEASE版本,因为此时SNAPSHOT版本的模块自动获取新特性的特点恰恰会造成生产环境的不稳定性,生产环境上,稳定性重于一切。
现在已经很明确了,在开发期间,活跃模块的版本号使用SNAPSHOT,在生产期间,依赖RELEASE版本模块。貌似,我们找到了银弹,不过这个只是理想状态,即所有的模块的版本都在自己的掌控或者间接掌控下,只有这样你才能影响对应模块的版本号。往往是理想很丰满,现实却很骨感,如果你依赖的一个模块只有SNAPSHOT版本,并且该模块也很活跃,最无助的是模块的维护人不理会你的请求,那么是否就没辙了,只能把应用构建在不稳定模块上呢?介绍一款maven插件——versions,这是一个非常强大的版本管理插件,其中有个对依赖版本加锁的特性——lock- snapshots,并且提供了参数可以控制锁定的依赖,就可以实现对特定的SNAPSHOT模块锁定版本,执行的命令如下:mvn versions:lock- snapshots -DincludesList="groupId:artifactId:type:classifier:version",执行这个命令之后,对应的版本号会变化,比如1.0.0-SNAPSHOT会变成1.0.0.20090327.172306-4,即完成了锁定,此时这个SNAPSHOT就变成了固定小版本的稳定版本,不会在变化了,也相当于正式版的功能了。当然以后也可以解锁,详细请看对应文档。
原文链接:https://blog.csdn.net/pange1991/article/details/86360229?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1