Maven 这个词可以翻译为专家的意思。它是由 Apache 组织的开源,主要服务 Java 平台项目的构建、依赖管理和项目信息管理。
有了 Maven 我们只需编写一些配置然后运行一条命令就可以自动完成项目编译、测试、打包、发布等流程。
Maven 需要依赖 Java 环境,所以首先要确认安装了 Java,首先去官网下载 Maven,然后就可以把它随便解压到一个文件夹,并把这个文件夹路径设置为 M2_HOME 环境变量,最后将 %M2_HOME%\bin(Windows)加入到 PATH,Linux 为 export PATH=PATH:M2_HOME/bin。
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
是对项目的描述。
上面这些字段定义了项目基本的信息,下面我们就可以编写项目代码了。
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
相吻合。
然后执行
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
<?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>
然后执行
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。
<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>
如果我们依赖一个项目的很多模块,因为是一个项目所以版本号都是一样的,这样我们就要给每个依赖填写一样的版本号,升级的话又要一个一个的改。
这时候我们就可以声明一个变量,然后其他地方直接使用就行了。
<?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:\maven\repository</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.jar\org\apache\maven\model\
文件夹,名为 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 可以帮助我们将构件部署到仓库中。
<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 是唯一标识,我们可以通过它来设置账号和密码。
然后我们可以执行如下命令发布
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 默认为主要的生命周期阶段绑定了很多插件目标,当调用生命周期阶段时,相应的插件就会被执行。
<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>
我们还可以通过命令行调用插件目标。
mvn [options] [goal<s>] [phase[s]]
因为有些插件目标不适合绑定到生命周期阶段执行,所以我们可以直接在命令行执行插件的目标。
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
都是一样的。
我们项目目录可能像下面这样。
|-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>
<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 pom.xml
中可以使用 ${}
来注入属性,它一共支持 6 类属性。
properties
中自定义的属性settings.xml
中的属性,如 ${settings.loaclRepository}
。${user.name}
${JAVA_HOME}
我们项目中开发环境和线上环境不同往往需要不同的配置。Maven 中的 Profile
就可以针对环境的不同使用不同的配置。
db.url=${db.url}db.password=${db.password}
<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 , 来帮助我们安装 node npm 或 yarn 来执行 npm script。
我们只需要在前端模块中添加这个插件
<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
插件可以帮助我们构建和发布 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 方法来构建。
FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
更多关于 spring boot docker 可以查看 Spring Boot Docker。