Maven学习笔记

Maven学习过程中所记的笔记

Maven依赖

Maven坐标

Maven通过坐标来精确定位一个项目。Maven坐标通常用冒号来作为分隔符来书写,像这样的格式:groupId:artifactId:packaging:version。 例如坐标可以表示为mavenbook:my-app:jar:1.0-SNAPSHOT.

groupId:项目组织唯一的标识符

artifactId:表示一个单独项目的唯一标识符。

packaging:项目的类型,默认是jar。它不是项目唯一标识符的一个部分。

version :一个项目的特定版本。

maven依赖范围

compile:默认,编译依赖范围。编译、测试、运行三种classpath

test:测试依赖范围。只对于测试classpath有效,对编译和运行无效

prodided:已提供依赖范围。对于编译和测试classpath有效,对运行无效

runtime:运行依赖范围。对于测试和运行classpath有效,对于编译主代码无效。

system:系统依赖范围。依赖与provided完全一致,但必须通过systemPath显示指定依赖文件路径

import:导入依赖范围。

传递性依赖

最左边一行是第一直接依赖范围,最上面一行是第二直接依赖范围。(以范围小的为准)

比如A->B->C,B在A中的依赖范围为compile,C在B中的依赖范围为test,查找下表compile所在行和test所在列,值为横线,表示A中无法传递依赖C,将直接丢弃。

如B中引入junit,scope为test,那A中是不会间接依赖到junit的,必须在A中显式定义junit或去掉B中的test scope。

依赖调解原则

1、路径最近者优先,以下情况路径短一点的X(1.0)会被解析使用。

A->B->C->X(2.0)

A->D->X(1.0)

2、第一声明者优先,在路径长度相等的情况下,在POM中的依赖申明顺序决定了谁会被解析使用。

聚合和继承

聚合

一条命令构建被聚合的模块,方便快速构建项目

1
2
3
4
<modules>
<module>son</module>
<module>daughter</module>
</modules>

继承

继承父模块配置,比如dependencies元素,消除重复配置

dependencies:元素即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承全部依赖

dependencyManagement:只是声明依赖,并不实际引入,因此子项目需要显式地声明需要用的依赖

1
2
3
4
5
<parent>
<artifactId>testmaven</artifactId>
<groupId>com.sankuai.meituan</groupId>
<version>1.0-SNAPSHOT</version>
</parent>

Maven仓库

settings.xml文件

settings.xml文件中有多个顶级元素,其中每个顶级元素的含义如下:

LocalRepository:该值表示构建系统本地仓库的路径

Servers:配置仓库访问的用户名和密码

Profiles:根据环境参数来调整构建配置的列表,可以配置仓库列表和插件仓库列表

ActiveProfiles:手动激活profiles的列表

Mirrors:为仓库列表配置的下载镜像列表

Proxies:用来配置不同的代理。

InteractiveMode:表示maven是否需要和用户交互以获得输入

UsePluginRegistry:maven是否需要使用plugin-registry.xml文件来管理插件版本。

Offline:表示maven是否需要在离线模式下运行。

PluginGroups:当插件的组织id(groupId)没有显式提供时,供搜寻插件组织Id(groupId)的列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<servers>
<server>
<id>repository1</id>
<username>deployment</username>
<password>deployment123</password>
</server>
</servers>

<mirrors>
<mirror>
<id>mirror1</id>
<mirrorOf>repository2</mirrorOf>
<name>mirror of repository2</name>
<url>http://maven.test.com/mirror1</url>
</mirror>
<mirror>
<id>mirror2</id>
<mirrorOf>repository4</mirrorOf>
<name>mirror of repository4</name>
<url>http://maven.test.com/mirror2</url>
</mirror>
</mirrors>

<profiles>
<profile>
<id>profile2</id>
<repositories>
<repository>
<id>repository3</id>
<name>repository3 name</name>
<url>http://maven.test.com/repository3</url>
</repository>
<repository>
<id>repository4</id>
<name>repository4 name</name>
<url>http://maven.test.com/repository4</url>
</repository>
</repositories>
</profile>
<profile>
<id>profile1</id>
<repositories>
<repository>
<id>repository1</id>
<name>repository1 name</name>
<url>http://maven.test.com/repository1</url>
</repository>
<repository>
<id>repository2</id>
<name>repository2 name</name>
<url>http://maven.test.com/repository2</url>
</repository>
</repositories>
</profile>
</profiles>

<activeProfiles>
<activeProfile>profile2</activeProfile>
<activeProfile>profile1</activeProfile>
</activeProfiles>

<distributionManagement>
<repository>
<id>repository1</id>
<name>repository1 name</name>
<url>http://maven.test.com/repository1</url>
</repository>
<snapshotRepository>
<id>repository2</id>
<name>repository2 name</name>
<url>http://maven.test.com/repository2</url>
</snapshotRepository>
</distributionManagement>
</settings>

仓库优先级顺序

当从仓库中拉取jar包时,该从哪个仓库中拉取呢?

按一般情况,该按就近原则,本地配置优于全局配置,但maven仓库的优先级并不是如此。

使用上述的settings.xml文件,文件中配置了四个仓库,其中两个又被设置了镜像。在本地pom.xml文件中再单独配置一个仓库repository5。

使用mvn clean compile拉取junit包实践上述问题,可得到以下结果:

1
2
3
4
5
6
7
Downloading from repository1: http://maven.test.com/repository1/junit/junit/4.12/junit-4.12.pom
Downloading from mirror1: http://maven.test.com/mirror1/junit/junit/4.12/junit-4.12.pom
Downloading from repository3: http://maven.test.com/repository3/junit/junit/4.12/junit-4.12.pom
Downloading from mirror2: http://maven.test.com/mirror2/junit/junit/4.12/junit-4.12.pom
Downloading from repository5: http://maven.test.com/repository5/junit/junit/4.12/junit-4.12.pom
Downloading from central: https://repo.maven.apache.org/maven2/junit/junit/4.12/junit-4.12.pom
Downloaded from central: https://repo.maven.apache.org/maven2/junit/junit/4.12/junit-4.12.pom (24 kB at 7.4 kB/s)

总结:

1、maven首先会从本地仓库中查找jar包

2、maven会从settings.xml配置的仓库中查找jar包。如果配置了多个profile,profile的顺序按中配置的倒序,而不是按中配置的激活顺序。如果中配置了镜像,被镜像的仓库会被镜像替代。

3、maven从项目的pom.xml中配置的仓库拉取jar包。

4、maven从超级pom配置的中央仓库拉取jar包,该地址是https://repo.maven.apache.org/maven2

5、如果按以上顺序都没有拉取到jar包,maven会提示错误。

包拉不下来?

1、更改仓库更新周期为always

1
2
3
4
5
6
7
8
9
10
11
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>

2、使用mvn clean install -U -DskipTests,-U参数会强制拉取丢失的release和更新snapshot,并忽略updatePolicy元素配置。Idea还得reimport生效。

3、删除本地仓库中的jar包强制拉取

Maven生命周期

1、Maven拥有三套相互独立的生命周期,分别是clean、default和site。

2、每个生命周期包含一些阶段(phase),当用户调用mvn pre-clean时,只有pre-clean阶段得以执行;当用户调用mvn clean时,pre-clean和clean阶段会得以顺序执行。

3、Maven的生命周期阶段与插件相互绑定,并且在核心为一些主要的生命周期阶段绑定了很多插件的目标,当用户通过命令行调用生命周期阶段时,对应的插件目标就会执行相应的任务。

例如clean生命周期阶段与插件maven-clean-plugin的clean目标绑定。当执行mvn clean时,会首先执行pre-clean绑定的插件,因为pre-clean无内置绑定,所以无任何动作。然后会执行clean生命周期阶段绑定的插件clean的clean目标,用于清理上一次构建生成的文件。

4、Maven支持直接从命令行调用插件目标。调用插件目标,需要完整输入“groupId”、“artifactId”、“version”等等定位到某个插件,然后通过“:”写下插件目标,就可以运行插件的目标了。

插件前缀可以让你更方便地输入指令,例如输入mvn dependency:tree等价于mvn org.apache.maven.plugins:maven-dependency-plugin:2.1:tree

maven-${prefix}-plugin,这个方式是官网插件使用的插件前缀格式。

${prefix}-maven-plugin,这个方式是第三方插件(包括我们自己写的)使用的插件前缀格式。

clean生命周期

生命周期阶段 内置绑定 描述
pre-clean 执行一些清理前需要完成的工作。
clean clean:clean 清理上一次构建生成的文件。
post-clean 执行一些清理后需要完成的工作。

default生命周期

生命周期阶段 内置绑定(打包类型为ejb/ejb3/jar/par/rar/war) 描述
validate 检查工程配置是否正确,完成构建过程的所有必要信息是否能够获取到。
initialize 初始化构建状态,例如设置属性。
generate-sources 生成编译阶段需要包含的任何源码文件。
process-sources 处理源代码,例如,过滤任何值(filter any value)。
generate-resources 生成工程包中需要包含的资源文件。
process-resources resources:resources 拷贝和处理资源文件到目的目录中,为打包阶段做准备。
compile compiler:compile 编译工程源码。
process-classes 处理编译生成的文件,例如 Java Class 字节码的加强和优化。
generate-test-sources 生成编译阶段需要包含的任何测试源代码。
process-test-sources 处理测试源代码,例如,过滤任何值(filter any values)。
generate-test-resources 生成测试工程包中需要包含的资源文件。
process-test-resources resources:testResources 拷贝和处理资源文件到目的测试目录中,为打包阶段做准备。
test-compile compiler:testCompile 编译测试源代码到测试目的目录。
process-test-classes 处理测试代码文件编译后生成的文件。
test surefire:test 使用适当的单元测试框架(例如JUnit)运行测试。
prepare-package 在真正打包之前,为准备打包执行任何必要的操作。
package ejb:ejb or ejb3:ejb3 or jar:jar or par:par or rar:rar or war:war 获取编译后的代码,并按照可发布的格式进行打包,例如 JAR、WAR 或者 EAR 文件。
pre-integration-test 在集成测试执行之前,执行所需的操作。例如,设置所需的环境变量。
integration-test 处理和部署必须的工程包到集成测试能够运行的环境中。
post-integration-test 在集成测试被执行后执行必要的操作。例如,清理环境。
verify 运行检查操作来验证工程包是有效的,并满足质量要求。
install install:install 安装工程包到本地仓库中,该仓库可以作为本地其他工程的依赖。
deploy deploy:deploy 拷贝最终的工程包到远程仓库中,以共享给其他开发人员和工程。

Site生命周期

生命周期阶段 内置绑定 描述
pre-site 执行一些生成项目站点文档前需要完成的工作。
site site:site 生成项目站点文档。
post-site 执行一些生成项目站点文档后需要完成的工作。
site-deploy site:deploy 将生成的项目站点发布到服务器上。

maven插件

编写插件

创建一个类,使用@Mojo注解定义goal,继承AbstractCompilerMojo,重写execute()方法。

例如下面compiler插件,当使用mvn compiler:compile命令时,会执行下面这个类的execute()方法。

1
2
3
4
5
6
@Mojo( name = "compile", defaultPhase = LifecyclePhase.COMPILE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE )
public class CompilerMojo extends AbstractCompilerMojo
{
public void execute() throws MojoExecutionException, CompilationFailureException{
}
}

为什么一定要配置maven-compiler-plugin插件?

compile阶段内置绑定maven-compiler-plugin插件编译代码,在maven-compiler-plugin 3.8.0源码中,如果未指定source和target,默认值是jdk 1.6(之前的版本是jdk 1.5),这时如果代码中存在java8语法,会报“ -source 1.6 中不支持 diamond 运算符”等错误。

所以如果配置<source>1.8</source><target>1.8</target> 可以指定源代码编译版本和最低支持jdk版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* <p>The -source argument for the Java compiler.</p>
*
* <b>NOTE: </b>Since 3.8.0 the default value has changed from 1.5 to 1.6
*/
@Parameter( property = "maven.compiler.source", defaultValue = DEFAULT_SOURCE )
protected String source;

/**
* <p>The -target argument for the Java compiler.</p>
*
* <b>NOTE: </b>Since 3.8.0 the default value has changed from 1.5 to 1.6
*/
@Parameter( property = "maven.compiler.target", defaultValue = DEFAULT_TARGET )
protected String target;

Compiler插件使用的Java编译器

在Compiler插件3.0之前,默认的Java编译器就是JDK自带的javac。但是从Compiler插件3.0开始,默认的Java编译器是javax.tools.JavaCompiler。见http://maven.apache.org/plugins/maven-compiler-plugin/

The Compiler Plugin is used to compile the sources of your project. Since 3.0, the default compiler is javax.tools.JavaCompiler (if you are using java 1.6) and is used to compile Java sources. If you want to force the plugin using javac, you must configure the plugin option forceJavacCompilerUse.

至于为什么使用javax.tools.JavaCompiler来替代JDK自带的javac,stackoverflow中有人提到,使用JavaCompiler不需要新起一个虚拟机来执行javac,将会加快编译速度。

Idea和eclipse使用的Java编译器

idea中build project与maven compile产生的效果相同?

参考

调试maven插件:https://www.jianshu.com/p/2f74295be60a

maven生命周期:http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference

Maven依赖传递:https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope

# Maven
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×