一. 什么是maven
基于java的一款跨平台的项目构建、依赖管理、项目信息管理的工具。
二.maven安装和配置
2.1 windows安装maven
1.检查jdk是否安装 java -version
2.下载解压maven
https://maven.apache.org/download.cgi
下载后解压到指定目录(如:D:\java\apache-maven-3.5.0)
设置环境变量:
在系统变量中添加变量名为 MAVEN_HOME,内容是解压的maven文件路径。
然后在Path中添加 %MAVEN_HOME%\bin
然后运行mvn -v 验证是否安装成功
2.2 在unix系统上安装Maven
首先下载Maven并解压,并移动到usr/local目录下
$ wget http://[mirror.bit.edu.cn/apache](http://mirror.bit.edu.cn/apache)/maven/maven-3/3.2.3/binaries/apache-maven-3.2.3-bin.tar.gz
$ tar vxf apache-maven-3.2.3-bin.tar.gz
$ mv apache-maven-3.2.3 /usr/local/maven3
修改环境变量,在/etc/profile中添加以下几行
MAVEN_HOME=/usr/local/maven3
export MAVEN_HOME
export PATH=${PATH}:${MAVEN_HOME}/bin
记得执行source /etc/profile
使环境变量生效。
最后运行mvn -v
验证maven是否安装成功,如果安装成功会打印如下内容
Apache Maven 3.2.3 (33f8c3e1027c3ddde99d3cdebad2656a31e8fdf4; 2014-08-12T04:58:10+08:00)
Maven home: /usr/local/maven3
Java version: 1.7.0_65, vendor: [Oracle](https://www.linuxidc.com/topicnews.aspx?tid=12 "Oracle") Corporation
Java home: /usr/lib/jvm/java-7-openjdk-amd64/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.13.0-35-generic", arch: "amd64", family: "unix"
2.3 ~./m2 和 setting.xml
在我们在安装好maven后,在用户目录下会发现.m2文件夹, 也是本地仓库的位置(idea 有时加了 -U参数,仓库里是拉下来了最新的SNAPSHOT,但是idea里拿不到最新的代码,可以到本地仓库中删除后重新拉取就可以了)
setting.xml的配置在 默认的模板上修改配置即可。
安装maven后,在maven安装目录的conf文件下会有setting.xml文件,但是一般需要复制setting.xml文件到~/.m2/setting.xml(最佳实践)
默认M2_HOME/conf/setting.xml是全局配置,对于登录不同用户都将都受到影响,而在~/.m2/setting.xml指影响登录用户,同时便于升级maven。
三.maven依赖
3.1maven pom文件的结构
maven的整个构建都是基于pom.xml
<?xml version=``"1.0"` `encoding=``"UTF-8"``?>
<project xmlns=``"[http://maven.apache.org/POM/4.0.0](http://maven.apache.org/POM/4.0.0)"
xmlns:xsi=``"[http://www.w3.org/2001/XMLSchema-instance](http://www.w3.org/2001/XMLSchema-instance)"
xsi:schemaLocation=``"[http://maven.apache.org/POM/4.0.0](http://maven.apache.org/POM/4.0.0) [http://maven.apache.org/xsd/maven-4.0.0.xsd](http://maven.apache.org/xsd/maven-4.0.0.xsd)"``>
<!--声明项目描述符遵循哪一个POM模型版本。模型本身的版本很少改变,虽然如此,但它仍然是必不可少的,这是为了当Maven引入了新的特性或者其他模型变更的时候,确保稳定性。-->
<modelVersion>``4.0``.``0``</modelVersion>
<!--项目的全球唯一标识符,通常使用全限定的域名区分该项目和其他项目。并且构建时生成的路径也是由此生成, 如com.mycompany.app生成的相对路径为:/com/mycompany/app-->
<groupId>com.miui.media.auto</groupId>
<!-- 构件的标识符,它和group ID一起唯一标识一个构件,项目下的某个模块 -->
<artifactId>auto-maven</artifactId>
<!--项目产生的构件类型,例如jar、war、pom。插件可以创建他们自己的构件类型,所以前面列的不是全部构件类型-->
<packaging>pom</packaging>
<!--项目版本-->
<version>``1.0``-SNAPSHOT</version>
<!--项目的名称, Maven产生的文档用-->
<name>auto-maven</name>`
<!--模块(有时称作子项目) 被构建成项目的一部分。列出的每个模块元素是指向该模块的目录的相对路径-->
<modules>
<module>auto-maven-base</module>
</modules>
<!--项目描述-->
<description>maven 例子</description>
<!--父项目的坐标。如果项目中没有规定某个元素的值,那么父项目中的对应值即为项目的默认值。 坐标包括group ID,artifact ID和 version。-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<!-- 父项目的pom.xml文件的相对路径。相对路径允许你选择一个不同的路径。默认值是../pom.xml。Maven首先在构建当前项目的地方寻找父项 目的pom,其次在文件系统的这个位置(relativePath位置),然后在本地仓库,最后在远程仓库寻找父项目的pom。-->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--项目属性-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 继承自该项目的所有子项目的默认依赖信息。这部分的依赖信息不会被立即解析,而是当子项目声明一个依赖(必须描述group ID和 artifact ID信息),如果group ID和artifact ID以外的一些信息没有描述,则通过group ID和artifact ID 匹配到这里的依赖,并使用这里的依赖信息。-->
<dependencyManagement>
<dependencies>
</dependencies>
</dependencyManagement>
<!--在列的项目构建profile,如果被激活,会修改构建处理-->
<profiles>
</profiles>
<!--构建项目需要的信息-->
<build>
<!--子项目可以引用的默认插件信息。该插件配置项直到被引用时才会被解析或绑定到生命周期 -->
<pluginManagement>
</pluginManagement>
<!--使用的插件列表 。-->
<plugins>
</plugins>
</build>
<!--项目分发信息,在执行mvn deploy后表示要发布的位置。有了这些信息就可以把网站部署到远程服务器或者把构件部署到远程仓库。-->
<distributionManagement>
<repository>
<id>archiva.internal</id>
<name>Internal Release Repository</name>
<url>http:``//nexus.d.xiaomi.net/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>archiva.snapshots</id>
<name>Internal Snapshot Repository</name>
<url>http:``//nexus.d.xiaomi.net/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
</project>
3.2坐标和依赖
maven坐标:
groupId, artifactId, version,packaging,classifier。这些组合的标识符拼成了一个项目的坐标,就像任何其它的坐标系统,一个Maven坐标是一个地址,即“空间”里的某个点:从一般到特殊。当一个项目通过依赖,插件或者父项目引用和另外一个项目关联的时候,Maven通过坐标来精确定位一个项目。Maven坐标通常用冒号来作为分隔符来书写,像这样的格式:groupId:artifactId:packaging:version
**groupId **
d 团体,公司,小组,组织,项目,或者其它团体。团体标识的约定是,它以创建这个项目的组织名称的逆向域名(reverse domain name)开头。来自Sonatype的项目有一个以com.sonatype开头的groupId,而Apache Software的项目有以org.apache开头的groupId。
**artifactId **
在groupId下的表示一个单独项目的唯一标识符。
**version **
一个项目的特定版本。发布的项目有一个固定的版本标识来指向该项目的某一个特定的版本。而正在开发中的项目可以用一个特殊的标识,这种标识给版本加上一个“SNAPSHOT”的标记。
项目的打包格式也是Maven坐标的重要组成部分,但是它不是项目唯一标识符的一个部分。一个项目的groupId:artifactId:version使之成为一个独一无二的项目;你不能同时有一个拥有同样的groupId, artifactId和version标识的项目。
**packaging **
项目的类型,默认是jar,描述了项目打包后的输出。类型为jar的项目产生一个JAR文件,类型为war的项目产生一个web应用
**classifier **
该元素用来帮助定义一些构建项目输出的一些附属构建,如javadoc.jar或者sources.jar等
项目构件的文件名域坐标相对应,一般的规则为artifactId-version[-classifier].packaging
依赖配置:
groupId、artifactId、version为对应的坐标
type:依赖的类型,对应于packaging,默认为jar
scop:依赖范围
用来控制依赖在测试、编译和运行时classpath中应该依赖哪些包,maven有以下几种依赖范围
1. compile 默认的范围,编译测试运行都有效。 (spring-core)
2. provided 编译和测试时有效,最后是在运行的时候不会被加入。比如在JavaEE web项目中我们需要使用servlet的API,但是呢Tomcat中已经提供这个jar,我们在编译和测试的时候需要使用这个api,但是部署到tomcat的时候,如果还加入servlet构建就会产生冲突,这个时候就可以使用provided。
3. runtime 在测试和运行时有效。 (jdbc驱动包mysql-connector-java )
4. test 在测试时有效。 (junit)
5. system 与本机系统相关联,可移植性差。编译和测试时有效。 使用时必须通过systemPath来显示指定文件路径
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency
6. import 导入的范围,它只在使用dependencyManagement中,表示从其他pom中导入dependecy的配置。
optional:标记依赖是否可选 A→B, B->X(optional),B→Y (optional) 这时X和Y不会传递过来 如果A要用有关X和Y的功能,得独立依赖进来。
exclusions:排除传递性依赖 (commons-logging silf4j会找不到日志入口)
3.3 传递性依赖
举例:
我们的项目依赖于server,apus-server也有他自己的依赖(下图左),当我们的项目依赖了server,apus的依赖也会对应的传递过来(下图右)
A→B, B->C,B→D C和D会传递到A的依赖集里面(前提是C和D没有被裁剪掉)
3.4 依赖范围对依赖传递的影响
假设A依赖于B,B依赖于C,我们定A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖,下图左一列代表第一直接依赖范围,最上面一行代表
第二直接依赖范围,中间的相交内容代表传递性依赖范围
3.5 依赖调解
因为依赖具有传递性,假如对于A同时直接依赖了B和C,B依赖于D的1.0.0版本,C依赖于D的2.0.0版本(无论是直接依赖还是间接依赖),如果两个版本都依赖都定会报错,造成重复依赖
这时A对于D的传递性依赖到底是1.0.0还是2.0.0?
依赖调解原则一:路径近者优先
A->B->C→X(2.0) 路径为3
A->D→X(1.0) 路径为2
这时A→X(1.0)
那对于A->B→Y(1.0), A->D->Y(2.0)呢?
依赖调解原则二:先声明者优先 (v2.0.9开始)
如果在denpendencies中先声明了B则依赖于Y(1.0),反之依赖于Y(2.0)
3.6优化依赖
maven三剑客:
dependency:list 获取解析后的依赖(并列显示,分不清是怎么依赖进来的)如下
[图片上传失败...(image-67ec4f-1606528449098)]
dependency:tree (可以清晰的看到依赖树,某个依赖是通过怎样的路径传递进来)
[图片上传失败...(image-f712cc-1606528449098)]
如果加-Dverbose可以看到所有的依赖传递,和maven是怎么依赖调解的
[图片上传失败...(image-4017dc-1606528449098)]
当然还可以加一些类似搜索的参数Dincludes或者Dexcludes,来满足查找需求
dependency:analyze
[图片上传失败...(image-33e1c8-1606528449098)]
我们看到结果是有2部分组成, Used undeclared dependencies found:是指我们没有在项目中显示的声明依赖,但是我们在项目中在使用,
这种就会存在一些潜在的风险,如上图中的org.apache.thrift:thrift:jar:0.5.0-mdf2.0.0:compile,我们在可能在新依赖的包的时候会覆盖掉,但是
新的版本里没有原来版本的接口,可能会出错,对于这类我们可以显示的声明依赖
另一部分是 Unused declared dependencies found: 显示声明但是未使用,不能一股脑直接删除(analyze只会分析编译主代码和测试代码需要的依赖),
可能在运行的时候需要这些包
可以用插件来声明白名单:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<ignoredUsedUndeclaredDependencies>
<ignoredUsedUndeclaredDependency>org.springframework:spring-beans</ignoredUsedUndeclaredDependency>
<ignoredUsedUndeclaredDependency>org.springframework:spring-context-support</ignoredUsedUndeclaredDependency>
</ignoredUsedUndeclaredDependencies>
<ignoredUnusedDeclaredDependencies>
<ignoredUnusedDeclaredDependency>com.meituan.service.mobile:mtthrift</ignoredUnusedDeclaredDependency>
</ignoredUnusedDeclaredDependencies>
</configuration>
</plugin>
四. maven仓库
4.1maven仓库的布局
groupId/artifactId/version/artifactId-version.packaging
4.2maven仓库分类
本地仓库一般在~./m2/repository 也可以在setting文件中用localRepository显示的指定
1.私服:
私服的优点:
节省外网的带宽,加速maven的构建,部署第三方构件,提高稳定性,增强控制(权限管理),降低中央仓库的负荷
2.远程仓库的配置
`<repositories>`
`<repository>`
`<id>central</id>`
`<url>http:``//nexus</url>`
`<releases>`
`<enabled>``true``</enabled>`
`<updatePolicy>daily</updatePolicy>`
`<checksumPolicy></checksumPolicy>`
`</releases>`
`<snapshots>`
`<enabled>``true``</enabled>`
`</snapshots>`
`</repository>`
`</repositories>`
其中releases和snapshots中的enabl可以配置true和false代表是否支持下载,updatePolicy更新频率
默认daily每天更新一次,never从不更新,always每次构建都更新,interval:X 每隔X分钟更新一次
<pre style="margin: 10px 0px 0px; padding: 0px; font-family: ConfluenceInstalledFont, monospace;">checksumPolicy校验策略默认为warn, 还有ignore和fail</pre>
注意:maven自带的远程仓库id为central,如果在配置远程仓库使用了这个id,就会覆盖中央仓库的配置
3.远程仓库认证
`<servers>`
`<server>`
`<id>deploy</id>`
`<username>admin</username>`
`<password>admin123</password>`
`</server>`
`<server>`
`<id>archiva.internal</id>`
`<username>renfeiwei</username>`
`<password>***</password>`
`</server>`
`<server>`
`<id>archiva.snapshots</id>`
`<username>renfeiwei</username>`
`<password>***</password>`
`</server>`
`</servers>`
必须配置在setting中,不能配置在pom中,id需要和pom中的distributionManagement 中respository对应上
4.部署项目到远程仓库 deploy
pom中配置
`<distributionManagement>`
`<repository>`
`<id>archiva.internal</id>`
`<name>Internal Release Repository</name>`
`<url>http:``//nexus.d.xiaomi.net/nexus/content/repositories/releases/`
`</url>`
`</repository>`
`<snapshotRepository>`
`<id>archiva.snapshots</id>`
`<name>Internal Snapshot Repository</name>`
`<url>http:``//nexus.d.xiaomi.net/nexus/content/repositories/snapshots/`
`</url>`
`</snapshotRepository>`
`</distributionManagement>`
5.从远程仓库解析依赖的机制
对于nsapshot
6.镜像
`<mirrors>`
`<mirror>`
`<!--This sends everything ``else` `to /``public` `-->`
`<id>nexus</id>`
`<mirrorOf>*</mirrorOf>`
`<url>http:``//nexus.d.xiaomi.net/nexus/content/groups/public</url>`
`</mirror>`
`</mirrors>`
7.仓库搜索 https://mvnrepository.com/
五. 生命周期和插件
5.1 什么是生命周期
在执行maven命令时,其实都对应了一个生命周期,对应的生命周期,maven都做了什么呢,其实每个生命在周期,maven都绑定了默认的插件来完成相应的目标
每个生命周期都可以绑定一个或多个插件行为,如下图编译实际是由mavne-compiler-pluin完成,测试是由maven-surefire-plugin完成
5.2 三套生命周期
5.2.1 clean生命周期
clean生命周期的目的是清理项目,包含3个阶段
1)pre-clean执行一些清理前需要完成的工作。
2)clean清理上一次构建生成的文件
3)post-clean执行清理后需要完成的工作
5.2.2 default生命周期
5.2.3site生命周期
5.3 命令行与生命周期
mvn clean 执行celan生命周期的pre-clean和clean阶段
mvn clean package 执行clean生命周期pre-clean和clean阶段,default生命周期的validate到package阶段
mvn clean deploy
5.4 插件目标和插件绑定
插件绑定:生命周期的阶段与插件的目标相互绑定,完成某个具体的构建任务
5.5 插件的内置绑定和自定义绑定
clean生命周期的clean阶段,删除项目输出目录
打jar包的default生命周期内置的插件绑定
自定义绑定
`<build>`
`<plugins>`
`<plugin>`
`<artifactId>maven-source-plugin</artifactId>`
`<version>``3.0``.``1``</version>`
`<executions>`
`<execution>`
`<id>attach-sources</id>`
`<phase>``package``</phase>`
`<goals>`
`<goal>jar</goal>`
`</goals>`
`</execution>`
`</executions>`
`</plugin>`
`</plugins>`
`</build>`
有些插件默认有绑定的生命周期,所以我们看到有时侯不需要指明phase,可以用下面的命令查看插件绑定的phase信息
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:3.0.1 -Ddetail
5.6 插件配置
命令行插件配置
-D参数值=value,例如maven-surefire-plugin 的skipTests(不执行也不编译), maven.test.skip(不执行但是会编译)
mvn clean package -DskipTests=true
pom中插件配置
`<plugin>`
`<artifactId>maven-compiler-plugin</artifactId>`
`<configuration>`
`<source>${java.version}</source>`
`<target>${java.version}</target>`
`<encoding>UTF-``8``</encoding>`
`</configuration>`
`</plugin>`
`<plugin>`
`<groupId>org.apache.maven.plugins</groupId>`
`<artifactId>maven-enforcer-plugin</artifactId>`
`<executions>`
`<execution>`
`<id>enforce-ban-duplicate-classes</id>`
`<goals>`
`<goal>enforce</goal>`
`</goals>`
`<configuration>`
`<fail>``true``</fail>`
`<failFast>``true``</failFast>`
`<rules>`
`<bannedDependencies>`
`<searchTransitive>``true``</searchTransitive>`
`<message>Dependecy Check Failed!</message>`
`<excludes>`
`<exclude>org.springframework:spring-beans:(,``4.1``.``2``.RELEASE]</exclude>`
`<exclude>commons-logging:commons-logging</exclude>`
`</excludes>`
`</bannedDependencies>`
`<banDuplicateClasses>`
`<ignoreClasses>`
`<ignoreClass>javax.*</ignoreClass>`
`<ignoreClass>org.apache.commons.logging.*</ignoreClass>`
`<ignoreClass>org.apache.shiro.*</ignoreClass>`
`<ignoreClass>org.objectweb.asm.*</ignoreClass>`
`<ignoreClass>org.aspectj.*</ignoreClass>`
`</ignoreClasses>`
`<findAllDuplicates>``true``</findAllDuplicates>`
`</banDuplicateClasses>`
`</rules>`
`</configuration>`
`</execution>`
`</executions>`
`<dependencies>`
`<dependency>`
`<groupId>org.codehaus.mojo</groupId>`
`<artifactId>extra-enforcer-rules</artifactId>`
`<version>${extra-enforcer-rules.version}</version>`
`</dependency>`
`</dependencies>`
`</plugin>`
项目中常用插件:
maven-enforcer-plugin
maven-dependency-plugin
maven-clean-plugin
maven-compiler-plugin
maven-source-plugin
maven-war-plugin
maven-jar-plugin
maven-surefire-plugin
maven-resources-plugin
spring-boot-maven-plugin
六. 聚合与继承
6.1 聚合与继承
聚合
`<modules>`
`<module>auto-comment-domain</module>`
`<module>auto-comment-dao</module>`
`<module>auto-comment-manager</module>`
`<module>auto-comment-service</module>`
`<module>auto-comment-common</module>`
`<module>auto-comment-client</module>`
`<module>auto-comment-server</module>`
`<module>auto-comment-client-test</module>`
`</modules>`
继承
`<parent>`
`<artifactId>auto-comment</artifactId>`
`<groupId>com.miui.media.auto</groupId>`
`<version>``0.0``.``7``-SNAPSHOT</version>`
`</parent>`
`<modelVersion>``4.0``.``0``</modelVersion>`
`<artifactId>auto-comment-client</artifactId>`
可继承的pom元素
6.2 反应堆
(反应堆内的继承和依赖)影响项目的构建实顺序
反应堆外的依赖,一定靠maven仓库,在编译前本地必须有;反应堆内的依赖,本地仓库可以没有,是靠这次编译的结果的
裁剪反应堆:例如mvn clean deploy -pl auto-comment-client -am -rf ../pom.xml
总结:结合实际使用的经验来分析,还需要多用,多总结。