目录
1. maven的作用
实现依赖管理、构建管理、模块拆分管理的自动化
参考书籍《Maven in Action》
参考内容:基于中华石杉老师的授课内容整理
2. 依赖管理
2.1 坐标机制
- groupId:以公司或者组织的官网的域名倒序来开头 + 项目名。如:com.baidu.oa
- artifactId:项目中的某个模块,或者某个服务(主流)。如oa-auth服务。
- version:工程的版本号
- packaging:工程的打包方式。一般是war和jar两种方式。
-
classifier:定义某个工程的附属项目。几乎不用。
坐标的意义:通过五个维度的坐标可以唯一定位一个项目的发布包
2.2 版本管理
version:1.0.0-SNAPSHOT版本
- 第一位是大版本。通常在整体架构有特别的升级或者变化时才会累加第一位大版本。
- 第二位是小版本,一般如果加入一些新的功能或者模块,或者做了一些重构,就会累加第二位小版本。
- 第三位是最小版本,一般如果是修复一些bug,或者作出轻微的改动,就会累加第三位小版本。
- SNAPSHOT:就是当前这个版本下的快照版本,代表代码正在开发或者测试中,可以试用,但是没有经过完善测试,不会承诺非常稳定。如果没有SNAPSHOT,那么就是说已经经过完善测试,是可以发布的稳定版本。
2.3 依赖范围
maven有三套classpath。classpath就是项目中用到的各种依赖的类,jvm在运行的时候需要去classpath下面加载对应的类。
maven在编译源代码的时候有一套classpath;在编译测试代码以及执行测试代码的时候有一套classpath;运行项目的时候有一套classpath;依赖范围就是用来控制依赖包与这三种classpath的关系的。
简单来说,不同的依赖范围,会导致那个依赖包可能在编译、测试或者打包运行的时候,有时候可以使用,有时候不能够使用
- compile:编译依赖范围,在编译,测试,运行时都需要。
- test: 测试依赖范围,测试时需要。编译和运行不需要。如Junit
- runtime: 运行时依赖范围,测试和运行时需要。编译不需要。如JDBC驱动包
- provided:已提供依赖范围,编译和测试时需要。运行时不需要。如servlet-api
- system:系统依赖范围。本地依赖,不在maven中央仓库。
2.4 依赖传递
1级依赖 \二级依赖 | compile | test | provided | runtime |
---|---|---|---|---|
compile | compile | - | - | runtime |
test | test | - | - | test |
provided | provided | - | provided | provided |
runtime | runtime | - | - | runtime |
举例1:junit的依赖范围是test,junit对A的依赖范围是compile,那么项目对A的依赖是test。
举例2:项目依赖于A,A是compile;A依赖于B,B是test;则项目对B的依赖范围是空。
2.5 依赖冲突的调节
Maven会自动依赖调解,即对已给项目不同的版本选择一个版本来使用。
Maven对依赖冲突采取的调节方式是最短路径原则和第一声明原则。
A->B->C->X(1.0)
A->D->X(2.0)
由于只能引入一个版本的包,此时Maven按照最短路径选择导入x(2.0)
A->B->X(1.0)
A->D->X(2.0)
路径长度一致,那么哪个依赖在pom.xml里先声明,maven就采用哪个。
2.6 可选依赖
<optional>true</optional>
此时依赖传递失效,不会向上传递。很少使用。
如果A依赖于B,B依赖于C,B对C的依赖是optional,那么A就不会依赖于C。
反之,如果没有optional,根据传递性依赖机制,A会依赖于C。
2.7 排除依赖
场景再现
X -> A -> C-1.0
X -> B -> D -> C-2.0
根据2大依赖原则,此时项目会采用1.0版本的C。如果D调用了2.0版本C中新的方法,此时由于项目中依赖的是1.0版本的C,该版本还没有此方法,那么项目就会报错。
显然,根据软件的兼容性,通常是引入最新版的依赖。此时,需要手动控制maven的依赖传递。
使用mvn dependency:tree进行依赖分析,此时的依赖路径树如下。
A
--C-1.0
B
--D
--C-2.0
使用exclusion将A中的1.0版本所依赖的C给排除掉,即可解决问题
<dependency>
<groupId>A</groupId>
<artifactId>A</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>C</groupId>
<artifactId>C</artifactId>
</exclusion>
</exclusions>
</dependency>
此时的依赖树如下:
A
B
--D
--C-2.0
3. 多仓库架构
图 3-1
4. nexus(V3.12)
4.1 常见仓库介绍
图 4-1
hosted:宿主仓库。这个仓库是用来让你把你公司内部的发布包部署到这个仓库里来,然后公司内的其他人就可以从这个宿主仓库里下载依赖去使用。
proxy:代理仓库。代理了公司外部的中央仓库,国内目前流行的是阿里云镜像仓库,由阿里云去连接中央仓库。
group:仓库组。其实就是将各种宿主仓库、代理仓库全部组成一个虚拟的仓库组,然后项目只要配置依赖于一个仓库组,该项目就可以自动连接仓库组对应的各种仓库。
maven-central:maven中央仓库的代理仓库。
maven-releases:该仓库是个宿主仓库。用于部署公司内部的release版本的发布包(类似于1.0.0,release的意思就是你的工程已经经过了完善的测试,如单元测试,集成测试,QA测试,上生产环境使用了)到这个仓库里面,供其他同事在生产环境依赖和使用。
maven-snapshots:该仓库是个宿主仓库,用于部署公司内部的snapshot版本的发布包到这个仓库里(如果你的某个工程还在开发过程中,测试还没结束,但是,此时公司里其他同事也在开发一些工程,需要依赖你的包进行开发和测试,联调,此时你的工程的版本就是类似1.0.0-SNAPSHOT这样的版本),供其他同事在开发和测试的时候使用。
3rd party:该仓库是个宿主仓库,主要用来部署没法从公共仓库获取的第三方依赖包。比如说,你的项目依赖于第三方支付厂商的一个依赖包,但是那个依赖包不是开源的,是商业的包。那么你是没有办法从maven中央仓库获取的。此时,可能会自己手动从支付厂商那里获取到一个jar包,下载之后上传到私服里来,就放这个仓库里。该仓库需要手动创建。
maven-public:仓库组。上面所有release仓库都在这个仓库组内
4.2 nexus仓库架构
图4-2
4.3 nexus的使用
4.3.1 激活profile
有了私服后就需要将公司中的项目配置为强制从公司内的私服来下载,不允许走外网。这样可以统一管理。毕竟nexus私服本身也是代理了各种中央仓库,直接用nexus私服就可以了。
通常会在settings.xml配置文件中,为当前机器统一配置使用的私服仓库地址,而且一般都是直接用私服中的仓库组,在settings.xml中用profiles即可。
<profiles>
<profile>
<id>nexus</id>
<repositories>
<repository>
<id>nexus</id>
<name>Nexus </name>
<url>http://localhost:8081/nexus/content/groups/public</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>nexus</id>
<name>Nexus Plugin Repository</name>
<url>http://localhost:8081/nexus/content/groups/public</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>nexus</activeProfile>
</activeProfiles>
激活对应的profile,在项目执行构建的时候,就会将profile中的仓库配置应用到每个项目中去。
4.3.2 配置mirror
用mirror镜像机制,来强制要求所有对远程仓库的请求,全部通过镜像走私服。
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://localhost:8081/nexus/content/groups/public</url>
</mirror>
</mirros>
将所有repository的id修改为central,直接覆盖maven超级pom中的morning中央仓库,相当于此时唯一的远程中央仓库变成了我们自己配置的两个仓库。
然后将url配置全部改为http://central,其实是没意义的一个url,因为此时在需要从远程仓库下载依赖或者插件的时候,会从两个自己配置的central仓库去走,然后看release或者snapshot是否支持,如果支持,那么就会找镜像配置,由于设置了镜像匹配所有请求,所以所有请求都会走镜像,而镜像配置的是私服地址,所以相当于所有的请求都会走私服了。
4.3.3 权限管理
nexus私服默认就是可以读的,不需要认证,公司局域网内的人都可以去配置之后拉取依赖。但是如果要进行部署的话,需要有一个专用的部署账号,通过账号认证,才能部署发布包到nexus私服。
nexus的权限使用的是经典的RBAC模型,默认有三个用户:
- admin:管理员账号。默认密码是admin123
- deployment:开发账号。可以进行搜索和部署构建。3版本后已经被消除掉了。
- anonymous:匿名账号。没有给认证信息的账号,只能查看依赖。
5. 部署项目到私服
5.1 发布仓库配置
在pox.xml中设置
<distributionManagement>
<repository>
<id> nexus-releases</id>
<name> Nexus Release Repository</name>
<url>http://localhost:8081/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id> nexus-snapshots</id>
<name> Nexus Snapshot Repository</name>
<url>http://localhost:8081/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
5.2 部署专业账号的配置
由于匿名用户只能下载依赖,不能部署发布包,因此如果要能够部署发布包,还需要在settings.xml文件里通过
<servers>
<server>
<id>nexus-releases</id>
<username>deployment</username>
<password>deployment123</password>
</server>
<server>
<id>nexus-snapshots</id>
<username>deployment</username>
<password>deployment123</password>
</server>
</servers>
5.3 自动部署
mvn clean deploy
执行此命令后,maven会自动编译源代码、运行单元测试、打成jar包、将jar包安装到本地仓库、将jar包部署到配置的远程私服仓库里面去。(清理、编译、测试、打包、安装、部署)。
5.4 手动部署
有些第三方厂商的jar包,比如特殊数据库厂商的jdbc驱动,或者某个厂商的支付相关的api jar包,是不提供公网下载的,只能从他们那里拿到一个jar包,此时就需要手动将其上传到3rd party仓库里面去。
比如:fastdfs_client_v1.24.jar
手动部署:
mvn deploy:deploy-file -DgroupId=com.csource -DartifactId=fastdfs-client-java -Dversion=1.24 -Dpackaging=jar
-Dfile=F:\DevelopmentKit\fastdfs_client_v1.24.jar
-Durl=http://localhost:8081/repository/3rd-party/
-DrepositoryId=nexus-3rd-party
6. 生命周期
一个完整的项目构建过程通常包括清理、编译、测试、打包、集成测试、验证、部署等步骤,Maven从中抽取了一套完善的、易扩展的生命周期。Maven的生命周期是抽象的,其中的具体任务都交由插件来完成。Maven为大多数构建任务编写并绑定了默认的插件,如针对编译的插件:maven-compiler-plugin。用户也可自行配置或编写插件。
6.1 三套生命周期
aven有三套完全独立的生命周期,clean,default和site。每套生命周期都可以独立运行,每个生命周期的运行都会包含多个phase(阶段),每个phase又是由各种插件的goal来完成的,一个插件的goal可以认为是一个功能。每次执行一个生命周期,都会依次执行这个生命周期内部的多个phase,每个phase执行时都会执行某个插件的goal完成具体的功能。且后面的phase依赖于前面的phase。执行某个phase时,其前面的phase会依顺序执行,但不会触发另外两套生命周期中的任何phase。
图6-1
6.1.1 clean生命周期
phase | goal |
---|---|
pre-clean | 执行清理前的工作 |
clean | 清理上一次构建生成的所有文件 |
post-clean | 执行清理后的工作 |
6.1.2 default生命周期
default生命周期是最核心的,它包含了构建项目时真正需要执行的所有步骤。
6.1.3 site生命周期
phase | goal |
---|---|
pre-site | |
site | 生成项目的站点文档 |
post-site | |
site-deploy | 发布生成的站点文档 |
注意,maven会从上到下执行每个pharse的plugin goal。如果该阶段没有绑定任务plugin,该阶段将什么都不执行。
6.2 不执行生命周期
但是有些命令是不执行任何一个生命周期的任何一个phase。而是直接执行指定的插件的一个goal。如:
mvn dependency:tree ---> 直接执行dependency这个插件的tree这个goal
mvn deploy:deploy-file ---> 直接执行deploy这个插件的deploy-file这个goal
7. 插件
http://maven.apache.org/plugins/index.html
由于maven在执行过程中会执行生命周期中的各种phase,通过在pom.xml中配置想要的插件,将某个第三方插件绑定到某个阶段的phase。此后插件就会执行其goal,实现想要的功能。
7.1 插件和goal
图 6-2
图 6-3
7.2 自定义绑定
用户可以根据需要将任何插件目标绑定到任何生命周期的阶段,如:将maven-source-plugin的jar-no-fork目标绑定到default生命周期的package阶段,这样,以后在执行mvn package命令打包项目时,在package阶段之后会执行源代码打包,生成如:ehcache-core-2.5.0-sources.jar形式的源码包。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase> <!-- 要绑定到的生命周期的阶段 -->
<goals>
<goal>jar-no-fork</goal> <!-- 要绑定的插件的目标 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
7.3 配置插件
Maven插件高度易扩展,可以方便的进行自定义配置。如:配置maven-compiler-plugin插件编译源代码的JDK版本为1.7:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
也可以对插件的各个目标进行更具体的配置。Configuring Plug-ins
7.4 插件仓库
跟其他构件一样,插件也是根据坐标存储在Maven仓库中。超级POM中Maven配置的默认插件远程仓库如下:
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Central Repository</name>
<url>http://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
8. 工程聚合
8.1 提出问题
假设有三个模块:组织机构,权限管理,流程审批,且这三个模块都是一个人在开发,那么势必会有一些场景,比如说是三个模块互相之间有一些依赖,然后呢,其中一个被依赖的基础模块修改了代码,这个时候,我们要干嘛?
(1)要运行一下基础模块的单元测试套件,确保所有单元测试都可以通过
(2)要将修改代码后的模块部署到本地仓库
(3)其他模块要依赖更新版本的snapshot包,
(4)需要运行其他所有依赖这个基础模块的模块的单元测试套件,确保依赖它的其他模块没有任何问题
那如果是20个工程呢?显然这样处理是不合理的。
8.2 解决方案
maven的解决方案是提供聚合功能。通过设定一个父工程,在父工程里聚合这些子工程。只要对父模块运行一次构建命令,maven就会自动对这个父模块下面的所有子模块都运行相应的构建命令,这样就可以保证一键自动化构建所有的模块,而无需一个一个依次去构建。
<groupId>com.xxx.oa</groupId>
<artifactId>oa-parent</artifactId> <!-- 通常父工程以parent结尾 -->
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging> <!-- 注意是个pom项目 -->
<name>oa parent project</name>
<modules>
<module>oa-organ</module> <!-- 聚合的子项目 -->
<module>oa-auth</module>
<module>oa-flow</module>
</modules>
一般来说会将模块统一放在父工程的目录下,然后对oa-parent运行mvn clean install,此时就会对oa-parent中所有的工程都进行统一的构建。
9.继承
9.1 继承
通常对同一个系统,需要将各个模块的工程,其中的基础性的、相同的依赖,全部放入一个父工程中,集中式统一约束所有模块的依赖,避免每个模块负责的人胡乱使用依赖版本,比如有的人用spring 3,有的人用spring 4。
maven里面提供了一个继承功能,即可以将一个项目中所有模块通用的一些配置,比如依赖和插件,全部放在一个父工程中,然后子工程可以选择从父工程中要继承的依赖和插件。此时子工程只要声明groupId和artifactId即可,不需要声明version版本号,因为version全部放在父工程中统一声明,强制约束所有子工程的相同依赖的版本要一样。
9.2 实现方式
在父工程中,直接用dependencies和plugins来声明依赖和插件的话,会强制子工程全部继承。
在父工程中使用
父工程oa-parent
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>${org.eclipse.persistence.jpa.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>${javaee-api.version}</version>
</dependency>
<dependcy>
....
</depency>
</dependencies>
</dependencyManagement>
子模块
<!--继承父类-->
<parent>
<artifactId>oa-parent</artifactId>
<groupId>com.xxx.oa</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>oa-organ</artifactId>
<packaging>jar</packaging>
<!--依赖关系-->
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
10.版本约束
通过
比如在父工程中:
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxx.oa</groupId>
<artifactId>oa-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>oa parent project</name>
<url>http://maven.apache.org</url>
<modules>
<!-- 模块都写在此处 -->
<module>oa-auth</module>
<module>oa-persist</module>
<module>oa-another</module>
</modules>
<properties>
<!-- 定义 spring版本号 -->
<spring.version>4.0.2.RELEASE</spring.version>
<junit.version>4.7</junit.version>
</properties>
<!-- 配置共有依赖 -->
<dependencyManagement>
<dependencies>
<!-- spring 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- junit 依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
以后每次修改,比如升级spring版本时,就直接修改父工程的properties元素就可以了,所有的子工程会保持一致
11.使用import强制约束依赖方的版本号
11.1 提出问题
比如你基于activiti开发了一个流程审批的封装组件,这是一个不属于任何一个项目的基础的一个组件。其他人如果要用你的这个组件,肯定会在depedencies里面去定义和声明。但是,假设依赖方依赖了你的组件,你的组件依赖了activiti 1.3版本,而依赖方自己又声明了一个对activiti 1.2的依赖,此时根据依赖调解的原则,肯定是用他自己的旧版本。这就会造成依赖冲突。
11.2 解决方案
所以说,对于公司里一些重量级的组件,一般来说会采取下面的这种方式:
- 首先自己开发一个工程,那个工程是对外提供服务的
- 接着再开发一个pom包,后缀名为bom。这个pom包中用
元素声明好对你的组件的依赖的版本号,还有你的组件使用的重要的第三方开源框架的版本号 - 依赖方依赖于那个工程的pom类型的包,再在dependency里面去声明自己要的依赖,此时版本就被约束了
依赖方
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.zhss.commons</groupId>
<artifactId>commons-flow-bom</artifactId>
<version>1.2.9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
此时,假设依赖方要自己依赖一个过旧的开源框架的版本,就会有提示报警,不让他自行定义过旧版本的框架。
12.版本管理与版本控制
12.1 两者的区别
版本管理:通常指的是maven中的version,项目的版本管理,随着整个功能的开发,bug的修复,或者大的架构升级,通常来说,都会增加版本号。
版本控制:代码版本控制,git中的多次提交,每次往git代码仓库提交一次代码,这个代码的版本就变更了一次,而git会自动记录下来每次代码版本的变更。因为每次提交代码,相当就是代码的版本就变更了一次。
12.2 maven中的版本管理
12.2.1 版本的类别
版本分为两种,一种是快照(snapshot)版本,一种是发布(release)版本
快照版本就是版本号后面加上SNAPSHOT,比如1.0.0-SNAPSHOT
发布版本就是版本号上不加SNAPSHOT的,比如1.0.0
12.2.2 常见的版本迭代规则
版本通常而言是三位的。比如1.0.0
1.0.0中的第一个1指的是一个重大版本,比如已经基本成型的一个系统架构。
也有不少项目一开始是0.0.1这样的版本,这个指的就是这个系统刚开始起步,甚至都没能形成一个完整的东西出来,只是少数核心功能也出来了。
比如,用springboot开发的电商系统,一开始1.0.0这个版本包含了商城首页、购物车、订单系统、物流系统,就这么几个模块。如果某天改造和升级到微服务架构,把核心的几个模块改造了spring cloud的微服务化。那么版本就变为了2.0.0。大版本的变更很少见,一般几年才一次。
1.0.0中的第二个0指的是一个次要版本,比如1.1.0,那么就是加了一些新的功能或者开发了一些新的模块,或者做了一些较大的代码重构,技术升级改造。
比如,新增一个用户评论功能,此时就会进入一个版本,叫做1.1.0-SNAPSHOT,在内部先开发和测试。接着发布1.1.0版本,此时这个版本就新增了一个用户评论的模块。
次要版本的变更一般就是对应各种需求,比如一个持续几周,或者一个月或者了几个月的大需求,对应一个次要版本
1.0.0中的第三个0指的是日常迭代的一个增量版本,比如1.1.1,一般对应着修复了一个bug,或者对某些代码做了轻微的优化或者调整。
比如,突然发现购物车这块有个bug,修复这个bug后版本号就是1.1.1-SNAPSHOT,这个bug的修复在内部先开发和测试,让产品经理来验收。最后发布,此时版本变成了1.1.1。此时代码就是包含了一个bug的修复。
增量版本一般就是一个大需求中的多个小需求,每个小需求可以给一个增量版本,或者是紧急fix了一个bug,那么就增加一个增量版本。
12.2.3 其他常见的命名版本
1)beta版本
maven的标准版本规范里,还包含了一个1.0.0-beta-1。
这里的beta代表的是公开测试版,就是对外提供试用的。
最后一个1代表的是增量版本的一个里程碑,就是在进行某个bug修复,或者功能调整的时候,是分为多个步骤,也就是多个里程碑的,那么此时可能就会对应了1.1.1-beta-1,1.1.1-beta-2,1.1.1-beta-3,这样好几个里程碑版本,每个里程碑版本代表完成了一个小阶段。
2)alpha版本
alpha也可以理解为实验版本,也就是内部测试版本,就是给自己公司内部用的。
3)rc版本
预发布版本,基本就比较稳定了,但是还不是最终发布版,可以尝试下载使用了。
12.3 从SNAPSHOT到RELEASE
通常,在开发的时候,版本都是SNAPSHOT版本。
如果要将snapshot发布发布为release版本,至少需要满足几个条件:
(1)所有的单元测试全部通过
(2)pom.xml中没有任何snapshot版本的依赖
(3)pom.xml中没有任何snapshot版本的插件
(4)这个版本的代码已经全部提交到对应的git分支上了,同时一定从分支merge到了master主干分支上去,并且给master分支打上了一个tag,这个tag就是对主干分支这一时刻代码的一个标签,这样任何时候都可以回退到主干分支的任何一个tag对应的版本的代码上去
此时一个release版本就完成了,然后就继续升级到下一个版本的snapshot版本上去,继续进行开发和测试。
12.4 版本与主干、分支以及标签
通常情况下一个版本对应一个分支,会在分支中进行各种开发+测试,搞定之后就会merge到主干。
然后就会给这个时候的主干打一个tag作为主干在这个版本的代码,以后任何时候都可以使用主干的某个tag,也就是某个时刻的某个版本的代码。