Maven详解

手工安装构件(jar包)到本地仓库

mvn install:install-file -Dfile=jar_path(whereever) -DgroupId=xxx -DartifactId=xxx -Dversion=xxx -Dpackaging=jar

 

Maven简介

何为Maven

主要服务于基于Java平台的项目构建、依赖管理、项目信息管理。

适用各种规模的项目的构建;无论是传统的瀑布式开发,还是流行的敏捷开发,Maven都能大显身手。

 

何为构建

除了编写源码,我们每天有相当一部分时间花在了编译、单元测试、生成文档、打包、部署等不起眼的工作上,这就是构建。于是有人用软件的方法让这一系列工作完全自动化,使得项目的构建可以像自动流水线一样,只需一条简单命令,所有繁琐的步骤都能自动完成。

 

Maven不仅仅是构建工具

它还能帮助我们管理原本分散在项目中各个角落的项目信息,包括项目描述、开发者列表、版本控制系统地址、许可证、bug管理系统地址等,虽然这些微小工作看起来不起眼,却不知不觉为我们节省了大量查找信息的时间。

使用Maven还能享受一个额外的好处:对于项目目录结构、测试用例命名方式等都有既定的规则,只要遵循了这些成熟的规则,用户在Maven项目间切换的学习成本几乎为零,可以说是约定优于配置(COC)。

 

Maven与极限编程

首先看一下Maven如何帮助XP团队实现一些核心价值:

简单。Maven暴露了一组一致、简单的操作接口,使开发人员从高度自定义、复杂的构建系统中解脱出来。

交流与反馈。与版本控制系统结合,自动生成项目报告。了解项目状态,促进团队交流。

测试驱动开发(TDD)。TDD强调测试先行,所有产品都应该由测试用例覆盖。Maven有现成的成熟的插件支持业界流行的测试框架,如Junit和TestNG。

十分钟构建。强调我们能够随时快速地从源码构建出最终的产品。这正是Maven所擅长的,只需做一些简单的配置,用一条命令就可以让Maven帮你清理、编译、测试、打包、部署,最后得到最终产品。

持续集成(CI)。CI的前提是版本管理系统和构建系统。目前业界流行的CI服务器Hudson和CruiseControl都能很好地和Maven集成。

富有信息的工作区。这条实践强调开发者能够快速方便地了解到项目的最新状态。使用Maven发布的项目报告站点,病配置你需要的项目报告,如测试覆盖率报告,都能帮你把信息推送到开发者面前。

 

 

 

Maven的安装和配置

Windows上安装

设置环境变量:M2_HOME,path变量值的末尾加上%M2_HOME%\bin。

升级Maven:更新M2_HOME环境变量即可。

 

基于Unix系统上安装

tar -zxvf apache-maven-3.0-bin.tar.gz

虽然直接使用该目录配置环境变量就能使用Maven了,这里推荐做法是:在安装目录旁平行地创建一个符号链接,方便日后升级。

ln -s目录名 链接名

M2_HOME环境变量指向符号链接,并改path环境变量。

export M2_HOME=/home/dev/maven-link

export PATH=$PATH:$M2_HOME/bin

检查Maven安装:

echo $M2_HOME

mvn -v

升级Maven:不必每次升级都必须更新环境变量。假设需要升级到Maven4

rm maven-link

ln -s apache-maven-4 maven-link

 

 

 

Maven使用入门

推荐为每个POM声明name标签,方便信息交流。

定义POM,能让项目对象模型最大程度地与实际代码相独立,我们称之为解耦,很大程度上避免了Java代码和POM代码的相互影响。当项目需要升级版本时,只需修改POM,不需更改Java代码;当POM稳定后,日常的Java代码开发工作基本不涉及POM的修改。

 

Maven项目结构规范

主代码:src/main/java

测试代码:src/test/java

包名:groupId+artifactId

 

install:install该任务将项目输出的jar包安装到了本地仓库中,只有安装到本地仓库后,其他Maven项目才能使用。

 

mvn clean compile

执行了以下任务:

clean:clean、resources: resources、compiler:compile

都是对应 插件:插件目标。

 

使用Archetype生成项目骨架

简单地运行:

mvn archetype:generate

实际上在运行插件maven-archetype-plugin,完整格式为groupId:artifactId:version:goal

之所以可以简化成archetype来代替maven-archetype-plugin插件,因为插件也是由三个坐标准确定位的,groupId、artifactId和version都有一个Maven默认值。

groupId默认是org.apache.maven.plugins。可以添加settings.xml <pluginGroups>下的<pluginGroup>元素,增加默认groupId。

artifactId默认根据插件简称和本地仓库中groupId对应的路径下maven-metadata.xml中匹配<prefix>元素确定插件前缀,进而知道和它同一级的<artifactId>。

version默认根据artifactId对应的路径下maven-metadata.xml文件<release>元素确定。

 

 

 

坐标和依赖

何为Maven坐标

世界上任何一个构件(artifact)的唯一标识,Maven坐标的元素包括:groupId、artifactId、version、packaging、classfier。

 

坐标详解

groupId:定义当前Maven项目所属的实际项目。Maven项目和实际的一个项目不一定是一对一的关系。比如SpringFramework这个实际项目,其对应的Maven项目会有很多,如spring-core、spring-context等。这些Maven项目相当于实际项目中划分好的模块。表示方式和Java包名表示类似,域名反向。

artifactId:定义实际项目中的Maven项目(实际项目划分好的模块)。推荐使用实际项目名作为artifactId的前缀,这样做的好处是方便寻找实际构件。

version:定义Maven项目当前所处的版本。需要注意的是,Maven定义了一套完整的版本规范,以及快照的概念。

packaging:定义Maven项目的打包方式。通常和生成的构件的扩展名对应,packaging为jar,最终文件名为artifactId-version.jar,使用war打包方式的Maven项目,最终生成一个.war构件。不过这不是绝对的,比如值为maven-plugin的构件扩展名为jar。打包方式会影响到构建的生命周期,比如jar打包和war打包会使用不同的命令。packaging不定义,Maven会使用默认值jar。

classfier:帮助定义构建输出的一些附属构件。附属构件与主构件相对应,例主构件是nexus-core-2.0.jar,该项目可能还会通过使用一些插件生成如nexus-core-2.0-sources.jar、nexus-core-2.0-javadoc.jar这样一些附属构件,其包含了Java源码和文档。这时候,sources和javadoc就是这两个附属构件的classfier。这样,附属构件也就拥有了自己唯一的坐标。

上述5个元素中,groupId、artifactId、version是必须定义的,packaging是可选的,classfier不能直接定义,通过命令行传参设定。

 

依赖的配置

<project>

<dependencies>

   <dependency>

      <groupId></groupId>

      <artifactId></artifactId>

      <version></version>

      <type></type>

      <scope></scope>

      <optional></optional>

      <exclusions>

         <exclusion>

           

         </exclusion>

      </exclusions>

   </dependency>

</dependencies>

</project>

每个依赖可以包含的元素有:

groupId、artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到需要的依赖。

type:依赖的类型,和项目坐标的packaging对应。通常情况下不必声明,其默认为jar。

scope:依赖的范围。

optional:标记依赖是否可选。

exclusions:排除传递性依赖。

 

依赖范围

JUint依赖的测试范围是test,测试范围用scope元素表示,下面将详解什么是测试范围,以及各种范围的效果和用途。

首先要知道,Maven在编译项目主代码的时候需要用一套classpath,用到spring项目中,项目主代码需要用到spring-core,该文件以依赖的方式引入到classpath中。其次,Maven在编译、测试时会使用另外一套classpath。

依赖范围就是用来控制依赖与三种classpath(编译classpath、测试classpath、运行classpath)的关系,Maven有以下几种依赖范围:

compile:编译依赖范围。没有指定的话,就默认用compile。典型例子是spring-core,三种情况都需要使用该依赖。

test:测试依赖范围。典型例子是JUnit,它只在编译测试代码、运行测试时才需要。

provided:已提供的依赖范围。典型例子是servlet-api,编译和测试项目的时候需要该依赖,但运行项目时,由于容器已经提供,就不需要Maven重复引入。

runtime:运行时依赖范围。典型例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或运行项目时才需要实现上述接口的具体JDBC驱动。

system:系统依赖范围。和provided依赖范围完全一致。但是使用时必须通过systemPath元素显式地指定依赖文件路径。由于依赖文件不是通过Maven解析的,而且往往与本机绑定,可能造成构建的不可移植,因此谨慎使用。systemPath元素可以引用环境变量:

<dependency>

   <groupId>java.sql</groupId>

   <artifactId>jdbc-stdext</artifactId>

   <version>2.0</version>

   <scope>system</scope>

   <systemPath>${java.home}/lib/rt.jar</systemPath>

</dependency>

import:导入依赖范围。该依赖范围不会对三种classpath产生实际影响,会在POM继承时,对依赖的完全继承起作用。

scope

编译classpath

测试classpath

运行classpath

例子

compile

Y

Y

Y

spring-core

test

-

Y

-

JUnit

provided

Y

Y

-

servlet-api

runtime

-

Y

Y

JDBC驱动实现

system

Y

Y

-

Maven之外的类文件

 

 

传递性依赖

构建一个spring项目,都会导入spring-core构件,在spring-core构件的POM文件包含了一个commons-logging依赖,

<dependency>

   <groupId>commons-logging</groupId>

   <artifactId>commons-logging</artifactId>

   <version>1.1</version>

   <scope></scope>

</dependency>

该依赖没有声明依赖范围,那么默认就是compile,项目p1对spring-core的依赖范围是compile。那么commons-logging就会成为p1的compile范围依赖,准确的说:传递性依赖。

有了此机制,在使用spring的时候就不用考虑它依赖了什么,也不用担心引入多余依赖。Maven会解析各个直接依赖,将那些必要的间接依赖,以传递性依赖形式引入到当前项目中。

依赖范围不仅可以控制三种classpath的关系,还对传递性依赖产生影响。

1             2

compile

test

provided

runtime

compile

compile

-

-

runtime

test

test

-

-

test

provided

provided

-

provided

provided

runtime

runtime

-

-

runtime

-为依赖不能传递

 

 

依赖调解

当项目A有这样的依赖关系:A->B->C->X(1.0)、A->D->X(2.0)

两个X依赖的groupId和artifactId相同,但是version不同。

Maven依赖调解(dependency mediation)的第一原则是:路径最近者优先。该例中X(1.0)的路径长度为3,而X(2.0)的路径长度为2,因此X(2.0)会被解析使用。

依赖调解第一原则不能解决所有问题,比如这样的依赖关系:A->B->Y(1.0)、A->C->Y(2.0),Y(1.0)和Y(2.0)的依赖路径长度是一样的,都是2。

Maven依赖调解的第二原则是:第一声明者优先。在依赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用,顺序最靠前的会被依赖。

 

 

可选依赖

项目A依赖于项目B,项目B对于项目X和项目Y的依赖都是可选依赖:A->B、B->X(可选)、B->Y(可选)。

由于X、Y是可选依赖,依赖将不会得以传递。换句话说,X、Y将不会对A有任何影响。

为什么要使用可选依赖这一特性呢?可能项目B实现了两个特性,其中一个特性依赖于X,特性二依赖于Y,并且这个特性是互斥的,用户不可能同时使用两个特性。比如B是一个持久层隔离工具包,它支持多种数据库,包括MySQL PostgreSQL等,在构建B时,需要这两种数据库的驱动,但在使用这个工具包的时候,只会依赖一种数据库。

<dependency>

   <groupId>mysql</groupId>

   <artifactId>mysql-connector-java</artifactId>

   <version>5.1.10</version>

   <optional>true</optional>

</dependency>

<dependency>

   <groupId>postgresql</groupId>

   <artifactId>postgresql</artifactId>

   <version>8.4</version>

   <optional>true</optional>

</dependency>

当项目A依赖于项目B时,如果其实际基于MySQL数据库,那么在项目A中就需要显式地声明mysql-connector-java这一依赖。

最后,关于可选依赖需要说明一点:在理想情况下,是不该使用可选依赖的。使用可选依赖的原因是某一个项目实现了多个特性,在面向对象设计中,有个单一职责性原则,这个原则在规划Maven项目时也同样适用。在上面的例子中,更好的做法是为MySQL和PostgreSQL分别创建一个Maven项目,基于同样的groupId分配不同的artifactId。

 

 

最佳实践

排除依赖

传递性依赖会给项目隐式地引入很多依赖,极大简化了项目依赖的管理。但是有些时候这种特性会带来问题。

例如,当前项目有一个第三方依赖,而这第三方由于某些原因依赖了另外一个类库的SNAPSHOT版本,那么这个SNAPSHOT就会成为当前项目的传递性依赖,SNAPSHOT的不稳定性会直接影响到当前项目。这时候,应该排除掉该SNAPSHOT,并在当前项目中声明该类库的某个正式版本。

你可能想替换某个传递性依赖,比如Sun JTA API,Hibernate依赖于这个jar,但是由于版权因素,该类库不能在中央仓库中,而Apache Geronimo项目有一个对应的实现。这时就可以排除Sun JTA API,再声明Geronimo的JTA API实现,

<dependency>

   <groupId>com.gome.qt</groupId>

   <artifactId>project-a</artifactId>

   <version>1.0</version>

   <scope></scope>

   <exclusions>

         <exclusion>

            <groupId>com.gome.qt</groupId>

            <artifactId>project-b</artifactId>

         </exclusion>

   </exclusions>

</dependency>

<dependency>

   <groupId>com.gome.qt</groupId>

   <artifactId>project-b</artifactId>

   <version>1.1</version>

</dependency>

注意:声明exclusion的时候只需要groupId和artifactId,而不需要version,因为只需要groupId和artifactId就能唯一定位依赖图中的依赖。

 

 

归类依赖

使用常量不仅让代码变得更简洁,更重要的是可以避免重复,同时也降低错误发生率。

同理,对于spring项目A,也应该在一个唯一的地方定义版本,在dependency声明中引用这一版本。

<project>

   <properties>

      <spring.version>2.5.6</spring.version>

   </properties>

   <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-core</artifactId>

      <version>${spring.version}</version>

   </dependency>

</project>

 

用<properties>定义Maven属性,定义好后,Maven运行时会将POM中的所有${spring.version}替换成2.5.6。用${}环绕的方式引用Maven属性。

 

 

优化依赖

查看当前项目的已解析依赖:

mvn dependency:list

查看当前项目的依赖树(有层次)

mvn dependency:tree

更详细的依赖关系

mvn dependency:analyze

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值