Maven实战计划

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会导致编译报错

典型单元测试

  1. 准备测试类及数据
  2. 执行要测试的行为
  3. 检查结果

历史原因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)的第一原则是:路径最近者优先。当两个路径相同时,第一声明者优先的第二原则就生效

可选依赖

  • 使用元素声明的依赖表示这个依赖不会被传递给依赖于此项目的项目
  • 在理想的情况下,是不应该使用可选依赖的,大部分情况下使用可选依赖的原因是某一个项目实现了多个特性,有悖于面向对象设计中的单一职责性原则

排除依赖

  • 使用排除依赖,声明的时候只需要groupId和artifactId,而不需要version元素就可以唯一定位依赖图中的某个依赖

归类依赖

  • 通过在根元素下的下自定义属性值标签来实现对依赖版本号的统一管理

优化依赖

  • 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修改,其他代码测试修改(如数据访问),请编写单元测试

版本管理

  • 一个健康的项目通常有一个长期的,合理的版本演变过程
  • 理想的发布版本应当对应了项目某个时刻比较稳定的状态,包括源代码的状态以及构建的状态,应当满足如下条件

发布版本应当满足的条件

  1. 所有自动化测试应当全部通过
  2. 项目没有配置任何快照版本的依赖
  3. 项目没有配置任何快照版本的插件
  4. 项目所包含的代码已经全部提交到版本控制系统中

Maven版本号约定

<主版本>.<次版本>.<增量版本>-<里程碑版本>
  • 主版本表示项目的重大架构变更
  • 次版本表示较大范围内的功能增加和变化
  • 增量版本一般为重大Bug的修复
  • 里程碑版本,顾名思义,这往往指某一个版本的里程碑

灵活的构建

  • 一个优秀的构建系统必须足够灵活,它应该能让项目在不同的环境下都能成功地构建
  • Maven三个构建特性:属性,Profile,资源过滤

常用双子星属性

  • ${basedir}表示项目根目录
  • ${version}表示项目版本

POM属性

  • 用户可以使用该类属性引用POM文件中对应元素的值
  • ${project.artifactId}
  • ${project.build.sourceDirectory}:项目的主源码目录src/main/java
  • ${project.build.directory}:项目构建输出目录,默认为target

你可能感兴趣的:(Maven实战计划)