2018年3月20日14点29分
前言
- 接着上一篇JSON的文章,我又算是系统的学习了一遍Maven吧
- 之前对Maven的使用中,只把它当作一个Jar包自动导入工具,对于它提供的很多其他功能几乎都不怎么用
- 趁着恶补JSON知识的时候,顺手又来了一发《Maven实战》,算是提升码农搬砖效率的核心工程吧
- 我记笔记只记录感觉是点睛之笔的语句,纯手敲,不粘贴,为的是在往完扫一本书的过程中记住这些散发着光亮的句子
- 分享给大家当作看这本书的入口
优秀的构建工具
- maven的用途之一就是服务于构建,能够自动化构建过程,从清理,编译,测试到生成报告,再到打包和部署
- 我们一直在不停地寻找避免重复的方法。设计的重复,编码的重复,文档的重复,当然还有构建的重复。Maven最大化地消除了构建的重复,抽象了构建生命周期,并且为大部分的构建提供了已经实现的插件
不仅仅是构建工具
- Maven还是一个依赖管理工具和项目信息管理工具
- Maven也是一个约定大于配置的开源项目管理构建工具
编写主代码
- 项目主代码与测试代码不同,项目的主代码会被打包到最终的构件中,比如Jar包,War包(web归档包),而测试代码只在运行测试时用到
- src/main/java中的Java类的包名应该与groupId和artifactId相吻合
-
mvn clean compile
:Maven编译命令
- scope为依赖范围,若依赖范围为test表示该依赖只对测试有效,在主代码中import Junit会导致编译报错
典型单元测试
- 准备测试类及数据
- 执行要测试的行为
- 检查结果
历史原因Maven核心插件compiler
- Maven的核心插件之一——compiler只支持编译Java1.3,因此需要配置该插件使其支持Java高版本
-
mvn clean test
:编译并运行测试
打包
- 项目最终的产出物-Jar|War
- Maven默认打包类型为Jar
-
mvn clean package
:编译并且打包
如何让其他的Maven项目引用这个Jar
-
mvn clean install
:编译并将项目安装到Maven本地仓库中
maven-shade-plugin
- maven默认打包生成的Jar包是不能够直接运行的,因为带有main方法的类信息不回添加到manifest中(打开jar文件中的META-INF/MANIFEST.MF文件,将无法看到Main-Class一行)
- 为了生成可执行的Jar文件,需要借助maven-shade-plugin
- 配置并重新运行
mvn clean install
,会生成两个Jar包,其中带有original-前缀的Jar包是原始Jar,依然为不可以运行的Jar包
通过简单案例学习Maven
需求用例
注册账户
主要场景:
1,用户注册页面
2,系统生成验证码图片
3,用户输入想要的ID,Emial地址
4,用于输入验证码
......
扩展场景:
4a,用户无法看清验证码,请求重新生成
1.跳转到步骤2
- 注册账户=>主要场景=>扩展场景
- 设计简要的界面原型
简要设计
接口
- 详细了解了这个简单账户注册服务的需求之后,就能勾勒出该系统对外的接口
-
定义了系统核心的接口之后,基于功能分割和方便复用的原则,再对系统进一步进行划分。这里基于包名划分模块,这也是在Java中比较常见的做法
- ...service...系统的核心,它封装了所有下层细节,对外暴露简单的接口。这实际是一个Facade模式
坐标和依赖
- Maven坐标为各种构件引入了秩序,任何一个构件必须明确定义自己的坐标,而一组Maven坐标是通过一些元素定义的,它们是groupId,artfactId,version,packaging,classifier
依赖范围
-
compile
:编译依赖范围。如果没有指定,就会默认使用该依赖范围,此范围的Maven依赖,对于编译,测试,运行三种classpath都有效。典型的例子是spring-core,在编译,测试,运行的时候都需要使用该依赖
-
test
:测试依赖范围。只对于测试classpath有效,典型为Junit,它只有在编译测试代码及运行测试的时候才需要
-
provided
:已提供依赖范围,对于编译和测试classpath有效,但在运行的时候无效。典型是Servlet-api,编译和测试项目时需要该依赖,运行时由于容器提供,不需要Maven重复地引入一遍
-
runtime
:运行时依赖范围,对于测试和运行classpath有效,但在编译主代码时无效。典型是JDBC驱动实现,项目主代码的编译只需要通过JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动
传递性依赖
- ...Maven会解析各个直接依赖的POM,将那些必要的间接依赖以传递性依赖的形式引入到当前的项目中
- 当依赖传递性造成问题的时候,我们需要清楚地知道该依赖性传递是从哪条依赖路径引入的。Maven依赖调解(Dependency Mediation)的第一原则是:路径最近者优先。当两个路径相同时,第一声明者优先的第二原则就生效
可选依赖
- 使用<option></option>元素声明的依赖表示这个依赖不会被传递给依赖于此项目的项目
-
在理想的情况下,是不应该使用可选依赖的,大部分情况下使用可选依赖的原因是某一个项目实现了多个特性,有悖于面向对象设计中的单一职责性原则
排除依赖
- 使用<exclusion></exclusion>排除依赖,声明的时候只需要groupId和artifactId,而不需要version元素就可以唯一定位依赖图中的某个依赖
归类依赖
- 通过在根元素下的<properties>下自定义属性值标签来实现对依赖版本号的统一管理
优化依赖
-
mvn dependency:list
:查看当前项目的已解析依赖
-
mvn dependency:tree
:产看该项目的依赖树
-
mvn dependency:analyze
分析当前项目的依赖
仓库
何为Maven仓库
- 对于Maven来说,每个用户只有一个本地仓库,但可以配置很多远程仓库
- 最原始的本地仓库是空的,Maven必须知道至少一个可用的远程仓库,才能在执行Maven命令的时候下载到需要的构件
- 私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,供局域网内的Maven用户使用
- 在repositories元素下,可以使用repository子元素声明一个或者多个远程仓库
- 在Maven世界中,任何一个项目或者构建都必须有自己的版本,版本的值可能是1.0.0,1.3-alpha,2.1-SNAPSHOT
快照版本和发布版本
- 快照版本只应该在组织内部的项目或者模块间依赖使用,因为这时,组织对于这些快照版本的依赖具有完全的理解及控制权
- 项目不应该依赖于任何组织外部的快照版本依赖,这样的依赖会造成潜在的危险
生命周期和构建
- Maven的生命周期就是为了所有的构建过程进行抽象和统一
- Maven生命周期包含了项目的清理,初始化,编译,测试,打包,集成测试,验证,部署和站点等几乎所有的构建步骤。也就是说,几乎所有的项目的构建,杜能映射到这样一个生命周期上
-
Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,在Maven的设计中,实际的任务(如编译源代码,执行测试用例集)都交由插件来完成。这种思想与设计模式中的模板方法(Template Method)非常相似
- 模板方法模式在父类中定义算法的整体结构,子类可以通过实现或者重写父类的方法来控制实际的行为。这就既保证了算法有足够的可扩展性,又能够严格控制算法的整体结构
三套生命周期
- Maven拥有三条相互独立的生命周期,分别是clean,default,site
-
clean
目的是清理项目
-
default
目的是构建项目
-
site
目的是建立项目站点
-
每个生命周期包含一些阶段(phase),这些阶段是有顺序的,并且后面的阶段依赖于前面的阶段。用户与Maven最直接的交互方式就是调用这些生命周期阶段
clean生命周期
-
pre-clean
:执行一些清理前需要完成的工作
-
clean
:清理上一次构建生成的文件
-
post-clean
:执行一些清理后需要完成的工作
default生命周期
-
default生命周期定义了真正构建时所需要执行的所有步骤,它是所有生命周期中最核心的部分
validate
initialize
generate-sources
-
process-sources
:处理项目主资源文件,一般来说是对src/main/resources目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中
generate-resources
process-resources
-
compile
:编译项目的主源码。一般来说是对src/main/java目录下的Java文件至项目输出的主classpath目录中
process-classes
generate-test-sources
-
process-test-sources
:处理项目测试资源文件
-
test-compile
:编译项目测试代码
process-test-classes
-
test
:使用单元测试框架运行测试,测试代码不会被打包或部署
prepare-package
-
package
:接受编译好的代码,打包成可发布的格式,如JAR
pre-integration-test
integration-test
post-integration-test
verify
-
install
:将包安装到Maven本地仓库,供本地其他Maven项目使用
-
deploy
:将最终的包复制到远程仓库,供其他开发人员和Maven项目使用
site生命周期
-
site生命周期的目的是建立和发布项目站点,Maven能够基于POM所包含的信息,自动生成一个友好的站点,方便团队交流和发布项目信息。该生命周期包含如下阶段:
-
pre-site
:执行一些在生成项目站点之前需要完成的工作
-
site
:生成项目站点文档
-
post-site
:执行一些在生成项目站点之后需要完成的工作
-
site-deploy
:将生成的项目站点发布到服务器上
命令行与生命周期
- 从命令行执行Maven任务的最主要方式就是调用Maven的生命周期阶段
- 各个生命周期是相互独立的,而一个生命周期的阶段是有前后依赖关系的
-
mvn clean
:该命令调用clean生命周期的clean阶段
mvn test
-
mvn clean install
:该命令调用clean生命周期的阶段和default生命周期的install阶段,在执行真正的项目构建之前,清理项目是一个很好的实践
聚合和继承
- 为了解决软件的复杂性问题,通过各种方式对软件划分模块,以得到更清晰的设计以及更高的重用性。当把Maven应用到实际项目中时候,也需要将项目划分成不同的模块
- Maven的聚合特性能够把项目的各个模块聚合在一起构建
- Maven的继承特性则能帮助抽取各模块相同的依赖和插件等配置,在简化POM的同时,还能促进各个模块配置的一致性
使用Web自动测试工具测试
- 可以用单元测试覆盖的代码就不应该依赖于Web页面测试
-
Web页面测试应该仅限于页面的层次,如JSP,CSS,JavaScript修改,其他代码测试修改(如数据访问),请编写单元测试
版本管理
- 一个健康的项目通常有一个长期的,合理的版本演变过程
- 理想的发布版本应当对应了项目某个时刻比较稳定的状态,包括源代码的状态以及构建的状态,应当满足如下条件
发布版本应当满足的条件
- 所有自动化测试应当全部通过
- 项目没有配置任何快照版本的依赖
- 项目没有配置任何快照版本的插件
- 项目所包含的代码已经全部提交到版本控制系统中
Maven版本号约定
<主版本>.<次版本>.<增量版本>-<里程碑版本>
- 主版本表示项目的重大架构变更
- 次版本表示较大范围内的功能增加和变化
- 增量版本一般为重大Bug的修复
- 里程碑版本,顾名思义,这往往指某一个版本的里程碑
灵活的构建
- 一个优秀的构建系统必须足够灵活,它应该能让项目在不同的环境下都能成功地构建
- Maven三个构建特性:属性,Profile,资源过滤
常用双子星属性
- ${basedir}表示项目根目录
- ${version}表示项目版本
POM属性
- 用户可以使用该类属性引用POM文件中对应元素的值
- ${project.artifactId}
- ${project.build.sourceDirectory}:项目的主源码目录src/main/java
- ${project.build.directory}:项目构建输出目录,默认为target