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 compile
clean
是让 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
依赖类型,默认是jar
scope
依赖范围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
生命周期是最核心的部分,它一共有如下阶段
validate
initialize
generate-sources
process-sources
处理主资源文件,一般是src/main/resources
目录下的文件。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
使用单元测试框架运行测试。prepare-package
package
接受编译好的代码,打包成可发布格式。pre-integration-test
integration-test
post-integration-test
verify
install
将包安装到 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。