本文详细分析了Maven的全局/用户级配置文件settings.xml和项目级配置文件pom.xml,便于查阅。

TABLE OF CONTENTS

  1. 一、工作原理
  2. 二、仓库管理
  3. 三、Maven配置文件
  4. 四、Settings配置详解
    1. 1、localRepository
    2. 2、interactiveMode
    3. 3、offline
    4. 4、pluginGroups
    5. 5、servers
    6. 6、proxies
    7. 7、mirrors
    8. 8、profiles
      1. repositories
      2. properties
      3. id
      4. pluginRepositories
      5. activation
    9. 9、activeProfiles
    10. 10、Settings.xml配置文件中mirrors和profile中repositories的关系
  5. 五、pom.xml配置详解
    1. 1、dependencies
    2. 2、dependencyManagement
    3. 3、properties
    4. 4、resources
    5. 5、profile
    6. 6、modules
    7. 7、查找并添加依赖
    8. 8、依赖范围管理
    9. 9、解决依赖冲突
  6. 六、参考资料

一、工作原理

项目根据Maven配置,通常先从本地仓库去拉取jar包,如果没有则取远程仓库拉取,这个远程仓库可以是私服,也可以是中央仓库或者镜像仓库。

二、仓库管理

Maven分为本地仓库和远程仓库,本地仓库即<localRepository>标签配置的路径。
远程仓库分为中央仓库、私服、其他公共库。

Maven仓库的关系:

由于墙的存在,国内访问国外中央仓库不顺利,Maven拉取Jar包时会很慢甚至无法拉取,因此需要配置下国内的中央镜像仓库。镜像仓库在Maven配置文件settings.xml下进行配置。

镜像仓库:将国外的中心仓库复制一份到国内,提升访问速度。

私服:基于系统保密性的原因,搭建的内部远程仓库,存储一些公司内部不希望被公开的依赖服务,也可以避免因外部中央仓库网络的访问不顺畅导致的各种问题。

三、Maven配置文件

Maven配置文件有三种:

  • 全局配置文件:%MAVEN_HOME%\conf\settings.xml
  • 用户配置文件:C:\Users\用户名\.m2\settings.xml
  • 项目配置文件:项目下的pom.xml

配置优先级:项目pom.xml > 本地settings > 全局settings,如果配置文件同时存在,会进行合并,有重复的配置时,优先级高的配置会覆盖优先级低的配置。

四、Settings配置详解

首先,可以从Maven安装目录的conf下查看配置文件settings.xml,示例如下(注意仅展示结构,要经过定制才可使用):

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
77
78
79
80
81
82
83
84
85
86
<?xml version="1.0" encoding="UTF-8"?>
<settings
xmlns="http://maven.apache.org/SETTINGS/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
<localRepository>/path/to/local/repo</localRepository>
<interactiveMode>true</interactiveMode>
<offline>false</offline>
<pluginGroups>
<pluginGroup>com.your.plugins</pluginGroup>
</pluginGroups>

<servers>
<server>
<id>deploymentRepo</id>
<username>repouser</username>
<password>repopwd</password>
</server>
<!-- Another sample, using keys to authenticate. -->
<server>
<id>siteServer</id>
<privateKey>/path/to/private/key</privateKey>
<passphrase>optional; leave empty if not used.</passphrase>
</server>
</servers>

<proxies>
<proxy>
<id>optional</id>
<active>true</active>
<protocol>http</protocol>
<username>proxyuser</username>
<password>proxypass</password>
<host>proxy.host.net</host>
<port>80</port>
<nonProxyHosts>local.net|some.host.com</nonProxyHosts>
</proxy>
</proxies>

<mirrors>
<mirror>
<id>maven-default-http-blocker</id>
<mirrorOf>external:http:*</mirrorOf>
<name>Pseudo repository to mirror external repositories initially using HTTP.</name>
<url>http://0.0.0.0/</url>
<blocked>true</blocked>
</mirror>
</mirrors>

<profiles>
<profile>
<id>jdk-1.4</id>
<activation>
<jdk>1.4</jdk>
</activation>
<repositories>
<repository>
<id>jdk14</id>
<name>Repository for JDK 1.4 builds</name>
<url>http://www.myhost.com/maven/jdk14</url>
<layout>default</layout>
<snapshotPolicy>always</snapshotPolicy>
</repository>
</repositories>
</profile>
<!-- Another sample -->
<profile>
<id>env-dev</id>
<activation>
<property>
<name>target-env</name>
<value>dev</value>
</property>
</activation>
<properties>
<tomcatPath>/path/to/tomcat/instance</tomcatPath>
</properties>
</profile>
</profiles>

<activeProfiles>
<activeProfile>alwaysActiveProfile</activeProfile>
<activeProfile>anotherAlwaysActiveProfile</activeProfile>
</activeProfiles>
</settings>

1、localRepository

配置本地仓库位置,默认位置为${user.home}/.m2/repository,可以配置为

1
<localRepository>D:\Workslace\Maven\maven-repository</localRepository>

2、interactiveMode

Maven是否需要和用户交互以获得输入,默认为true

3、offline

用来标识是否以离线模式运营Maven。当系统不能联网时,可以通过该配置来离线运行。

4、pluginGroups

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

5、servers

访问私服时,某些私服需要配置认证信息,在这里填写。之所以servers配置不写在pom.xml,是因为pom.xml会随着代码上传代码仓库,而settings.xml存储在本地,相对安全。因此,通常在settings.xml中配置servers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<servers>
<server>
<!--这是server的id(注意不是用户登陆的id),该id与distributionManagement中repository元素的id相匹配。 -->
<id>server001</id>
<!--鉴权用户名。 -->
<username>my_login</username>
<!--鉴权密码。 -->
<password>my_password</password>
<!--鉴权时使用的私钥位置。和前两个元素类似,私钥位置和私钥密码指定了一个私钥的路径(默认是${user.home}/.ssh/id_dsa)以及如果需要的话,一个密语。将来passphrase和password元素可能会被提取到外部,但目前它们必须在settings.xml文件以纯文本的形式声明。 -->
<privateKey>${usr.home}/.ssh/id_dsa</privateKey>
<!--鉴权时使用的私钥密码。 -->
<passphrase>some_passphrase</passphrase>
<!--文件被创建时的权限。如果在部署的时候会创建一个仓库文件或者目录,这时候就可以使用权限(permission)。 -->
<filePermissions>664</filePermissions>
<!--目录被创建时的权限。 -->
<directoryPermissions>775</directoryPermissions>
</server>
</servers>

6、proxies

用来配置代理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<proxies>
<proxy>
<!--代理id。用来区分不同的代理元素。 -->
<id>myproxy</id>
<!--该代理是否是激活的那个。true则激活代理。当我们声明了一组代理,而某个时候只需要激活一个代理的时候,该元素就可以派上用处。 -->
<active>true</active>
<!-- 格式:协议://主机名:端口 -->
<!--代理的协议。 协议://主机名:端口 -->
<protocol>http</protocol>
<!--代理的主机名。 -->
<host>proxy.somewhere.com</host>
<!--代理的端口。 -->
<port>8080</port>
<!--代理的用户名。用户名和密码表示代理服务器认证的登录名和密码。 -->
<username>proxyuser</username>
<!--代理的密码。用户名和密码表示代理服务器认证的登录名和密码。 -->
<password>somepassword</password>
<!--不该被代理的主机名列表。该列表的分隔符由代理服务器指定;例子中使用了竖线分隔符,使用逗号分隔也很常见。 -->
<nonProxyHosts>*.google.com|ibiblio.org</nonProxyHosts>
</proxy>
</proxies>

7、mirrors

配置镜像仓库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/repository/central</url>
<mirrorOf>central</mirrorOf>
</mirror>
<mirror>
<id>alimaven_public</id>
<name>aliyun maven public</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>

当本地仓库没有需要的依赖时,根据<mirrorOf>匹配远程仓库请求,去对应的镜像仓库地址拉取依赖。

下面是一些常用的语法示例:

  • <mirrorOf>*<mirrorOf>:匹配所有远程仓库。
  • <mirrorOf>external:*<mirrorOf>:匹配所有不在本机上的远程仓库。
  • <mirrorOf>repo1,repo2<mirrorOf>:匹配仓库repo1和repo2,使用逗号分隔多个远程仓库。
  • <mirrorOf>*,!repo1<mirrorOf>:匹配所有远程仓库,repo1除外,使用感叹号将仓库从匹配中排除。

注意
镜像仓库完全屏蔽了被镜像仓库,当镜像仓库不稳定或者停止服务的时候,Maven仍将无法访问被镜像仓库,因而将无法下载jar包。

此外,maven读取mirror配置是从上往下读取的,因此谨慎配置<mirrorOf>*<mirrorOf>,因为如果第一个镜像仓库配置了如此标志,那么如果该仓库即使不存在对应依赖也不会向下游查询。

8、profiles

根据环境参数来调整构建配置的列表。可以将settings.xml下的profile理解为pom.xml种profile的阉割版。
settings.xml中的profiles包含了id、activation、repositories、pluginRepositories和properties元素

注意:
如果一个settings.xml的profile被激活,那么它的值会覆盖任何其他定义在pom.xml种带有相同id的profile

激活profile有三种方式

  • 通过命令行编译构建时激活,最常用的方式。
  • 通过activeProfiles直接激活,详细往下看。
  • 通过activation激活,详细往下看。

通过命令行激活

通常在pom.xml中定义不同环境的profile,在编译构建不同的包时,执行下面的命令即可激活对应的profile进行编译构建。

以开发环境为例:

1
2
# 命令格式:mvn package -P [profile的ID]
mvn package -P dev

repositories

定义了一组远程仓库的列表,当该属性对应的profile被激活时,会使用该远程仓库。

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
<repositories>
<!--包含需要连接到远程仓库的信息 -->
<repository>
<!--远程仓库唯一标识 -->
<id>codehausSnapshots</id>
<!--远程仓库名称 -->
<name>Codehaus Snapshots</name>
<!--如何处理远程仓库里发布版本的下载 -->
<releases>
<!--true或者false表示该仓库是否为下载某种类型构件(发布版,快照版)开启。 -->
<enabled>false</enabled>
<!--该元素指定更新发生的频率。Maven会比较本地POM和远程POM的时间戳。这里的选项是:always(一直),daily(默认,每日),interval:X(这里X是以分钟为单位的时间间隔),或者never(从不)。 -->
<updatePolicy>always</updatePolicy>
<!--当Maven验证构件校验文件失败时该怎么做-ignore(忽略),fail(失败),或者warn(警告)。 -->
<checksumPolicy>warn</checksumPolicy>
</releases>
<!--如何处理远程仓库里快照版本的下载。有了releases和snapshots这两组配置,POM就可以在每个单独的仓库中,为每种类型的构件采取不同的策略。例如,可能有人会决定只为开发目的开启对快照版本下载的支持。参见repositories/repository/releases元素 -->
<snapshots>
<enabled />
<updatePolicy />
<checksumPolicy />
</snapshots>
<!--远程仓库URL,按protocol://hostname/path形式 -->
<url>http://snapshots.maven.codehaus.org/maven2</url>
<!--用于定位和排序构件的仓库布局类型-可以是default(默认)或者legacy(遗留)。 -->
<layout>default</layout>
</repository>
</repositories>

properties

定义一组拓展属性,当对应的profile被激活时该属性才生效。
执行mvn help:system可以获取System Properties和Environment Variables,根据下面注释进行使用即可。

1
2
3
4
5
6
7
8
9
10
11
<!--
1. env.X: 在一个变量前加上"env."的前缀,会返回一个shell环境变量。例如,"env.PATH"指代了$path环境变量(在Windows上是%PATH%)。
2. project.x:指代了POM中对应的元素值。例如: <project><version>1.0</version></project>通过${project.version}获得version的值。
3. settings.x: 指代了settings.xml中对应元素的值。例如:<settings><offline>false</offline></settings>通过 ${settings.offline}获得offline的值。
4. java System Properties: 所有可通过java.lang.System.getProperties()访问的属性都能在POM中使用该形式访问,例如 ${java.home}。可以实际运行获取查看
5. x: 在<properties/>元素中,或者外部文件中设置,以${someVar}的形式使用。
-->
<properties>
<user.install>${user.home}/our-project</user.install>
</properties>

id

全局唯一标识,如果一个settings.xml中的profile被激活,它的值会覆盖任何其它定义在pom.xml中带有相同id的profile。

pluginRepositories

同repositories差不多,不过该标签定义的是插件的远程仓库。

activation

触发激活该profile的条件。注意在Maven 3.2.2之前,当满足一项指定条件时就会激活。从Maven 3.2.2开始,需要满足所有指定条件时才会激活。可以移步官方文档查阅。

Before Maven 3.2.2 activation occurs when one or more of the specified criteria have been met. When the first positive result is encountered, processing stops and the profile is marked as active. Since Maven 3.2.2 activation occurs when all of the specified criteria have been met.

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
<activation>
<!--profile默认是否激活的标识 -->
<activeByDefault>false</activeByDefault>
<!--当匹配的jdk被检测到,profile被激活。例如,1.4激活JDK1.4,1.4.0_2,而!1.4激活所有版本不是以1.4开头的JDK。 -->
<jdk>1.5</jdk>
<!--当匹配的操作系统属性被检测到,profile被激活。os元素可以定义一些操作系统相关的属性。可以结合java.lang.System.getProperties()中的属性进行配置 -->
<os>
<!--激活profile的操作系统的名字 -->
<name>Windows XP</name>
<!--激活profile的操作系统所属家族(如 'windows') -->
<family>Windows</family>
<!--激活profile的操作系统体系结构 -->
<arch>x86</arch>
<!--激活profile的操作系统版本 -->
<version>5.1.2600</version>
</os>
<!--如果Maven检测到某一个属性(其值可以在POM中通过${name}引用),其拥有对应的name = 值,Profile就会被激活。如果值字段是空的,那么存在属性名称字段就会激活profile,否则按区分大小写方式匹配属性值字段 -->
<property>
<!--激活profile的属性的名称 -->
<name>mavenVersion</name>
<!--激活profile的属性的值 -->
<value>2.0.3</value>
</property>
<!--提供一个文件名,通过检测该文件的存在或不存在来激活profile。missing检查文件是否存在,如果不存在则激活profile。另一方面,exists则会检查文件是否存在,如果存在则激活profile。 -->
<file>
<!--如果指定的文件存在,则激活profile。 -->
<exists>${basedir}/file2.properties</exists>
<!--如果指定的文件不存在,则激活profile。 -->
<missing>${basedir}/file1.properties</missing>
</file>
</activation>

9、activeProfiles

指定激活的profile,不管profile的activation配置如何,都会激活对应的profile。

1
2
3
4
5
<activeProfiles>
<!-- 要激活的profile id -->
<activeProfile>test</activeProfile>
<activeProfile>dev</activeProfile>
</activeProfiles>

10、Settings.xml配置文件中mirrorsprofile中repositories的关系

两个标签都定义了一个远程仓库的位置,如果一个依赖同时存在于这2个仓库,会先加载哪个依赖?

mirrors相当于拦截器,maven加载依赖时,如果mirror的mirrorOf配置的值与对应的repository的id相同,那么mirror的仓库地址替换掉repository的仓库地址。

五、pom.xml配置详解

settings.xml定义的是全局或用户的配置,pom.xml定义的是一个项目的依赖配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<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>
<!-- 公司或者组织的唯一标志,也是打包成jar包路径的依据 -->
<!-- 例如com.companyname.project-group,maven打包jar包的路径:/com/companyname/project-group -->
<groupId>com.companyname.project-group</groupId>
<!-- 项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
<artifactId>projectid</artifactId>
<!-- 项目当前版本,格式为:主版本.次版本.增量版本-限定版本号 -->
<version>1.0.2</version>
<!--项目产生的构件类型,
jar、war主要用来标识项目打包出的服务是jar包还是war包
pom一般用作多moudle的项目中 顶层的pom用来指定子moudle中需要依赖的版本-->
<packaging>jar</packaging>
<!--定义了本项目的名称与example的网址 -->
<name>blog project</name>
<url>blog.tsukasa.moe</url>
</project>

1、dependencies

定义了项目中所需要的相关依赖。

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
<dependencies>
<dependency>
<!-- 依赖坐标 -->
<!--依赖项目的坐标三元素:groupId + artifactId + version -->
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>3.8.1</version>
<!-- 依赖传递 -->
<!--依赖排除,即告诉maven只依赖指定的项目,不依赖该项目的这些依赖。此元素主要用于解决版本冲突问题 -->
<exclusions>
<exclusion>
<artifactId>spring-core</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
<!-- 可选依赖,用于阻断依赖的传递性。如果在项目B中把C依赖声明为可选,那么依赖B的项目中无法使用C依赖,如使用到C依赖提供的服务,需要另外引入C依赖 -->
<optional>true</optional>
<!-- 依赖范围 -->
<!--依赖范围。在项目发布过程中,帮助决定哪些构件被包括进来
- compile:默认范围,适用于所有阶段,会随着项目一起发布;
- runtime: 在执行时需要使用,如JDBC驱动,适用运行和测试阶段,不同于例如fastjson,需要在编译时使用;
- test: 只在测试时使用,用于编译和运行测试代码,例如junit,不同于junit,在发布时并不需要;
- optional: 当项目自身被依赖时,标注依赖是否传递。用于连续依赖时使用 -->
<scope>test</scope>
</dependency>
</dependencies>

2、dependencyManagement

一个服务中存在多个module时,每个子module可能都引用了相同的jar包,此时可以通过maven继承,父级pom.xml统一管理依赖版本。需要使用到<dependencyManagement>标签。

示例:

父级pom.xml

1
2
3
4
5
6
7
8
9
10
<!--在父pom中定义子pom需要的相关依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</dependencyManagement>

子module的pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--在子pom中  如下定义了父pom中相关依赖信息 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/>
</parent>

<dependencies>
<dependency>
<!--因为引用了父pom 所以可以不指定版本 maven会自动去父pom中查找指定版本 此处为1.0.0 -->
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
</dependencies>

3、properties

properties主要用来定义常量,常见的有依赖版本、JDK版本、字节编码等,通过${value}来使用。

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
<!--配置依赖版本-->
<properties>
<!-- Environment Settings -->
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring cloud Settings -->
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
<slf4j.version>1.7.12</slf4j.version>
</properties>

<dependencies>
<!--spring cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependencies>

此外,Maven还通过约定大于配置的方式定义了一些常用的属性:

属性 定义
${basedir} 存放pom.xml和所有的子目录
${basedir}/src/main/java 项目的java源代码
${basedir}/src/main/resources 项目的资源,比如说property文件,springmvc.xml
${basedir}/src/main/webapp/WEB-INF web应用文件目录,web项目的信息,比如web.xml
${basedir}/target 打包输出目录
${project.version} 项目版本
${project.groupId} 项目groupId

4、resources

<build>下面的<resources>标签用来标识项目在编译运行时需要额外编译的文件。例如手工引入jar包、不同运行环境对应不同的profile等等。

<testResources>用法与<resources>类似。

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
<build>
<resources>
<!--首先将默认resources目录下的所有文件包含进来 -->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<!--只编译所有以.fxml结尾的文件 -->
<includes>
<include>**/*.fxml</include>
</includes>
<!--排除掉所有的yaml文件 -->
<excludes>
<exclude>**/*.yaml</exclude>
</excludes>
</resource>
<!--将不同环境下对应的不同yaml或properties文件编译运行 -->
<resource>
<!--
<directory>src/main/profiles/dev</directory><directory>src/main/profiles/beta<directory><directory>src/main/profiles/pre</directory>
-->
<directory>src/main/profiles/product</directory>
<filtering>true</filtering>
<includes>
<include>**/*.fxml</include>
</includes>
</resource>
<!--将手工引入的jar包编译运行 -->
<resource>
<directory>lib</directory>
<targetPath>BOOT-INF/lib/</targetPath>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
</resources>
</build>

5、profile

通常地,setting.xml用来标识不同的远程仓库,而pom中的profile一般用来标识当前profile的配置属于哪个环境,当然也可以用来指定远程仓库。

在编译打包时,通过mvn package -P prod命令来激活不同环境。

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
<profiles>
<profile>
<id>dev</id>
<!--激活条件 其中默认为该profile -->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<!--当此profile被激活时,会将 project.active 的属性赋值为dev -->
<properties>
<project.active>dev</project.active>
</properties>
</profile>
<profile>
<id>test</id>
<!--当此profile被激活时,会将 project.active 的属性赋值为test -->
<properties>
<project.active>test</project.active>
</properties>
</profile>
</profiles>

<resources>
<resource>
<!--根据不同的环境 project.active 取得不同的值 从而会将不同的环境下的yaml或properties文件编译进项目中 达到只需要在编译时指定环境变量即可 不用每次都要修改配置文件 -->
<directory>src/main/${project.active}</directory>
<filtering>true</filtering>
<includes>
<include>**/*.fxml</include>
</includes>
</resource>
</resources>

在Intellij IDEA的Maven窗口中,profile作为一个个的可选项存在。

6、modules

项目中存在多个module时,如果需要单独打包需要在每一个module都执行maven命令,通过父级pom.xml添加的<modules>标签可以将自服务进行聚合,只需要打包该服务,也会将子module同时打包。

1
2
3
4
5
6
7
<modules>
<!-- 引入子模块所在的相对目录 -->
<module>spring-sub-module</module>
<!-- 引入同级模块所在的相对目录 -->
<module>../utils</module>
</modules>

7、查找并添加依赖

配置好Maven后,想要添加什么依赖,可以到仓库索引搜索,然后选择对应的坐标,复制到pom.xml文件<dependencies>中即可。

8、依赖范围管理

Maven项目构建生命周期包括clean清理项目,default构建项目,site生成项目文档和站点。重点关注default生命周期的各个阶段:

阶段 中文名称 描述
validate 校验 验证项目是否正确,所有必需信息是否可用。
initialize 初始化 初始化构建状态,例如设置属性或创建目录。
generate-sources 生成源代码 生成项目的源代码。
process-sources 处理源代码 处理项目的源代码,例如进行过滤等操作。
generate-resources 生成资源文件 生成项目的资源文件。
process-resources 处理资源文件 复制并处理资源文件,为打包做准备。
compile 编译 编译项目的源代码。
process-classes 处理类文件(字节码) 对编译后的字节码进行处理。
generate-test-sources 生成测试源代码 生成项目的测试源代码。
process-test-sources 处理测试源代码 处理项目的测试源代码,例如进行过滤等操作。
generate-test-resources 生成测试资源文件 生成项目的测试资源文件。
process-test-resources 处理测试资源文件 复制并处理测试资源文件,为测试做准备。
test-compile 编译测试源代码 编译项目的测试源代码。
process-test-classes 处理测试类文件(字节码) 对测试编译后的字节码进行处理。
test 测试 使用合适的测试框架运行测试(一般我们在idea种开发项目时,可以选择屏蔽掉test步骤)。
prepare-package 准备打包 进行必要的操作,以便进行打包。
package 打包 将编译后的代码打包成可分发的格式,例如 JAR、WAR。
pre-integration-test 集成测试前 在集成测试之前进行的操作。
integration-test 集成测试 处理和部署项目,以便进行集成测试。
post-integration-test 集成测试后 在集成测试之后进行的操作。
verify 验证 检查包是否有效,符合质量标准。
install 安装 将包安装到本地仓库,以便其他项目依赖。
deploy 部署 将最终的包复制到远程仓库,共享给其他开发人员和项目。



在pom.xml文件的<dependency>标签中有一个子标签<scope>,默认值为compile,记录了依赖的jar包在哪些环境有效

Scope 编译环境 测试环境 运行环境 例子 描述
compile Y Y Y spring-core 依赖对所有的classpath都有效
provided Y Y - servlet-api 执行打包mvn package时会移除,运行时由应用服务器提供
system Y Y - 本地的,Maven仓库之外的类库文件 类似provided,但依赖项不会从maven仓库中查找,通过<systemPath>标签获取
runtime - Y Y jdbc驱动包 编译的时候不需要,只在运行和测试的时候需要用到
test - Y - Junit/Mockito 只在测试编译和测试运行阶段可用

还有一个特殊的取值:import,仅在父级pom.xml的<dependencyManagement>的标签中可用,不做展开。

scope为system示例:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.open</groupId>
<artifactId>open-core</artifactId>
<version>1.5</version>
<scope>system</scope>
<!-- package时会排除这个jar,运行时由应用服务器提供 -->
<systemPath>${basedir}/WebContent/WEB-INF/lib/open-core.jar</systemPath>
</dependency>

关于Dependency Scope的官方描述,请查阅:https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html

总结

通过maven引入的jar包,里面的类都是已经编译好的字节码,与scope参数没有什么联系。

compile、runtime和provided的区别,需要在执行打包mvn package命令,并且打包格式时war时才能看出来。
通过compile和runtime引入的jar包,执行打包命令后会出现在war包里,而provided引入的jar包则不会。

通过compile和provided引入的jar包,里面的类,在项目中直接import进来就可以使用了,编译也没有问题;
而通过runtime引入的jar包中的类,项目代码不能直接使用,用了无法通过编译,只能通过反射的方式来调用。

9、解决依赖冲突

依赖传递遵循三个原则:

  • 最短路径优先:项目中存在两级以上的不同依赖,引用同一个依赖时,层级越浅,优先级越高。举例:项目A -> B -> C1,A -> D -> E -> C2,其中C1和C2是同一个依赖的不同版本,此时A项目以C1的版本为准。
  • 声明优先:对于两级以上的同级依赖,先声明的依赖会覆盖后声明的依赖包。举例:项目A -> B -> C1,A -> D -> C2,以C1版本为准。
  • 特殊优先:同级依赖中,后加载覆盖先加载原则。举例:项目A->C1,再次配置A->C2,那么以C1版本为准。

Intellij IDEA解决依赖冲突思路:

  • 通过Maven Helper插件的Dependency Analyzer对pom文件进行依赖分析,找到冲突或重复的jar包,用<exclusions>标签排除不需要的版本,要注意是否存在兼容性问题。此外也可以通过命令行mvn dependency:tree查看项目的依赖树,观察实际加载的版本。
  • 编译打包后打开查看实际打的jar包版本,进行核对

依赖分析相关的maven命令

mvn dependency:list:查看项目已解析的依赖。
mvn dependency:tree 查看项目的依赖树。
mvn dependency:analyze:分析项目的依赖信息,有2种,根据需要选择主动声明,或者移除不需要的依赖。

  • Used undeclared dependencies,项目中已使用但未声明的依赖。
  • Unused declared dependencies,项目中未使用但已声明的依赖。

六、参考资料

Maven进阶学习指南 | 京东云技术团队
实际上手体验maven面对冲突Jar包的加载规则 | 京东云技术团队

声明:本站所有文章均为原创或翻译,遵循署名 - 非商业性使用 - 禁止演绎 4.0 国际许可协议,如需转载请确保您对该协议有足够了解,并附上作者名 (Tsukasa) 及原文地址