Maven 这个词可以翻译为专家的意思。它是由 Apache 组织的开源,主要服务 Java 平台项目的构建、依赖管理和项目信息管理。
有了 Maven 我们只需编写一些配置然后运行一条命令就可以自动完成项目编译、测试、打包、发布等流程。
安装
Maven 需要依赖 Java 环境,所以首先要确认安装了 Java,首先去官网下载 Maven,然后就可以把它随便解压到一个文件夹,并把这个文件夹路径设置为 M2_HOME 环境变量,最后将 %M2_HOME%bin(Windows)加入到 PATH,Linux 为 export PATH=PATH:M2_HOME/bin。
代码语言:javascript复制mvn -v # 在命令行运行这条命令,查看 Maven 版本对于升级就是重复上面的流程。
Maven 安装目录下的 conf 文件下存放着 Maven 的配置 settings.xml,它的作用域是全局的,我们可以复制它到 ~/.m2 下,用户目录下的 settings.xml 修改只对当前的用户有作用。Maven 的依赖包仓库放在,~/.m2 文件夹下的 repository 文件夹中。
因为 Maven 实际上执行的是 Java 命令,我们可以通过 MAVEN_OPT 环境变量设置它的参数。通常需要设置它的值为 -Xms128m -Xmx512m 因为对于大点的项目可能出现内存不够的错误。
对于编辑器中的 Maven 我们可以设置它使用我们下载的 Maven,这样就可以避免两个 Maven 版本不一致而造成的构建行为不一致。
入门
对于 Maven 项目,最核心的就是 pom.xml (Project Object Model) 我们需要把项目的构建配置信息都写在里面。
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hello world project</name>
<description>Demo project</description></project>第一行是 XML 头,指定了 XML 版本和文件编码。
然后就是 project 元素,它是配置文件的根元素,它还声明了 POM 的命名空间。
然后就是 modelVersion 对于 Maven2 和 Maven3 它只能是 4.0.0 版本。
groupId, artifactId 和 version 定义了一个项目的基本坐标。
groupId 定义了项目属于哪个组,它通常和 Java 中包名命名一样,例如 a 公司启动了一个 myapp 项目,那么他的 groupId 就可能是 com.a.myapp。
artifactId 定义了当前 Maven 项目在组中唯一的 ID,因为一个项目可能有多个子项目或模块。
version 指定了当前项目的版本。SNAPSHOT 为快照版本。
name 给项目更友好的名称,description 是对项目的描述。
上面这些字段定义了项目基本的信息,下面我们就可以编写项目代码了。
代码语言:javascript复制package com.demo.helloworld;public class HelloWorld { public String sayHello() { return "hello world";
} public static void main(String[] args) {
System.out.println(sayHello());
}
}Maven 采用约定大于配置的方式,在大多数的情况下项目源码应该放在项目文件夹下的 src/main/java 下(Maven 会自动在该目录下搜寻源码),资源放在 src/main/resources 下,测试代码放在 src/test/java 下。
我们的包名也应该和 groupId 和 artifactId 相吻合。
然后执行
代码语言:javascript复制mvn clean compileclean 是让 Maven 清除项目输出 target 目录。compile 任务用来将项目编译到 target/classes 目录下。
然后我们用 JUnit 编写单元测试,首先需要在 pom.xml 加上 junit 的依赖。
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hello world project</name>
<description>Demo project</description>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies></project>引入一个依赖我们需要填写它的基本坐标,有了这个坐标,Maven 就会自动取中央仓库下载,这个依赖到 ~/.m2/repository 文件夹下。
scope test 是表示依赖只对测试有效,在主代码中引入 junit 会报错。
package com.demo.helloworld;import org.junit.Test;import static org.junit.Assert.assertEquals;public class HelloWorldTest { @Test
public void test() { HelloWorld helloWorld = new HelloWorld();
assertEquals("hello world", helloWorld.sayHello());
}
}然后我们需要对 Maven 的编译插件进行一些配置,因为它默认只支持 Java 1.5 所以我们需要配置它为更高版本的 Java
代码语言:javascript复制<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hello world project</name>
<description>Demo project</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies></project>然后执行
代码语言:javascript复制mvn clean test它会编译测试文件然后运行测试,最后我们就能够看到测试通过的输出。
下一步就是对项目打包,因为我们没有指定项目打包的类型,所以默认就是 jar。
mvn clean package进行打包之前 Maven 会自动帮我们编译,测试,通过了就打成 jar 包,放在 target 目录下,名称为 hello-world-0.0.1-SNAPSHOT.jar 它是根据 artifactId 和 version 还有打包类型进行命名。
test 会自动帮我们执行 compile , package 会自动帮我们执行 test,install 会自动帮我们执行 package, install 是将项目安装到仓库中。
我们还可以使用 archetype 插件来生成项目骨架,执行 mvn archetype:generate 命令就可以了,当然也可以使用编辑器新建一个 Maven 项目来选择项目模板。
坐标
Maven 是通过坐标找到一个依赖的,而一组坐标是通过一些元素定义的。
groupId一个组织的的一个实际项目artifactId实际项目中的一个 Maven 项目或模块version版本packaging打包方式有jar,war等classifier用来定义构建输出的附属构建 如hello-world-1.0.0-javadoc.jar,它里面包含了 Java 文档,javadoc就是就是附属构建的classifier。
前三个必须定义,packaging 默认 jar,classifier 不能直接定义。
依赖
一个项目依赖需要放在 dependencies 中,dependency 有几个子元素。
groupId,artifactId和version是项目基本坐标。type依赖类型,默认是jarscope依赖范围optional是否可选exclusions用来排除传递依赖
其中依赖范围有几个值可以选。依赖范围主要是控制编译, 测试 和 运行 的 classpath
compile默认,在编译,测试和运行都有效test只对测试 classpath 有效,如 junit 它只要在测试的时候能用到就行。provided对编译和测试有效,比如servlet-api,因为运行时容器都会提供,所以无需重复引入。runtime运行时依赖,对测试和运行时有效,在编译时无效,比如 jdbc 驱动实现,只有在需要运行的时候在需要特定的驱动实现。system系统依赖范围,它与provided依赖范围完全一致,只是它的依赖必须使用systemPath显式的指定依赖路径,它不是通过 Maven 仓库解析。
<dependencies>
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
</dependencies>还有一种 import 为导入依赖范围,不会对上面三个依赖范围产生影响。
传递性依赖
传递依赖就是比如我们依赖 spring-core (compile) 但是 spring-core 依赖 commons-logging (compile),那么我们的项目也依赖 commons-logging (compile)。有了这个机制,我们就不用考虑 spring 依赖什么,不必手动安装它的依赖,Maven 会自动将必要的间接依赖引入当前项目。spring boot 的起步依赖就是利用 Maven 的传递依赖。
依赖范围也会对传递依赖产生影响。

左侧表示直接依赖,上面是代表间接依赖,中间就表示传递依赖范围。
依赖调解
比如我们项目传递依赖中有两个依赖是一样的但是它的版本不一样,那么 Maven 就会看谁的路径最短,最短的优先。
如果它们是一样长的,Maven 就查看 POM 中的依赖声明谁在前面谁就优先。
可选依赖
如果我们项目依赖 A(compile),A 依赖 B(compile) 和 C (compile),但是 B 和 C 定义为可选的,那么依赖就不会被传递。依赖可选可以通过 <optional>true</optional> 指定。
排除依赖
如果我们想排除一个传递依赖,比如 spring boot 默认是使用的 jackson,如果我们想用 gson,那么我们就可以将 jackason 排除,然后显式的引入 gson。
代码语言:javascript复制<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency></dependencies>归并依赖
如果我们依赖一个项目的很多模块,因为是一个项目所以版本号都是一样的,这样我们就要给每个依赖填写一样的版本号,升级的话又要一个一个的改。
这时候我们就可以声明一个变量,然后其他地方直接使用就行了。
代码语言:javascript复制<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hello world project</name>
<description>Demo project</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>2.5.6</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies></project>${} 可以引入 Maven 的属性。
仓库
Maven 中任何一个项目或插件的输出都称为构件。任何构件都会有一个唯一的坐标。为了重用 Maven 的依赖都统一放在仓库中,而不是每个项目都有个 lib 一样的文件夹来装它的依赖。
Maven 的仓库分为远程仓库和本地仓库,Maven 会首先通过坐标去本地仓库寻找依赖,如果没有就去远程仓库下载依赖,然后在放入本地仓库再使用。如果都没有的话那么就会报错。
我们可以自定义远程仓库,Maven 自带了一个远程仓库,它包含绝大部分的构件,默认情况都会去这个中央仓库下载构件。
私服是另一种远程仓库,为了节约宽带和时间,在局域网中搭建一个私有仓库,用其代理外部远程仓库,内部项目还可以安装到私服上供其他项目使用。
除了上面两种还有其他的公开远程仓库。比如 jboss repository 等。
本地仓库
本地仓库默认位置是当前用户目录下的 .m2/repository 文件夹,如果我们想更改它的位置可以修改 .m2/settings.xml 文件。
<settings>
<localRepository>D:mavenrepository</localRepository></settings>如果我们本地有两个项目 A 和 B ,项目 B 依赖于 A,那么我们可以将项目 A 安装到本地仓库,这样我们就可以在 B 项目中依赖 A 了,我们可以在 A 项目中执行 mvn clean install 来将 A 项目安装到本地仓库。
远程仓库
Maven 需要知道最少一个远程仓库,这样 Maven 才能下载构件到本地。中央仓库就是默认的远程仓库,所有 Maven 项目 pom.xml 会继承一个超级POM,它就在 Maven 安装目录下的 lib/maven-model-builder-3.6.1.jarorgapachemavenmodel 文件夹,名为 pom-4.0.0.xml。
<?xml version="1.0" encoding="UTF-8"?><project>
<modelVersion>4.0.0</modelVersion>
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
</project>私服
私服是特殊远程仓库,它代理多个外部远程仓库,我们使用私服来下载构件,私服上如果没有就会取远程下载,然后缓存起来。

配置
如果我们需要的构件不在中央仓库而在另外一个仓库,我们就可以在 pom.xml 中配置该仓库。
<repositories>
<repository>
<id>jboss</id>
<name>JBoss Repository</name>
<url>http://repository.jboss.org/maven2/</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository></repositories>其中 id 必须时唯一的,如果有仓库声明和中央仓库一样的 id 就会覆盖它,url 就是仓库地址,releases 和 snapshots 分别用来控制发布版和快照版构件的下载,enabled 为 true 表示开启下载,false 表示关闭下载。
快照版本是表示开发中的版本,开发中项目会平凡的变化,比如我们开发一个项目中一个模块,但是它要依赖另一个模块,我们就将它安装到本地依赖,这样就可以在我们项目中使用,但是如果依赖项目变了,但是我们还是会使用缓存本地的模块,这时候就要使用 snapshot 版本了,对于快照版本 Maven 每次会去检查当前是不是最新的,如果不是就下载最新的代码。
snapshots 可以设置 Maven 检查更新频率。
<snapshots>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>ignore</checksumPolicy></snapshots>never 从不,always 每次构件都去检查,daily 每天(默认值)。
checksumPolicy 是 Maven 下载构件时会校验构件,默认时 warn 警告,还有 fail 项目构建会失败,ignore 忽略。
远程仓库验证
对于组织内部的仓库往往需要认证才运行访问,我们可以在 settings.xml 中设置仓库的账号和密码。
<servers>
<id>repo</id>
<username>username</username>
<password>password</password></servers>其中 id 和我们定义远程仓库 id 对应。
部署到远程仓库
私服的一个作用就是用来部署第三方构件,Maven 可以帮助我们将构件部署到仓库中。
代码语言:javascript复制<project>
<distributionManagement>
<repository>
<id>id</id>
<name>Release Repository</name>
<url>http://196.0.0.1/path/to/release</url>
</repository>
<snapshotRepository>
<id>snapshot</id>
<name>Snapshot Repository</name>
<url>http://196.0.0.2/path/to/release</url>
</snapshotRepository>
</distributionManagement></project>repository 表示发布版的仓库,snapshotRepository 表示快照仓库,id 是唯一标识,我们可以通过它来设置账号和密码。
然后我们可以执行如下命令发布
代码语言:javascript复制mvn clean deploy仓库解析依赖机制
当依赖返回是 system 时 Maven 回去本地寻找。
当是显式版构件时 如 1.2, 1.3-beta-1 等,Maven 会去所有远程仓库下载到本地。
当依赖版本是 RELEASE LATEST 和 SNAPSHOT 时会根据更新策略去所有远程仓库搜寻构件元数据,然后和本地的元数据合并,再通过合并后的值取寻找版本。
镜像
我们还可以在 settings.xml 中设置镜像镜像服务器。
<mirrors>
<mirror>
<id>maven.net.cn</id>
<name> maven central mirror</name>
<url>http://maven.net.cn/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror></mirrors>我们上面给中央仓库设置一个镜像,我们也可以设置 mirrorOf 为 * 表示匹配所有远程仓库。
生命周期和插件
Maven 有 3 套生命周期,分别是 clean、default 和 site,Maven 的生命周期是抽象的存在,就像一个接口,它把实际工作交给个插件。这 3 套生命周期是相互独立的。
每个生命周期都有一些阶段(phase),如 clean 生命周期有 3 个阶段,pre-clean, clean 和 post-clean,阶段是有顺序的,当执行 clean 阶段,会执行它前面的 pre-clean。
clean 生命周期的阶段一共有
pre-clean执行清理前需要执行的工作clean清理上次构件post-clean清理过后需要执行的操作
default 生命周期是最核心的部分,它一共有如下阶段
validateinitializegenerate-sourcesprocess-sources处理主资源文件,一般是src/main/resources目录下的文件。generate-resourcesprocess-resourcescompile编译项目源码。process-classesgenerate-test-sourcesprocess-test-sources处理项目测试资源文件。generate-test-resourcesprocess-test-resourcestest-compile编译测试源码process-test-classestest使用单元测试框架运行测试。prepare-packagepackage接受编译好的代码,打包成可发布格式。pre-integration-testintegration-testpost-integration-testverifyinstall将包安装到 Maven 本地仓库,供本地其他项目使用deploy将包复制到远程仓库。
site 生命周期目的是建立和发布项目站点。
pre-site生成站点之前要执行的操作site生成项目站点文档post-site执行生成站点之后要完成的工作site-deploy将站点发布到服务器
命令行
执行 Maven 任务主要方式就是调用 Maven 的生命周期阶段。
mvn clean 就是执行 clean 生命周期的 clean 阶段
mvn test 就是执行 default 生命周期的 test 阶段
mvn clean install 就是执行 clean 生命周期 clean 阶段和 default 的 install 阶段。
插件目标
Maven 只是定义了生命周期,然而实际的工作还是要交给插件。一个插件会有一个或多个目标(goal)每个目标对应一个功能,如 surefire:test surefire 是插件名,test 是插件目标,surefire 是 Maven 默认测试插件。
Maven 的生命周期的阶段和插件的目标相互绑定,来完成实际任务。
Maven 默认为主要的生命周期阶段绑定了很多插件目标,当调用生命周期阶段时,相应的插件就会被执行。

自定义绑定
代码语言:javascript复制<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins></build>上面我们将 maven-source-plugin 的 jar-no-fork 目标绑定到了 verify 阶段。id 为任务名。
插件配置
插件也有参数,我们可以通过命令行或在 pom.xml 设置它的参数。
我们可以通过 -D参数键=参数值 来设置插件目标参数,如 mvn package -Dmaven.test.skip=true -D 是 Java 自带的,用来设置 Java 系统属性,Maven 只是重用了该参数。
在 pom.xml 中我们可以通过 configuration 设置参数。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins></build>除了在外层设置插件参数(全局),我们还可以对一个 execution 设置参数。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>ant-validate</id>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>lalala</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins></build>命令行调用插件
我们还可以通过命令行调用插件目标。
代码语言:javascript复制mvn [options] [goal<s>] [phase[s]]因为有些插件目标不适合绑定到生命周期阶段执行,所以我们可以直接在命令行执行插件的目标。
代码语言:javascript复制mvn dependency:tree # 查看项目依赖# 当然我们将插件的 groupId artifactId version 都写上我们知道插件有它的基本坐标,Maven 是如何通过 dependency 查到对应的插件呢?
因为 dependency 是 maven-dependency-plugin 插件的前缀,Maven 可以通过前缀查找到对应的 artifactId。Maven 会通过本地仓库查找插件,如果查不到就会取远程仓库查找。
对于未指定 groupId 的插件,Maven 会默认使用 org.apache.maven.plugins 作为它的 groupId。Maven 在超级POM 中设定了核心插件的版本,我们项目中就可以继承到这些版本的设定,而无需自己设置。
如果一个插件既不是核心插件又没有设定版本,那么会检查所有仓库可用版本,然后做出选择。
聚合与继承
Maven 还支持多模块开发,我们一个项目可能有很多的模块,Maven 可以将它们聚合在一起。
假如我们有一个项目 app,它分为 a、b 和 c 三个模块,也就是三个 Maven 项目,因为它们是一个项目所以它们的 groupId,version 都是一样的。
我们项目目录可能像下面这样。
代码语言:javascript复制|-app
|- a
|- src
|- pom.xml
|- b
|- src
|- pom.xml
|- c
|- src
|- pom.xml
pom.xml我们最外层有一个 pom.xml,我们用它来聚合项目中的模块。
<?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.demo.app</groupId>
<artifactId>app-aggregator</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>a</module>
<module>b</module>
<module>c</module>
</modules></project>需要将 packaging 设置为 pom,然后设定它要聚合的模块。
module 中是模块的相对路径,如果和其他模块是平行目录则路径就是 ../a 等。
现在我们就不用一个一个取构件了,我们在最外层执行 mvn clean install。Maven 会解析 pom.xml 并计算出模块的构建次序,然后顺序执行。
继承
我们发现我们的子模块有很多相同的配置,这时候我们就可以使用继承来消除重复。
我们可以再创建一个用来做 parent 的 pom.xml 也可以重用我们上面创建的 aggregator pom.xml,如果重用的话我们就无需修改它,但是需要修改要继承它的模块。
<?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>
<artifactId>app-a</artifactId>
<parent>
<groupId>com.demo.app</groupId>
<artifactId>app-aggregator</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent></project>我们使用 parent 来指明模块要继承的父级。这里的 relativePath 的默认值就是 ../pom.xml 我们也可以省略它。
现在我们继承了父级的 groupId 和 version,如果我们需要不同的值,也可以覆盖它。几乎所有的项目都可以继承父级的。
如果我们父级声明了一个依赖,那么所有子模块都会继承这个依赖,即使有的模块不需要这个依赖。
Maven 提供了 dependencyManagement 来让子模块不会引入实际依赖,只有子模块声明才会依赖。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
</dependencies></dependencyManagement>代码语言:javascript复制<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency></dependencies>有了 dependencyManagement,我们子模块不会直接继承,但是如果声明了需要继承就无需填写依赖的版本了。
spring boot 就是利用 Maven 的继承,我许让我们自己填写依赖的版本。
还有一个 pluginManagement (在 build 元素下) 它的作用和 dependencyManagement 是一样的,只是它是作用于插件。
测试
Maven 的测试是使用 maven-surefire-plugin 插件。
有时候我们想跳过测试可以在命令行加入 -DskipTests ,-Dmaven.test.skip=true 不仅跳过测试,也会跳过测试代码的编译。
我们还可以运行指定测试,如 -Dtest=*Tests 表示只运行 Tests 结尾的测试,* 匹配 0 或多个字符。还可以使用 , 分割多个参数,如 -Dtest=*Tests,*IT 。
Maven 属性
Maven pom.xml 中可以使用 ${} 来注入属性,它一共支持 6 类属性。
- 内置属性,如 {version} 项目版本 和 {basedir} 项目根目录
- POM 属性,可以引用 pom.xml 中的属性,如 {project.version}, {project.build.sourceDirectory} 等
- 自定义属性,在
properties中自定义的属性 settings.xml中的属性,如${settings.loaclRepository}。- Java 系统属性,如
${user.name} - 环境变量,如
${JAVA_HOME}
Profile
我们项目中开发环境和线上环境不同往往需要不同的配置。Maven 中的 Profile 就可以针对环境的不同使用不同的配置。
db.url=${db.url}db.password=${db.password}代码语言:javascript复制<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<db.url>dev.url</db.url>
<db.password>dev.password</db.password>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<db.url>prod.url</db.url>
<db.password>prod.password</db.password>
</properties>
</profile></profiles><!--
需要资源开启过滤,这样上面 properties 文件的 ${} 就可以注入我们的 properties 中属性了
--><build>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources></build>然后我们就可以在命令行中使用 -Pdev 来激活开发模式的配置,profile 中的配置是当激活当前 profile 才会生效的配置。
frontend-maven-plugin
有时候我们需要将前端和后端放在一起,我们就可以使用 frontend-maven-plugin , 来帮助我们安装 node npm 或 yarn 来执行 npm script。
我们只需要在前端模块中添加这个插件
代码语言:javascript复制 <plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>${frontend-maven-plugin.version}</version>
<configuration>
<installDirectory>target</installDirectory>
<nodeVersion>v13.6.0</nodeVersion>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>npm lint</id>
<goals>
<goal>npm</goal>
</goals>
<phase>compile</phase>
<configuration>
<arguments>run lint</arguments>
</configuration>
</execution>
<execution>
<id>npm run build</id>
<goals>
<goal>npm</goal>
</goals>
<phase>compile</phase>
<configuration>
<arguments>run build</arguments>
</configuration>
</execution>
<execution>
<id>npm run test</id>
<goals>
<goal>npm</goal>
</goals>
<phase>test</phase>
<configuration>
<arguments>run test</arguments>
</configuration>
</execution>
</executions>
</plugin>它会自己安装全新的 node 和 npm ,与全局的 node 隔离。然后我们使用它的 npm goal 来执行 npm 命令。 我们也可以使用 clean 插件来清理每次生成的代码。
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>${maven-clean-plugin.version}</version>
<executions>
<execution>
<id>remove existing output</id>
<phase>compile</phase>
<goals>
<goal>clean</goal>
</goals>
<configuration>
<excludeDefaultDirectories>true</excludeDefaultDirectories>
<filesets>
<fileset>
<directory>build</directory>
</fileset>
</filesets>
</configuration>
</execution>
</executions>
</plugin>dockerfile-maven-plugin
dockerfile-maven-plugin 插件可以帮助我们构建和发布 docker 镜像,而无需再手动输入命令。
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>${dockerfile-maven-version}</version>
<executions>
<execution>
<id>default</id>
<goals>
<goal>build</goal>
</goals>
<configuration>
<contextDirectory>context</contextDirectory>
<dockerfile>not-context/Dockerfile</dockerfile>
<buildArgs>
<IMAGE_VERSION>0.0.1</IMAGE_VERSION>
</buildArgs>
</configuration>
</execution>
<execution>
<id>tag</id>
<goals>
<goal>tag</goal>
</goals>
<configuration>
<repository>test/build-tag-version</repository>
<tag>${project.version}</tag>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>我们需要一个 Dockerfile 来构建我们的 image,如果是 spring boot 项目可以简单使用 fat jar 方法来构建。
代码语言:javascript复制FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]更多关于 spring boot docker 可以查看 Spring Boot Docker。


