maven
1、maven介绍
Maven
,Ant
,Gradle
都是项目管理构建工具;- 作为
apache
的一个颇为成功的开源项目,maven
主要服务于Java
平台的项目构建、依赖管理、项目信息管理
2、maven环境的搭建
- 下载安装包,解压
- 配置环境变量
- mvn -v 验证是否成功
3、maven常用的命令
// 查看版本
mvn -v
// 清除target(项目生成的字节码文件)
mvn clean
// 编译项目 .java 文件转换为.class文件
mvn compile
// 测试,测试之前已经进行了 mvn compile
// 测试的类必须以Test结尾
mvn test
// 打包项目
mvn package
// 安装jar包到项目中
mvn install
4、坐标依赖
1、依赖的配置
<dependency>
<groupId>com.zhao.MQTT</groupId> // 包名一般是公司的网址 + 项目名称
<artifactId>MQTT-Login</artifactId>// 一般是项目名称 + 模块功能名称
<version>1.0-SNAPSHOT</version> // 版本
<type>.....</type> // 依赖的类型,对应于项目坐标定义的packageing,大部分情况下不用申明,默认是jar
<scope>....</scope>// 依赖的范围
<optional>...</optional> // 标记依赖是否可选
<exclusions> // 排除传递性依赖
<exclusion>
......
</exclusion>
</exclusions>
</dependency>
2、依赖范围
依赖范围就是用来控制依赖与三种classpath(编译classpath、测试classpath、运行classpath)的关系,maven有以下几种依赖范围:
- compile
编译依赖范围。
如果没有指定,就会默认使用该依赖范围。使用此依赖范围的maven依赖,对于编译,测试,运行三种classpath都有效。典型的例子就是spring-core,在编译、测试和运行的时候都需要使用该依赖
- test
测试依赖范围。
使用次依赖范围的maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。典型的例子就是JUnit,它只有在编译测试代码及运行测试的时候裁需要
- provided
已提供依赖范围。
使用次依赖范围的maven依赖,只对于编译和测试classpath有效,但是运行时无效。
- rumtime
运行时依赖范围。
对于测试和运行classpath有效,但是在编译主代码时无效。典型的例子就是JDBC驱动的实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候裁需要实现上述接口的具体JDBC驱动。
- system
- import
3、传递性依赖
A依赖于B,B依赖于C,那么C就是A的一个传递性依赖;
依赖范围不仅可以用来控制依赖和三种classpath的关系,而且还对传递性依赖产生影响。
假设A依赖于B,B依赖于C,那么我们说A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围
4、依赖调解
maven引入的传递性依赖机制,一方面大大简化和方便了依赖申明,另一方面,大部分情况下我们只需要关心项目的直接依赖是什么,而不是考虑这些直接依赖会引入什么传递性依赖。但是有时候,当传递性依赖造成问题的时候,我们就需要清除的知道该传递性依赖是从那条依赖路径引入的。
- 例子
项目A有这样的例子:A->B->C->X(1.0),A->D->X(2.0),X是A的传递性依赖,但是两个依赖路径上有两个版本的X,那么那个X会被maven解析使用呢?
两个版本的依赖都被解析显然是不对的,因为那样的话会造成依赖重复,因此必须选择一个。
- 原则
maven依赖调解机制的第一原则是:路径最者这优先,该例子中X(1.0)的路径长度是3,X(2.0)的长度是2,因此X(2.0)会被解析使用。
mavne的依赖调解机制的第一原则不能解决所有的问题,比如这样的依赖关系:
A->B->Y(1.0),A->C->Y(2.0),依赖路径都是2,那么到底谁会被解析使用呢?在maven之前的版本中,这是不确定的,但是在maven2.0.9版本后,为了尽可能的避免构建的不确定性,maven定义了依赖的调解的第二原则:第一申明者优先,在依赖长度相等的情况下,在pom
表中依赖生命的顺序决定了谁会被解析使用。顺序最靠前的那个依赖优胜。例子中:如果B的依赖申明在C之前,那么Y(1.0)就会被解析使用。
5、可选依赖
项目A依赖于项目B,项目B依赖于项目X和Y,B对于X和Y的依赖都是可选依赖:A->B、B->X(可选),B->Y(可选)。根据依赖性传递的定义,如果这个三个依赖的范围都是compile,那么X和Y就是A的compile范围传递性依赖。
但是这里的X、Y是可选依赖了,依赖将不会得以传递。换句话说:X,Y将不会对A有任何影响。
- 为什么使用可选依赖这一特性呢?
可能项目B实习县了两个特性,其一依赖于X,其二依赖于Y,而且这两个特性是互斥的,用户不可能同时使用两个特性;
比如B是一个持久层隔离工具包,它支持多种数据库,包括mysql,oracle,在构建的时候需要这两种数据库的驱动,但是在使用这个工具包的时候,只需要某一个驱动;
实际的应用中,我们应该尽可能的避免可选依赖的出现
6、排除依赖
传递性依赖会给项目隐式地引入很多依赖,这极大地假话了项目依赖的管理,但是有些时候这种特性也会带来问题,例如当前鲜蘑菇中有一个第三方依赖,而这个依赖由于某些原因依赖了另外一个类库的snapashot,那么这个snapshot就会成为当前项目的传递性依赖,而snpshot的不稳定性会直接影响到当前的项目。
有些时候你也可能想替换掉某个传递性依赖,换成自己想要的依赖。比如 sun jta api,hiberanate依赖与这个jar,但是由于版权的因素,该类库不在中央仓库中,而apache geronimo项目有一个对应的实现,这时候我们就可以排除sun jat api,申明Geronimo的jta api实现。
7、归类依赖
很多关于spring framework的依赖,它们分别是org.springframework:spring-core:2.5.6、org.springframework:spring-context-support:2.5.6,它们都是来自同一个项目的不同模块。因此浙西恶意来的版本都是相同的,而且是可以预见的,如果将来升级spring framework,这些依赖的版本会一起升级。
如下
<properties>
<spring.version>4.3.9.RELEASE</spring.version>
</properties>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
5、仓库
对于mavne来说:仓库只分为两类:本地仓库和远程仓库。当maven根据左边寻找构建的时候,首先回去本地仓库,如果没有回去中央仓库,下载到本地,然后使用。
- 仓库
- 本地仓库
- 远程仓库
- 镜像仓库:所谓的镜像仓库就是一个提供了和中央仓库同样功能,但是下载速度快了很多
1、本地仓库
如何修改本地仓库地址?
- 找到setting.xml
<localRepository>/path/to/local/repo</localRepository>// 修改本地仓库的地址
2、远程仓库
每个用户只有一个本地仓库,但是可以访问配置多个远程仓库
3、中央仓库
4、私服
私服是一种特殊的远程仓库,是架设在局域网内部的仓库,私服代理广域网上的远程仓库,工局域网内的maven用户使用。当maven需要下载构建的时候,它从私服请求,如果私服上不存在该构建,则从外部的远程仓库下载。缓存在私服上之后,在为maven的下载请求提供服务。
一些无法从外部仓库下载到的构建也能从本地上传到私服上供我们使用
- maven使用私服是一个很好的习惯
节省外网资源、
加速构建、
部署第三方构建、
提高稳定性、
降低中央仓库的负荷
5、远程仓库的配置
很多情况下,默认的中央仓库无法满足项目的需求,可能项目需要的构建存在于另一个远程仓库中,如JBoss Maven仓库中,这时,可以在pom表中配置该仓库,请注意实在pom表中
<repositories>
<repository>
<id>juziwl</id>
<name>Juzi Repository</name>
<url>http://192.168.129.198:8081/nexus/content/groups/public </url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
6、远程仓库的认证
大部分的远程仓库无需认证就可以访问,但是有时候处于安全方面的考虑,我们需要提供认证信息才能访问一些远程仓库。
例如:组织内部有一个maven仓库服务器,该服务器为每个项目都提供独立的maven仓库,为了防止非法的仓库访问,管理员为每个仓库提供了一组用户名及密码。这时候为了能让maven访问仓库内容,就需要配置认证信息。
配置认证信息和配置仓库信息不同,仓库信息可以直接配置在pom文件中,但是认证信息必须配置在settings.xml文件中。因为pom往往是被提交到代码仓库中共所有成员访问的,而settings.xml一般只放在本机。因此在settings.xml中配置认证信息更为安全。
<servers>
<server>
<id>juziwl3</id>
<username>admin</username>
<password>admin123</password>
</server>
</servers>
这里面的id很关键,必须和pom表中需要认证的repository元素的id完全一致。换句话说,正是这个id降认证信息与仓库配置联系在了一起。
7、部署至远程仓库
私服的最大用处就是部署第三方构建,包括组织内部生成的构建以及一些无法从外部仓库直接获取的构建,无论是日常开发中生成的构建,还是正式版本发布的构建,都需要部署到仓库中供其他团队成员使用。
maven除了能对项目进行编译、测试、打包之外,还可以将项目生成的构建部署到仓库中。需要在pom.xml中进行配置。
<distributionManagement>
<repository>
<id>juziwl</id>
<name>Juzi Repository</name>
<url>http://192.168.129.198:8081/nexus/content/groups/public</url>
</repository>
<distributionManagement>
<snapshotRepository>
<id>juziwl</id>
<name>Juzi SnapshotRepository</name>
<url>http://192.168.129.198:8081/nexus/content/groups/public</url>
</snapshotRepository>
</distributionManagement>
</distributionManagement>
distributionManagement包含repository和snapshotRepository子元素,前者表示发布版本构件的仓库,后者表示快照版本的仓库。
往远程仓库部署构建的时候认证的。配置需要在settings.xml进行配置
<servers>
<server>
<id>juziwl3</id>
<username>admin</username>
<password>admin123</password>
</server>
</servers>
然后命令行:
mvn clean deploy
如果项目的当前的版本是快照版本,则部署到快照版本仓库地址,否则就部署到发布版本仓库地址。
8、镜像
如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像,换句话说,任何一个可以从仓库Y获取的构建都可以从X获取。
-
settings.xml配置文件
关于镜像的一个更为常见的用法就是结合私服。由于私服可以代理任何外部的公共仓库(包括中央仓库),因此,对于组织内部的maven用户来说,使用给一个私服地址就等于使用了所有需要的外部仓库,这可以将配置集中到私服,从而简化maven本身的配置,在这种情况下,任何需要的构建都可以从私服获取,私服就是所有仓库的镜像。
- 镜像第二次配置
<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
该例子中:
6、生命周期和插件
6.1、三套生命周期
在maven中存在三套生命周期,每一套生命周期都是相互独立的,互不影响
cleanLifeCycle:清理的生命周期
clean
defaultLifeCycle:默认的生命周期
compile,test,package,install,deploy
siteLifeCycle:站点生命周期
site
之所以说是三套生命周期,是因为他们之间互不影响;
但是在一套生命周期之内执行后面的命令前面操作会自动执行;
6.2、命令行与生命周期
从命令执行mavne任务的最主要方式就是调用maven的生命周期阶段。需要注意的是,各个生命周期是相互独立的,而一个生命周期的阶段四有前后依赖关系的。
6.3、插件目标
maven的核心仅仅定义了抽象的生命周期,具体的任务是交由插件完成的,插件以独立的构建形式存在。
6.4、插件绑定
maven的生命周期与插件相互绑定,用以完成实际的构建任务。具体而言,是生命周期的阶段与插件的目标相互绑定,以完成某个具体的构建任务。例如项目编译这一任务,它对应了default生命周期的compile这一阶段,而maven-compile-plugin这一插件的compile目标能够完成该任务。因此将他们绑定,就能实现项目编译的目的。
6.5、内置绑定
6.6、自定义绑定
6.7、插件配置
完成了插件和生命周期的绑定之后,用户还可以配置插件目标的参数,进一步调整插件目标所执行的任务,以满足项目的需求。几乎所有的maven插件的目标都有一些可配置的参数,用户可以通过命令行和pom配置等方式来配置这些参数。
命令行插件配置
很多插件目标的参数都支持从命令行配置,用户可以在manve命令中使用-D参数,病伴随一个参数=参数值形式,来配置插件目标的参数。
例如:maven-surefire-plugin提供一个maven.test.skip的参数,当其值为true的时候,就会跳过执行测试。于是,在运行命令的时候,加上如下:
mvn install -Dmaven.test.skip=true
-D是java自带的,其功能就是通过命令行设置一个java系统属性,maven简单的重用了改参数,在准备插件的时候检查系统属性,便实现了插件参数的配置。
POM中插件全局配置
并不是所有的插件参数都适合从命令行配置,有些参数的值从项目创建的发哦项目发布都不会改变,或者说很少改变,对于这种情况,在POM文件中一次性配置就显然比重复在命令行输入要方便。
用户可以在申明插件的时候,对此插件进行一个全局的配置。也就是说,所有该基于该插件目标的任务,都会使用这些配置。例如,我们通常会需要配置maven-compiler-plugin 告诉它编译Java1.5版本的源文件,生成与JVM1.5兼容的字节码文件。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
7、聚合与继承
7.1、聚合
Maven聚合(或者称为多模块),是为了能够使用一条命令就构建多个模块,例如已经有两个模块,分别为account-email,account-persist,我们需要创建一个额外的模块(假设名字为account-aggregator,然后通过该模块,来构建整个项目的所有模块,accout-aggregator本身作为一个Maven项目,它必须有自己的POM,不过作为一个聚合项目,其POM又有特殊的地方,看下面的配置
<?xml version="1.0" encoding="UTF-8"?>
<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>demo</groupId>
<artifactId>demo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>demo-springboot</module>
<module>demo-java</module>
<module>demo-provider</module>
<module>demo-consumer</module>
<module>demo-generator</module>
</modules>
</project>
上面有一个特殊的地方就是