release and snapshot
Maven 中建立的依赖管理方式基本已成为Java语言依赖管理的事实标准,Maven 的替代者 Gradle 也基本沿用了 Maven的依赖管理机制。在 Maven 依赖管理中,唯一标识一个依赖项是由该依赖项的三个属性构成的,分别是 groupId、artifactId 以及 version。这三个属性可以唯一确定一个组件(Jar 包或者 War 包)。
其实在 Nexus 仓库中,一个仓库一般分为 public(Release) 仓和 SNAPSHOT 仓,前者存放正式版本,后者存放快照版本。
如果在项目配置文件中(无论是 build.gradle 还是 pom.xml)指定的版本号带有 '-SNAPSHOT' 后缀,比如版本号为 'Junit-4.10-SNAPSHOT',那么打出的包就是一个快照版本。
快照版本和正式版本的主要区别在于,本地获取这些依赖的机制有所不同。假设你依赖一个库的正式版本,构建的时候构建工具会先在本次仓库中查找是否已经有了这个依赖库,如果没有的话才会去远程仓库中去拉取。所以假设你发布了 Junit-4.10.jar 到了远程仓库,有一个项目依赖了这个库,它第一次构建的时候会把该库从远程仓库中下载到本地仓库缓存,以后再次构建都不会去访问远程仓库了。所以如果你修改了代码,向远程仓库中发布了新的软件包,但仍然叫 Junit-4.10.jar,那么依赖这个库的项目就无法得到最新更新。你只有在重新发布的时候升级版本,比如叫做 Junit-4.11.jar,然后通知依赖该库的项目组也修改依赖版本为 Junit-4.11,这样才能使用到你最新添加的功能。
这种方式在团队内部开发的时候会很坑。假设有两个小组负责维护两个组件,example-service 和 example-ui,其中example-ui 项目依赖于 example-service。而这两个项目每天都会构建多次,如果每次构建你都要升级 example-service 的版本,那么你会疯掉。这个时候 SNAPSHOT 版本就派上用场了。每天日常构建时你可以构建 example-service的 快照版本,比如example-service-1.0-SNAPSHOT.jar,而 example-ui 依赖该快照版本。每次 example-ui 构建时,会优先去远程仓库中查看是否有最新的 example-service-1.0-SNAPSHOT.jar,如果有则下载下来使用。即使本地仓库中已经有了 example-service-1.0-SNAPSHOT.jar,它也会尝试去远程仓库中查看同名的jar是否是最新的。
有的人可能会问,这样不就不能充分利用本地仓库的缓存机制了吗?别着急,Maven 比我们想象中的要聪明。在配置Maven 的 Repository 的时候中有个配置项,可以配置对于 SNAPSHOT 版本向远程仓库中查找的频率。频率共有四种,分别是always、daily、interval、never。当本地仓库中存在需要的依赖项目时,always 是每次都去远程仓库查看是否有更新,daily 是只在第一次的时候查看是否有更新,当天的其它时候则不会查看;interval 允许设置一个分钟为单位的间隔时间,在这个间隔时间内只会去远程仓库中查找一次,never 是不会去远程仓库中查找(这种就和正式版本的行为一样了)
<profile>
<id>nexus</id>
<repositories>
<repository>
<id>nexus-public</id>
<url>http://localhost:8081/nexus/content/groups/public/</url>
<releases><enabled>true</enabled></releases>
<!-- daily(默认) always interval:10(10分钟一次) never-->
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
所以一般在开发模式下,我们可以频繁的发布 SNAPSHOT 版本,以便让其它项目能实时的使用到最新的功能做联调;当版本趋于稳定时,再发布一个正式版本,供正式使用。当然在做正式发布时,也要确保当前项目的依赖项中不包含对任何SNAPSHOT 版本的依赖,保证正式版本的稳定性。
手动部署jar
# 手动安装jar到本地库
mvn install:install-file -Dfile={Path/to/your/xx.jar} -DgroupId=com.oracle -DartifactId=ojdbc6
-Dversion=11.2.0 -Dpackaging=jar
生命周期 和 插件
- 生命周期:
是指项目构建的生命周期,一个项目构建要经过的各个阶段 ==> PHASE
Maven的生命周期( LifeCycle
),idea 中会显示部分,
idea 显示的只是maven项目构建时生命周期(lifecycle)的一部分阶段(phase):
早期项目构建,方式各不相同,maven抽取了大量的经验,总结了一套规范的项目构建过程,即构建的各个 阶段(phase)
【pre-clean,clean,post-clean,
validate,initialize,generate-sources,process-sources,generate-resources,process-resources,
compile,process-classes,generate-test-sources,process-test-sources,generate-test-resources,
process-test-resources,test-compile,process-test-classes,test,prepare-package,package,
pre-integration-test,integration-test,post-integration-test,verify,install,deploy,
pre-site,site,post-site,site-deploy】
生命周期分为3套:【clean周期】、【default周期】、【site周期】
clean周期:负责清理项目 (清理上次编译的文件)
default周期:主体周期,负责完成项目构建主体过程
site周期:建立站点( 生成站点文档,发送站点到服务器 )
每个生命周期都分为一些阶段(phase):
clean周期:分为【pre-clean、clean、post-clean】3个 phase
: pre-clean负责清理前的准备工作,clean负责清理工作,post-clean负责清理后的善后工作
default周期:分为【valiedate、initialize、generate-source、...、generate-resources、process-resources、
compile、...、package、...、install、deploy】多个 phase
:compile负责编译,package负责打包,install负责安装到本地仓库,deploy负责安装到远程仓库
site周期:分为【pre-site、site、post-site、site-deploy】4个 phase
:site负责生成站点 文档,site-deploy负责将站点发送到服务器
执行方式:mvn phase名
mvn clean 清理上次编译的文件
mvn package 打包
mvn compile 编译
mvn clean compile 清理并编译(多个phase同时执行)
执行原则:周期独立,阶段顺序依赖
(mvn clean compile:执行clean周期的clean阶段和default周期的compile阶段,但由于周期独立,阶段顺序依赖,实际上不仅执行了clean周期的clean阶段及之前,也执行了default周期的compile阶段及之前)
周期独立:三个周期可以单独执行任意一个,没有顺序依赖,即不执行clean也可以执行default,site亦然。
: 执行clean周期内的phase对其他周期没任何影响。
phase顺序依赖:在执行周期内的阶段时是顺序依赖的,如【pre-clean、clean、post-clean】,执行 clean时,其实会依次执行
pre-clean和clean;执行post-clean时会依次执行pre-clean,clean,post-clean
执行compile时,会先依次执行default周期内compile之前的所有phase。
总结:三个周期可以单独执行任意一个,没有顺序依赖
一个周期内的多个phase之间是具有逻辑关联的,类似是一个流水线,必须一步接一步的做
- 插件:
Maven中的生命周期都是抽象的,真正的工作都有插件来完成。比如:package
阶段由 maven-jar-plugin
插件实现
complie
阶段由 maven-compiler-plugin
实现。类似于接口和实现类的关系,这样就提供了很好的扩展性。在有限的生命周期上可以定制无限的插件实现。
Maven为大多数的阶段,绑定了默认插件。比如 package 阶段和 compile 阶段
生命周期和插件密不可分,密切配合。
每个插件,都有很多目标(goal),则如此则有一个对应关系:goal of plugin ==> phase of lifecycle
插件除了有很多 goal 之外,还有自己的前缀,如下的插件前缀是“dependency
”,其中有goal “tree
”,则 tree 的调用方式为 mvn dependency:tree
(前缀:goal),即插件可以独立执行,只是有的插件的目标绑定到了某些生命周期阶段 phase。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</build>
- 生命周期 phase 和插件 goal 的绑定:
生命周期与插件绑定,或者说是 阶段和目标绑定。
比如,编译过程:对应的是default周期的compile阶段,而插件 maven-compiler-plugin的compile目标可以完成此任务。
maven为了方便用户使用,已经为一些主要的阶段绑定了目标,比如:
【凡是 `maven-xx-plugin`如此模式命名的都是maven官方插件,自定义的插件命名:`xx-maven-plugin`】
pre-clean 没有目标
clean maven-clean-plugin:clean
post-clean 没有目标
process-resources maven-resources-plugin:resources ( 拷贝主资源文件到输出目录 )
compile maven-compile-plugin:compile
package maven-jar-plugin:jar
install maven-install-plugin:install
deploy maven-deploy-plugin:deploy
执行:mvn compile
输出:
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ spring01 ---
[WARNING] Using platform encoding (GBK actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ spring01 ---
[INFO] Changes detected - recompiling the module!
可以看到插件的执行情况,目标和阶段的绑定可见
- 自主设计生命周期 phase 和插件 goal 的绑定:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<goals>
<goal>tree</goal>
</goals>
<phase>post-clean</phase>
</execution>
<executions/>
</plugin>
</plugins>
</build>