项目管理工具:Maven(中)
POM文件管理
pom.xml文件叫做项目对象模型(POM,Project Object Model)是Maven项目核心配置文件。
坐标
在 Maven 世界中存在着数十万甚至数百万构件,Maven 坐标包括 groupId、artifactId、version、packaging 等元素,通过坐标在仓库中找到项目所需的Jar包。
- groupId标签: 项目组 ID,定义当前 Maven 项目隶属的组织或公司,通常是唯一的。它的取值一般是项目所属公司或组织的网址或 URL 的倒写,例如 www.baidu.com,倒写后:com.baidu.www。
- artifactId标签: 项目的唯一的标识符,通常是项目的名称。
- version标签:版本号。
- packaging标签:项目的打包方式,比如 jar包,war包、pom等,默认值为 jar。
其中groupId标签、artifactId标签 和 version标签三个必须定义的元素,组成坐标唯一标识依赖。示例代码如下:
<?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>com.study.test</groupId>
<artifactId>helloMaven</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
SNAPSHOT(快照)
团队项目开发中,可能会因为不同项目组之间版本不同导致错误,Maven的SNAPSHOT(快照)是一种特殊的版本,它表示当前开发进度的副本。与常规版本不同,快照版本的构件在发布时,Maven 会自动为它打上一个时间戳,有了这个时间戳后,当依赖该构件的项目进行构建时,Maven 就能从仓库中找到最新的 SNAPSHOT 版本文件。
Maven 仓库分为两种,Snapshot 快照仓库和 Release 发行仓库。
Snapshot 快照仓库
<version>1.0-SNAPSHOT</version>
Release 发行仓库
<!--<version>1.0</version>-->
<version>1.0-RELEASE</version>
两者最大的区别是:每次重新构建项目的时候SNAPSHOT(快照)版本,自动从远程仓库上下载最新的快照版本;Release (发行)版本在不更改版本号的前提下,直接编译打包时,如果本地仓库已经存在该版本的模块,则 Maven 不会主动去远程仓库下载。
如果你不想让SNAPSHOT(快照)版本频繁的去远程仓库更新,可以在 Maven 的配置文件settings.xml中有一个 repository标签。频率共有四种,分别是 always、daily、interval、never,默认值是daily。
- always:每次都去远程仓库查看是否有更新。
- daily:每天第一次的时候查看是否有更新。
- interval:
interval:*
设置一个分钟为单位的间隔时间,在这个间隔时间内只会去远程仓库中查找一次。 - never:不会去远程仓库中查找。
<profile>
<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>
聚合和继承
在实际的开发过程中,我们所接触的项目一般都由多个模块组成。无论是聚合还是继承,它们都应属于一个项目中不同模块的关系。项目结构,如图
- 聚合:将项目多个模块聚合在一起,这样能够更加方便项目的管理。
示例代码如下:
父模块 helloMaven,示例代码如下:
<?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>com.study.test</groupId>
<artifactId>helloMaven</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>helloMaven-common</module>
<module>helloMaven-server</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
使用<modules>
标签进行项目聚合,不过需要注意父项目的<packaging>
类型一定是pom,表明这是个聚合工程用于Jar包管理。
我们执行mvn install
命令,可以看到对子项目的打包,如图:
- 继承:定义了聚合以后,每个子项目就继承自父项目,继承的好处就是对依赖的统一管理,如果相同的依赖在每个模块中都写一遍,更换版本时,有项目版本未及时更改导致错误,这样操作非常繁琐。子项目也可通过版本覆盖的方式,定义指定版本,更加灵活。
子模块 helloMaven-server,示例代码如下:
<?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">
<parent>
<artifactId>helloMaven</artifactId>
<groupId>com.study.test</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>helloMaven-server</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
子模块 helloMaven-common,示例代码如下:
<?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">
<parent>
<artifactId>helloMaven</artifactId>
<groupId>com.study.test</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>helloMaven-common</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
我们可以看到子模块中,通过<parent>
标签继承父模块,子模块可以访问父模块的依赖,可以不用定义<groupId>
标签和 <version>
标签,其中<relativePath>
标签定义了父项目的pom文件位置,如果找不到再去本地仓库查找,如果子模块在父模块的文件夹中可以被省略。
除了<groupId>
标签和 <version>
标签可以被继承外,还有很多标签也可以被继承,简单列举一些,如下:
<groupId>
:项目组ID,项目坐标的核心元素。<version>
:项目版本,项目坐标的核心元素。<description>
:项目的描述信息。<organization>
:项目的组织信息。<properties>
:自定义的Maven属性。<dependencies>
:项目的依赖配置。<dependencyManagement>
:项目的依赖管理配置。<repositories>
:项目的仓库配置。<build>
:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等。<reporting>
:包括项目的报告输出目录配置、报告插件配置等。
Maven 的继承和聚合的目的不同,继承的目的是为了消除 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>com.study.test</groupId>
<artifactId>helloMaven</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>helloMaven-common</module>
<module>helloMaven-server</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
再没有模块依赖的情况下,是按照预期执行的,但是实际开发中,会有很多各种依赖。Maven会考虑模块之间的继承和依赖关系。如果某个模块中包含其它模块依赖,会去执行依赖模块的打包,直至依赖模块中没有依赖其它模块,再去执行原来模块的打包工作,周而复始(这很像套娃)。
子模块 helloMaven-common依赖helloMaven-server模块,示例代码如下:
<?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">
<parent>
<artifactId>helloMaven</artifactId>
<groupId>com.study.test</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>helloMaven-common</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.study.test</groupId>
<artifactId>helloMaven-server</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
子模块 helloMaven-server,示例代码如下:
<?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">
<parent>
<artifactId>helloMaven</artifactId>
<groupId>com.study.test</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>helloMaven-server</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
对比上一张图,你会发现构建顺序的明显区别。注意,如果两个模块之间相互依赖,这样会报错:
子模块 helloMaven-common,示例代码如下:
<?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">
<parent>
<artifactId>helloMaven</artifactId>
<groupId>com.study.test</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>helloMaven-common</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.study.test</groupId>
<artifactId>helloMaven-server</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
子模块 helloMaven-server,示例代码如下:
<?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">
<parent>
<artifactId>helloMaven</artifactId>
<groupId>com.study.test</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>helloMaven-server</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.study.test</groupId>
<artifactId>helloMaven-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
执行mvn install
命令,错误信息如上图所示。
Maven可以通过命令去裁剪反应堆,构建单个模块,简单举例一些命令,有兴趣自行了解:
使用 -pl
命令指定构建某几个模块
mvn clean install -pl moduleA,moduleB,…
使用 -rf
命令在完整的反应堆构建顺序基础上指定从哪个模块开始构建
mvn clean install -rf moduleA
依赖(dependency)
Maven 坐标是依赖的前提, Maven 构建所产生的构件(例如 Jar 文件)被其他项目引用,那么该构件就是其他项目的依赖。
示例代码如下:
<?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>com.study.test</groupId>
<artifactId>helloMaven</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<type>jar</type>
<scope>compile</scope>
<optional>true</optional>
<!--主要用于排除传递性依赖-->
<exclusions>
<exclusion>
<groupId>…</groupId>
<artifactId>…</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
dependencies元素可以包含一个或者多个 dependency 子元素,用以声明一个或者多个项目依赖。每个依赖可以包含的元素有:
- grounpId、artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到需要的依赖。
- type:依赖的类型,对于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值为jar。
- scope:依赖的范围。
- optional:标记依赖是否可选。
- exclusions:用来排除传递性依赖。
绝大部分依赖的 Maven 坐标都能在 中央仓库 中获取,比如搜索spring的依赖,如图所示:
依赖范围
Maven 在对项目进行编译、测试和运行时,会分别使用三套不同的 classpath。通过pom.xml的依赖声明使用 <scope>
元素来控制依赖与三种 classpath(编译 classpath、测试 classpath、运行 classpath )之间的关系,这就是依赖范围。
Maven 具有以下 6 中常见的依赖范围:
- compile:默认依赖范围。此依赖范围对于编译、测试、运行三种classpath都会被引入。以junit为例,示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
可以发现除了测试以外也是可以正常使用的。
- test:测试依赖范围。只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此依赖。比如:Junit,示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
可以看到,此范围只对测试有效。
- provided:已提供依赖范围。只对编译 classpath 和测试 classpath 有效。例如,servlet-api 依赖对于编译、测试阶段而言是需要的,但是运行阶段,由于tomcat 容器已经提供,故不需要 Maven 重复引入该依赖。由于Junit本身属于测试classpath,我们加入fastJson来进行测试,示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.11</version>
<scope>provided</scope>
</dependency>
</dependencies>
当设置为provided范围时,运行时使用fastJson会报异常,只有测试时候正常。
- runtime:运行时依赖范围。只对测试 classpath、运行 classpath 有效。例如,JDBC 驱动实现依赖,其在编译时只需 JDK 提供的 JDBC 接口即可,只有测试、运行阶段才需要实现了 JDBC 接口的驱动。以Junit为例,实例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>runtime</scope>
</dependency>
</dependencies>
可以看到编译时是过不去的,测试是可以的,可能会好奇编译都过不去,如何运行呢?采用反射的方式可以在程序运行时使用该类。比如jdbc的链接,示例代码如下:
public static void main(String[] args) {
Class clazz=Class.forName("com.mysql.jdbc.Driver");
Driver driver=(Driver)clazz.newInstance();
String url="jdbc:myslq://local:3306/database";
Properties info=new Properties();
info.setProperty("user","root");
info.setProperty("password","****");
Connection con=driver.connect(url,info);
}
看到网上回答说是防止程序员直接使用依赖代码到项目,强制使用抽象接口。
- system:系统依赖范围。其用于添加非 Maven 仓库的本地依赖,通过依赖元素 dependency 中的 systemPath 元素指定本地依赖的路径,不依赖Maven仓库,解析与本机系统绑定,可能造成构件的不可移植,因此谨慎使用。以fastJson为例,引入相对应依赖,示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.11</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/fastjson-2.0.11.jar</systemPath>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.11</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/fastjson2-2.0.11.jar</systemPath>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson2-extension</artifactId>
<version>2.0.11</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/fastjson2-extension-2.0.11.jar</systemPath>
</dependency>
</dependencies>
网上所说,与provided依赖范围一致,也只不过是不参与打包,仅此而已。但是编译、测试、运行其实都是可以的,以下是官网原话:
This scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository.
除了必须提供显式包含它的JAR之外,此作用域与提供的作用域类似。工件总是可用的,不需要在存储库中查找。
- import:导入依赖范围。该依赖范围只能与 dependencyManagement 元素配合使用,其功能是将目标 pom.xml 文件中 dependencyManagement 的配置导入合并到当前 pom.xml 的 dependencyManagement 中。该依赖范围不会对三种classpath产生实际的影响,了解即可。
比如有两个项目,helloMaven项目,示例代码如下:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.11</version>
</dependency>
</dependencies>
</dependencyManagement>
helloMaven2项目,示例代码如下:
<!--将父项目的dependencyManagement拿到本POM中,不再继承parent 依赖声明-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.study.test</groupId>
<artifactId>helloMaven</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<!-- 从继承的父项目中继承依赖版本 -->
</dependency>
</dependencies>
看过网上很多代码示例,感觉它们写都不用思考的,dependencyManagement里配置并不会实际引入,只是为了版本管理,实际引入需要直接在dependencies中添加,dependencies 元素中声明的依赖只配置了 groupId 和 artifactId,省略了 version 和 scope。
依赖范围与三种 classpath关系表,如下:
依赖范围 | 编译 classpath | 测试 classpath | 运行 classpath | 是否参与打包 | 例子 |
---|---|---|---|---|---|
compile | √ | √ | √ | 是 | log4j |
test | - | √ | - | 否 | junit |
provided | √ | √ | - | 否 | servlet-api |
runtime | - | √ | √ | 是 | JDBC-driver |
system | √ | √ | - | 否 | 非 Maven 仓库的本地依赖 |
import | - | - | - | 否 | 其它pom文件中的依赖管理 |
依赖传递
假设:项目 A 依赖于项目 B,B 又依赖于项目 C,此时 B 是 A 的直接依赖,C 是 A 的间接依赖,间接依赖 C 以传递性依赖的形式引入到项目 A 中。
在默认情况下项目编译时,Maven会把直接引用和间接引用的Jar包都下载到本地仓库中。
helloMaven项目,示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
helloMaven2项目,示例代码如下:
<dependencies>
<dependency>
<groupId>com.study</groupId>
<artifactId>helloMaven</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
下面介绍主要的四种依赖范围对传递依赖的影响
- compile:当helloMaven项目的依赖范围为compile时,如图:
红框所示,因为compile的依赖范围都可以用,所以helloMaven2项目依赖传递对应的间接依赖也会包含且默认依赖范围也是compile。
- test:当helloMaven项目的依赖范围为test时,如图:
红框所示,因为test的依赖范围只有测试时可以用,所以helloMaven2项目依赖传递对应的间接依赖并没有。
- provided:当helloMaven项目的依赖范围为provided时,如图:
红框所示,因为provided的依赖范围运行时被丢弃,所以helloMaven2项目对应的间接依赖并没有。
- runtime:当helloMaven项目的依赖范围为runtime时,如图:
红框所示,因为runtime的依赖范围在测试或运行时使用,所以helloMaven2项目依赖传递对应的间接依赖也会包含且默认依赖范围也是runtime。
假设,以helloMaven项目为直接依赖(纵向),helloMaven2项目为间接依赖(横向),直接依赖和间接依赖的依赖传递关系表,如下:
依赖范围 | complie | test | provided | runtime |
---|---|---|---|---|
complie | complie | test | provided | runtime |
test | - | - | - | - |
provided | - | - | - | - |
runtime | runtime | test | provided | runtime |
通过关系表可以简单清晰的了解传递范围对传递依赖的关系,默认的依赖范围以直接依赖为主,否则参考关系表,比如:第一个项目的依赖范围定义为runtime,第二个项目的间接依赖的依赖范围定义为test,那么这个依赖范围在第二个项目的中属于test。
可选依赖
当项目中不需要某个依赖时,通过配置可选依赖进行排除,可选依赖不会被传递。
前面讲解的依赖传递(optional)默认可选依赖为false,如果不想将依赖进行传递,可设为true。
helloMaven项目,示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
helloMaven2项目,示例代码如下:
<dependencies>
<dependency>
<groupId>com.study.test</groupId>
<artifactId>helloMaven</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
红框所示,helloMaven项目中的junit没有传递到helloMaven2项目中。
排除依赖
或许可选依赖并不能满足多个项目的灵活性,我们可以通过排除依赖的方式,对指定项目的某个具体的Jar包进行依赖排除。
helloMaven项目,示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
helloMaven2项目,示例代码如下:
<dependencies>
<dependency>
<groupId>com.study.test</groupId>
<artifactId>helloMaven</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
红框所示,helloMaven项目中的junit没有传递到helloMaven2项目中,这样给项目带来更多的灵活性。
我们可以清晰的知道,可选依赖和排除依赖的区别,可选依赖是统一配置,也就导致灵活性的缺失;排除依赖对应不同项目间的依赖就比较的灵活,可自由配置。不过需要注意可选依赖的优先级高于排除依赖,若对于同一个间接依赖同时使用排除依赖和可选依赖进行设置,那么可选依赖的取值必须为 false,否则排除依赖无法生效。
依赖调解
当一个间接依赖存在多条引入路径时,为了避免出现依赖重复的问题,Maven 通过依赖调节来确定间接依赖的引入路径。依赖调节遵循以下两条原则:
- 引入路径短者优先
还是用之前的案例,假设现在存在这样一个关系:
helloMaven2项目->junit
helloMaven2项目->helloMaven项目->junit
helloMaven项目,示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
helloMaven2项目,示例代码如下:
<dependencies>
<dependency>
<groupId>com.study.test</groupId>
<artifactId>helloMaven</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
</dependency>
</dependencies>
helloMaven项目路径长度为2,helloMaven2项目路径长度为1,红框所示,helloMaven2项目中的junit最终引用3.8.2版本。
- 先声明者优先
如果路径都一样长的话,第一原则就无法起作用,假设现在存在这样一个关系:
helloMaven2项目->helloMaven项目->junit4.12
helloMaven2项目->helloMaven3项目->junit3.8.2
helloMaven项目,示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
helloMaven3项目,示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
helloMaven2项目,示例代码如下:
<dependencies>
<dependency>
<groupId>com.study.test</groupId>
<artifactId>helloMaven</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.study.test</groupId>
<artifactId>helloMaven3</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
helloMaven项目路径长度为1,helloMaven3项目路径长度为1,长度相同的前提下,顺序靠前的优先使用。红框所示,helloMaven2项目中的junit最终引用4.12版本。
以上两条原则,优先使用第一条原则解决,第一条原则无法解决,再使用第二条原则解决。
- 覆盖策略
同一个pom.xml文件下,出现不同版本的依赖,依赖调解两大原则都不起作用,假设存在这样一个关系:
helloMaven2项目->junit4.12
helloMaven2项目->junit3.8.2
示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
</dependency>
</dependencies>
红框所示,helloMaven2项目最终引入3.8.2的版本。
归类依赖
当依赖包很多时,会出现很多版本号相同的情况,我们可以将这些版本号统一管理,如果多个版本号一起变动的时候,可以方便修改。示例代码如下:
<dependencies>
<dependency>
<groupId>com.study.test</groupId>
<artifactId>helloMaven</artifactId>
<version>${helloMaven.version}</version>
</dependency>
<dependency>
<groupId>com.study.test</groupId>
<artifactId>helloMaven3</artifactId>
<version>${helloMaven.version}</version>
</dependency>
</dependencies>
通过<properties>
标签,可以定义对应的版本号,通过占位符使用。
properties属性
归类依赖篇章中简单介绍了占位符的使用,使用${}
当作占位符,通过再<properties>
标签中定义变量。
Maven总共有5类属性:POM属性、自定义属性、Settings属性、Java系统属性和环境变量属性。
- POM属性
(1)${project.basedir}
:这引用了module/project的根文件夹(当前pom.xml文件所在的位置),还可以简化的写法:${basedir}
。
(2)${project.build.sourceDirectory}
:项目的主源码目录,默认为src/main/java/。
(3)${project.build.testSourceDirectory}
:项目的测试源码目录,默认为src/test/java/。
(4)${project.build.directory}
: 项目构建输出目录,默认为target/。
(5)${project.outputDirectory}
: 项目主代码编译输出目录,默认为target/classes/。
(6)${project.testOutputDirectory}
:项目测试主代码输出目录,默认为target/testclasses/。
(7)${project.groupId}
:项目的groupId。
(8)${project.artifactId}
:项目的artifactId。
(9)${project.version}
:项目的version,与${version}
等价。
(10)${project.build.finalName}
:项目打包输出文件的名称,默认为${project.artifactId}
-${project.version}
。
- Settings属性
以settings.
开头的属性引用settings.xml文件中的XML元素的值。
- Java系统属性
所有Java系统属性都可以用Maven属性引用,如${user.home}
指向了用户目录。
- 环境变量属性
所有环境变量属性都可以使用以env.
开头的Maven属性引用,如${env.JAVA_HOME}
指代了JAVA_HOME环境变量的值。
占位符除了可以再pom.xml文件中使用,还可以再.properties
、.yml
等文件中使用。
- 自定义属性
再<properties>
标签中定义。
子模块 helloMaven-server,示例代码如下:
<?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">
<parent>
<artifactId>helloMaven</artifactId>
<groupId>com.study.test</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>helloMaven-server</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
.properties
文件
maven.compiler.target=${maven.compiler.target}
maven.compiler.target1=${maven.compiler.target1}
执行mvn install
命令,再target->classes目录下,可以看到编译后的.properties
文件,如图
可以看到pom文件中定义的占位符,编译后自动替换成了对应的值。
插件(plugin)
Maven 实际上是一个依赖插件执行的框架,它执行的每个任务实际上都由插件完成的。Maven 的核心发布包中并不包含任何 Maven 插件,它们以独立构件的形式存在, 只有在 Maven 需要使用某个插件时,才会去仓库中下载。
执行mvn clean install
命令,Maven 默认为一些核心的生命周期阶段绑定了插件目标,当用户调用这些阶段时,对应的插件目标就会自动执行相应的任务。
生命周期
Maven的生命周期将项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建过程进行了抽象和统一。
Maven 生命周期是抽象的,其本身不能做任何实际工作,这些实际工作(如源代码编译)都通过调用 Maven 插件 中的插件目标(plugin goal)完成的。
Maven 拥有三套标准的生命周期:
- clean: 在进行真正的构建之前进行一些清理工作。删除前一次构建在target文件夹下生成的各个Jar包等,它可以分为三个阶段:
(1)pre-clean:清理前
(2)clean:清理
(3)post-clean:清理后
示例代码如下:
<build>
<plugins>
<!-- 添加插件 maven-antrun-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<!--自定义阶段 id -->
<id>pre-clean</id>
<!--阶段 -->
<phase>pre-clean</phase>
<!--目标 -->
<goals>
<goal>run</goal>
</goals>
<!--配置 -->
<configuration>
<!-- 执行的任务 -->
<tasks>
<!-- 输出自定义文本信息 -->
<echo>预清理阶段</echo>
</tasks>
</configuration>
</execution>
<execution>
<!--自定义阶段 id -->
<id>clean</id>
<!--阶段 -->
<phase>clean</phase>
<!--目标 -->
<goals>
<goal>run</goal>
</goals>
<!--配置 -->
<configuration>
<!-- 执行的任务 -->
<tasks>
<!--自定义文本信息 -->
<echo>清理阶段</echo>
</tasks>
</configuration>
</execution>
<execution>
<!--自定义阶段 id -->
<id>post-clean</id>
<!--阶段 -->
<phase>post-clean</phase>
<!-- 目标 -->
<goals>
<goal>run</goal>
</goals>
<!--配置 -->
<configuration>
<!-- 执行的任务 -->
<tasks>
<!-- 执行的任务 -->
<echo>清理后阶段</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
输入mvn post-claen
命令,一般情况下不会执行post-clean
阶段,如图
- default:定义了构建项目时所需要的执行步骤,它是所有生命周期中最核心部分,比如:编译,测试,打包,部署等等。
简单讲解部分流程,示例代码如下:
<build>
<plugins>
<!-- 添加插件 maven-antrun-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<!--自定义阶段 id -->
<id>validate</id>
<!--阶段 -->
<phase>validate</phase>
<!--目标 -->
<goals>
<goal>run</goal>
</goals>
<!--配置 -->
<configuration>
<!-- 执行的任务 -->
<tasks>
<!-- 输出自定义文本信息 -->
<echo>校验阶段</echo>
</tasks>
</configuration>
</execution>
<execution>
<!--自定义阶段 id -->
<id>compile</id>
<!--阶段 -->
<phase>compile</phase>
<!--目标 -->
<goals>
<goal>run</goal>
</goals>
<!--配置 -->
<configuration>
<!-- 执行的任务 -->
<tasks>
<!--自定义文本信息 -->
<echo>编译阶段</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
执行mvn package
命令,如图
我们可以看到打包的完整流程,最开始从校验开始、然后编译、等,到最后完成打包。比较重要的一些步骤命令如下:
- validate:验证工程是否正确,所有需要的资源是否可用。
- initialize:做构建前的初始化操作,比如初始化参数、创建必要的目录等
- compile:编译项目的源代码。
- test:使用合适的单元测试框架来测试已编译的源代码。这些测试不需要已打包和布署。
- package:把已编译的代码打包成可发布的格式,比如jar。
- verify:运行所有检查,验证包是否有效且达到质量标准。
- install:把包安装到maven本地仓库,可以被其他工程作为依赖来使用。
- deploy:在集成或者发布环境下执行,将最终版本的包拷贝到远程的repository,使得其他的开发者或者工程可以共享。
比如:mvn test
就是在调用default生命周期的test阶段,实际执行了validate到test阶段之间的所有阶段,如图所示:
比如:mvn clean package
就是先调用clean生命周期的pre-clean和clean阶段,再调用default生命周期的validate到package阶段,如图所示:
执行完后,可以看到项目中的target目录中生成了对应的Jar包,如图所示:
- site:目的是建立和发布项目站点,生成一个站点,方便团队交流和发布项目信息。一共有四个阶段:
(1)pre-site:执行生成站点前的准备工作,比如下载一些依赖包。
(2)site:生成项目站点文档。
(3)post-site:执行生成站点后需要收尾的工作。
(4)site-deploy:将生成的站点发布到服务器上。
执行mvn site
命令,示例代码如下:
<build>
<plugins>
<!-- 添加如下两个插件 maven-site-plugin,不使用默认jar包,否则报找不到jar包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<!-- 添加插件 maven-antrun-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<!--自定义阶段 id -->
<id>pre-site</id>
<!--阶段 -->
<phase>pre-site</phase>
<!--目标 -->
<goals>
<goal>run</goal>
</goals>
<!--配置 -->
<configuration>
<!-- 执行的任务 -->
<tasks>
<!--自定义文本信息 -->
<echo>站点前阶段</echo>
</tasks>
</configuration>
</execution>
<execution>
<!--自定义阶段 id -->
<id>site</id>
<!--阶段 -->
<phase>site</phase>
<!--目标 -->
<goals>
<goal>run</goal>
</goals>
<!--配置 -->
<configuration>
<!-- 执行的任务 -->
<tasks>
<!--自定义文本信息 -->
<echo>生成站点阶段</echo>
</tasks>
</configuration>
</execution>
<execution>
<!--自定义阶段 id -->
<id>post-site</id>
<!--阶段 -->
<phase>post-site</phase>
<!--目标 -->
<goals>
<goal>run</goal>
</goals>
<!--配置 -->
<configuration>
<!-- 执行的任务 -->
<tasks>
<!--自定义文本信息 -->
<echo>站点后阶段</echo>
</tasks>
</configuration>
</execution>
<execution>
<!--自定义阶段 id -->
<id>site-deploy</id>
<!--阶段 -->
<phase>site-deploy</phase>
<!--目标 -->
<goals>
<goal>run</goal>
</goals>
<!--配置 -->
<configuration>
<!-- 执行的任务 -->
<tasks>
<!-- 输出自定义文本信息 -->
<echo>站点发布阶段</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
执行结果如图:
我们找到target目录里的site文件,打开index.html站点页面,如图所示:
phase(阶段)和goal(目标)
Maven对一些生命周期的phase(阶段) 默认绑定了插件目标,每个phase(阶段)会挂接一到多个goal(目标)。goal(目标)是Maven里定义任务的最小单元。可以把插件理解为一个类,构建目标是类中的方法,构建阶段是是对这些方法的顺序调用。
有很多插件明确要求你要定义你的目标,下面讲解下如何查看插件目标:
- 执行
mvn help:describe -Dplugin='org.apache.maven.plugins:maven-shade-plugin'
如图,红框所示,比如:shade:shade
,右边的值就是对应的goal的值。
- 使用编辑器查看,比如:idea。
如图,红框所示,阶段:目标
。
configuration
对当前插件进行配置,并不是所有插件的配置都是一样的,但是它们之间确实是有相同的配置,如果你引用了非当前插件的配置,可能会导致意外的错误,或许你可以使用idea等编辑器查看有哪些配置,如图
简单列举一些插件的配置,示例代码如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<!--声明源代码的jdk版本-->
<source>1.8</source>
<!--声明生成目标的jdk版本-->
<target>1.8</target>
<!--声明编译文件的编码-->
<encoding>UTF-8</encoding>
<!-- 指定打包的jar包输出路径 -->
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<excludes>
<exclude>**/*.properties</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<tasks>
<echo>打印:test</echo>
</tasks>
</configuration>
</plugin>
</plugins>
</build>
如果你对这些配置标签有所不理解,可以翻阅官方文档,它会告诉你每个插件的配置标签,和一些代码示例。
Archetype(原型/模板)
Maven 的所有功能都是通过插件实现的,Archetype 也不例外,由一个名为 maven-archetype-plugin 的插件实现的,该插件提供了 ArcheType 的所有功能。
Archetype 是 Maven 项目的模板工具包,它定义了 Maven 项目的基本架构。Archetype 为开发人员提供了数千种创建 Maven 项目的模板,Maven 通过这些模板可以帮助用户快速的生成项目的目录结构以及 POM 文件。
以Idea创建为例,如图所示:
在红框处,选择模板(maven-archetype-quickstart),点击create按钮,等待生成项目。
生成好的项目,我们可以看到再main文件和test文件下生成了默认的初始类,不仅如此,pom文件中也加入很多插件。除此之外还有很多的模板,参考网上其它文章,如图所示:
占位符替换
spring-boot为了保护application.yml和application.properties,修改了默认的占位符${*}
为@*@
,我们也可以修改默认占位符格式,示例代码如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<configuration>
<!--是否使用默认占位符-->
<useDefaultDelimiters>false</useDefaultDelimiters>
<delimiters>
<!--占位符格式,*号代表内容-->
<delimiter>$[*]</delimiter>
</delimiters>
<!--占位符中value的编码方式-->
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
当格式修改为$[]
,执行mvn install
命令,可以看到能够正常替换值,说明格式替换成功。
打包
默认Maven打包的结果只包含项目本身的代码,不会包含依赖,如图:
在真正的开发中,会包含很多依赖,如果不对这些依赖打包,项目运行过程中就会出错。
在Maven中,主要有3个插件(当然还有很多框架打包插件)可以用来打包:
- maven-jar-plugin插件:默认的打包插件,用来打普通的项目Jar包。
示例代码如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
</plugin>
</plugins>
</build>
执行mvn install
命令,结果如图
执行结果如上图所示。
- maven-assembly-plugin插件:定制化打包方式,对项目目录的重新组装。assembly配置分为两部分,一部分是assembly的配置文件,一部分是在pom.xml中的配置。
示例代码如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<!-- 这个是assembly 所在位置 -->
<descriptor>src/main/resources/assembly.xml</descriptor>
</descriptors>
</configuration>
</plugin>
</plugins>
</build>
assembly.xml,示例代码如下:
<assembly>
<formats>
<!--支持 zip,tar,tar.gz,tar.bz2,jar,dir,war 等 -->
<format>tar.gz</format>
<format>zip</format>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<!--文件集,可用于不同文件存放路径 -->
<fileSets>
<fileSet>
<!--资源路径-->
<directory>src/main/resources</directory>
<!--输出目录-->
<outputDirectory>conf</outputDirectory>
<!--文件的权限-->
<fileMode>0644</fileMode>
</fileSet>
</fileSets>
<!--依赖jar包以及项目打包文件存储文件-->
<dependencySets>
<dependencySet>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>
执行 mvn assembly:assembly -Dmaven.test.skip=true
命令或者mvn package
命令,执行结果如图:
assembly插件把本身项目也当作依赖进行了打包,里面存放着对应的代码。
maven-shade-plugin插件
:简单的依赖打包。
示例代码如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<id>shade-plugin</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<!--排除以下文件,防止程序启动启动时,校验错误-->
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<!--资源转换器:定义程序入口,除此之外还可以合并相同文件名资源-->
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.study.test.HelloMaven</mainClass>
</transformer>
</transformers>
</configuration>
</plugin>
</plugins>
</build>
执行mvn package
命令,结果如图
图中我们看到生成文件目录下有两个文件,一个是原Jar包文件,里面不包含依赖,一个是shade插件生成的文件,与assembly插件不同,依赖并不是以Jar包的形式,而是.class
文件进行引入。
profile
一个项目有不同的运行环境,比如,开发环境,测试环境、生产环境。不同的环境有不同的配置。每次将项目部署到不同的环境时,都需要修改相应的配置,这样重复的工作,不仅浪费劳动力,还容易出错。为了解决这一问题,Maven 引入了 Profile 的概念,通过它可以为不同的环境定制不同的构建过程。
示例代码如下:
<!--定义资源位置-->
<build>
<resources>
<resource>
<directory>src/main/resources/</directory>
<filtering>true</filtering>
<includes>
<include>${profile.active}.properties</include>
</includes>
</resource>
</resources>
</build>
<!--定义配置环境-->
<profiles>
<profile>
<id>dev</id>
<properties>
<profile.active>dev</profile.active>
</properties>
</profile>
<profile>
<id>test</id>
<!--设置默认环境-->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<profile.active>test</profile.active>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<profile.active>prod</profile.active>
</properties>
</profile>
</profiles>
其原理很简单通过<profiles>
标签配置不同环境标识和命令,然后通过<resources>
标签定义资源位置,在<includes>
标签中使用占位符,灵活打包配置文件。
简单介绍下<resources>
标签结构含义:
<build>
<resources>
<resource>
<directory></directory>
<filtering></filtering>
<includes>
<include></include>
</includes>
<excludes>
<exclude></exclude>
</excludes>
</resource>
</resources>
</build>
<directory>
:资源文件目录。<filtering>
:布尔值,打包时的配置文件中是否进行变量替换,与<filters>
标签使用。<includes>
:指定打包文件。<excludes>
:指定不打包文件。
我们执行mvn clean test -Ptest
命令,激活配置,如图所示,可以看到打包了test的配置文件:
激活的方式有很多种,比如:
- 命令行激活:id为自定义的值,执行多个profile用
,
分开。
mvn clean install -Pid,id
- settings.xml 文件显示激活
<activeProfiles>
<activeProfile>test</activeProfile>
</activeProfiles>
- 操作系统环境激活
<activation>
<os>
<name>Windows 10</name>
<family>Windows</family>
<arch>amd64</arch>
<version>10.0</version>
</os>
</activation>
- 文件存在与否激活
<activation>
<file>
<exists>./src/main/resources/env.prod.properties</exists>
<missing>./src/main/resources/env.test.properties</missing>
</file>
</activation>
- 默认激活
<activation>
<activeByDefault>true</activeByDefault>
</activation>
除了使用Maven方式调用不同配置环境,还有很多方法,简单列举常见的:
- 使用java命令指定配置文件
java -jar 生成的jar包 --spring.profiles.active=prod
- idea设置启动环境
- springboot-application.yml多环境配置
修改application.yml文件中的active值,比如active: test
就是将读取application-test.yml文件,示例代码如下:
spring:
profiles:
active: test
依赖管理和插件管理
- dependencyManagement依赖管理
在前面依赖范围:import,篇章中有介绍到依赖管理的使用,在该元素下声明的依赖不会实际引入到模块中,只有在 dependencies 元素下同样声明了该依赖,才会引入到模块中。若未指定版本,则使用 dependencyManagement 中指定的版本,否则将覆盖 dependencyManagement 中的版本。
以继承项目为例,父项目helloMaven,示例代码如下:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</dependencyManagement>
子模块helloMaven-server,示例代码如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
项目结构如下,可以看到红框所示部分,引入了junit的包,且代码也能正常使用。
- pluginManagement插件管理
pluginManagement 元素与 dependencyManagement 元素的原理十分相似,主要用于Maven插件的管理。如图,我们可以看到helloMaven-server项目编译时,Maven默认的插件版本
以继承项目为例,父项目helloMaven,示例代码如下:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
子模块helloMaven-server,示例代码如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
</plugins>
</build>
执行 mvn install
命令后,如图所示,插件的版本改变
仓库管理
仓库顾名思义是用来存储东西的地方, 在 Maven 中任何一个依赖、插件或者项目构建的输出,都可以称之为 “构件” 。Maven 仓库能帮助我们管理构件(主要是JAR)。
当 Maven 根据坐标寻找构件时,它会首先查看本地仓库,若本地仓库存在此构件,则直接使用;若本地仓库不存在此构件,Maven 就会去远程仓库查找,若发现所需的构件后,则下载到本地仓库使用。如果本地仓库和远程仓库都没有所需的构件,则 Maven 就会报错。本地仓库就像一个 “缓存”,从远程仓库下载 Jar 包到本地仓库中以备将来使用,本地仓库会随着项目的积累越变越大。
Maven 仓库可以分为 2 个大类:
- 本地仓库
默认情况下,不管在Window还是Linux下,每个用户在自己用户目录下都有一个路径名为.m2/repository/
的仓库目录。在%M2_HOME%\conf
目录下的settings.xml中,可以配置本地仓库位置,示例代码如下:
<localRepository>C:\Users\.m2</localRepository>
- 远程仓库
远程仓库还可以分为 3 个小类:中央仓库、私服、其他公共仓库。
(1)中央仓库
中央仓库是由 Maven 社区提供的一种特殊的远程仓库,它包含了绝大多数流行的开源构件。在默认情况下,当本地仓库没有 Maven 所需的构件时,会首先尝试从中央仓库下载。可通过 http://search.maven.org/ 或者http://mvnrepository.com来访问。
(2)私服
私服一般是指公司内部使用的仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,它是开发人员自己定制仓库,运行 Maven 时,Maven 所需要的任何构件都是从本地仓库获取的,但是如果本地仓库没有,它就会尝试从远程仓库中下载构件到本地仓库。如果 Maven 无法连接到远程仓库,将无法正常构建项目。
配置示例代码如下:
<!-- 配置远程仓库 -->
<repositories>
<repository>
<id>maven-repo</id>
<name>Repository</name>
<url>http://192.168.16.10/maven2/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</releases>
<snapshots>
<enabled>false</enabled>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
<layout>default</layout>
</repository>
</repositories>
(3)其他公共仓库
除了中央仓库、私服外,还有公共仓库,可能在中央仓库中没有,示例代码如下:
<!-- 设定远程主仓库,按设定顺序进行查找。 -->
<repositories>
<repository>
<id>oschina-repos</id>
<name>Oschina Releases</name>
<url>http://maven.oschina.net/content/groups/public</url>
</repository>
<repository>
<id>aliyun-repos</id>
<name>aliyun Releases</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
</repositories>
章节目录
上一章:项目管理工具:Maven(上)
下一章:项目管理工具:Maven-Nexus(下)