持续集成与持续交付渐渐成为不少公司保证交付质量的不二法门,朋友的公司也顺应趋势,通过maven在测试环境上手了一套。在稳定运行了一段时间后,持续集成部分场景下的案例失败率达到100%,排查后发现是由于本地仓库的某个有问题的依赖包导致的,可是公司私库中这个依赖包已经被修复上传,为什么持续集成的机器没有下载呢?让我们一起探索一下吧。

问题定位

原来该持续集成机器采用的命令是mvn clean install XXXX,缺少了一个关键的参数-U,该参数可以强制让maven在每次构建时都检查所有SNAPSHOT依赖是否更新,确保本次构建基于最新的状态。如果没有该参数,maven默认以天为单位检查依赖更新,而持续集成的频率显然比这高很多(通用的做法是代码每次提交都会触发一次构建集成),这就导致了虽然问题已修复的jar包已经上传了私库,但是当天没有触发依赖更新检查,自然该存在问题的jar包就不会被替换了,持续失败也就不可避免了。

让我们看一下正确的构建命令吧。

mvn clean deploy -B -e -U -Dmaven.repo.local=xxx

-U参数前面已经解释过,接下来让我们一起看一下其他参数的含义吧。

  • clean: 删除项目中已经编译好的信息,删除target目录,保证上一次构建的输出不会影响到本次构建。
  • deploy:构建的SNAPSHOT输出自动部署到私有maven仓库供他人使用。
  • -B:使用批处理模式构建项目,能够避免一些需要人工参与交互而造成的挂起状态。
  • -e:如果构建出现异常,该参数能让maven打印完整的stack trace,进而可以方便定位问题。
  • -Dmaven.repo.local:持续集成服务器可能有很多任务,每个任务都会下载自身依赖至本地仓库,为了避免多任务并发使用本地仓库可能会引起的冲突,建议使用-Dmaven.repo.local=/home/mac/ci/accout-test/这样的参数为每个任务分配对应的本地仓库存储路径。

由此可以看出深入了解一下maven还是很有必要的,接下来我们一起系统地掌握一下maven吧,一方面可以应用于实际的项目实践中,另一方面也可以在面试过程中轻松吊打面试官,进而迎娶白富美,走上人生巅峰。

Maven的定义

Maven是一个采用纯Java编写的开源项目管理工具,它采用了一种被称之为Project Object Model (POM)概念来管理项目,所有的项目配置信息都被定义在一个POM.xml的文件中, 通过该文件maven可以管理项目的整个生命周期,包括clean、compile、test、package、install、deploy等;同时它也是一个依赖管理系统(Dependency Management System),可以对项目所依赖的jar进行统一管理。

Maven工程的拆分与聚合思想

工程的拆分

工程的拆分可以实现分模块开发与测试,可以实现多人的并行开发与管理,提高工程代码复用度的同时也提高了软件的开发速度与效率。比如:常见的安卓应用模块化可以通过maven把一个应用拆分为多个模块(如用户、支付、配送等),每个模块形成独立的工程,将来要用到的时候就把它们的坐标(定义后面描述)给引进来进行统一构建。

工程的聚合

项目拆成多个子模块后,独立运行各个模块是无法完成软件项目的要求的,需要把它们都整合起来来完成相关工作。这时就需要父工程来管理各个子模块,将它们聚合在一起运行,比如可以先把后端应用拆分为数据访问层、应用服务层、前端控制层、数据持久层等子模块,然后打成一个独立的可运行的war包,通过tomcat等容器部署后提供完整服务。

继承的理解

类似java类的继承,都是为了消除重复。子类继承父类,父类里有的方法和属性在子类中就不需要再定义和实现了,使用的时候直接调用父类就可以。如上面所示,首先有一个父工程,然后子工程(数据访问层、应用服务层、前端控制层和数据持久层等)要用到的依赖都可以在父工程(parentProject)的pom.xml里预先定义好,然后子工程在开发的时候直接使用就可以了,不需要再引用坐标。

深度理解

父工程本身不写代码,它里面有一个pom.xml文件,这个文件可以将多个子模块中通用的jar所对应的坐标,集中在父工程中配置,将来的子模块就可以不需要在pom.xml中配置通用jar的坐标了。

Maven项目标准目录结构

  • 核心代码部分:src/main/java
  • 配置文件部分:src/main/resources
  • 测试代码部分:src/test/java
  • 测试配置文件:src/test/resources
  • 页面资源(包含js,css,图片资源等):src/main/webapp

Maven的坐标定义

坐标:在平面几何中坐标(x,y)可以标识平面中唯一的一点,而maven坐标的主要组成如下所示:

  • groupId:当前项目隶属项目、组织
  • artifactId:当前项目的唯一标识符
  • version:当前项目的版本号,一般分为开发版本(Snapshot)和发布版本(Release)两种。
  • packaging:当前项目的打包方式(pom/jar/war,默认为jar)

groupId、artifactId、version简称为GAV。

Q:maven为什么要使用坐标?

A:Maven世界拥有大量构件,需要一个唯一标识来进行区分定位,正如Java里面包名与类名的组合。

Q:Maven的版本为什么需要区分为两种呢?

A:因为在实际开发测试过程中,经常遇到服务依赖的场景,比如A服务依赖于B服务,A和B同时开发,但B在开发中发现了BUG,修改后,将版本由1.0升级为2.0,那么A必须也跟着在POM.XML中进行版本升级。过了几天后,如果B又发现了问题,进行修改升级版本发布后,必然需要通知A再次进行升级...如此周而复始,A的开发过程会不断被中断,进行版本升级。那如果A被忘记告知了呢?特别是测试人员拿到的是一个依赖错误版本发布,并经过一番测试后却被告知这个包是一个有问题的发布包,一切需要重来,反复几次,再坚强的测试人员也必定会崩溃。

那有什么比较好的解决办法吗?

这就需要使用Snapshot版本,在开发过程中B发布的版本标志为Snapshot版本,A进行依赖的时候选择B的Snapshot版本,那么每次B发布后,都会在私服仓库中形成带有时间戳的Snapshot版本,而A构建的时候会自动下载B最新时间戳的Snapshot版本!这样上面的问题就被完美解决,无论开发还是测试人员都不担心啦。

Maven的仓库

Maven在某个统一的位置存储所有项目的共享的构件,这个统一的位置,就称之为仓库(仓库就是存放依赖和插件的地方)。

Maven的仓库有两大类:1.本地仓库 2.远程仓库(又分为中央仓库、私服、其它公共库)。

  • 本地仓库:就是Maven在本机存储构件的地方。Maven的本地仓库,在安装maven后并不会创建,它是在第一次执行maven命令的时候才被创建。Maven本地仓库的默认位置在用户的目录下的.m2/repository/,可以通过修改setting.xml修改默认位置。
  • 中央仓库:包含了绝大多数流行的开源Java构件,以及源码、作者信息、SCM、信息、许可证信息等。开源的Java项目依赖的构件都可以在这里下载到。地址:http://repo1.maven.org/maven2/
  • 私服:是一种特殊的远程仓库,一般为各个公司架设在局域网内的供全公司开发人员使用。

Maven的jar包搜索顺序

Maven项目中依赖jar包搜索顺序如下所示:




maven 拷贝jar maven copy_maven 拷贝jar


以上分析了持续集成中maven的正确构建命令并引申出了maven的简单介绍,希望可以对大家有所帮助,后续会针对maven依赖及常见面试问题展开,敬请期待。