项目构建之Maven
Maven
Apache Maven 是Apache 旗下的开源项目,纯Java开发。
环境:安装Maven 需要 JDK(Maven 3.3 及以上版本需要 JDK1.7或更高版本)
目录结构
Maven 使用一个标准的目录结构:
{basedir}
├─src
│ ├─main
│ │ ├─java
│ │ └─resources
│ └─test
│ │ ├─java
│ │ └─resources
├─target
│ ├─classes
│ └─test-classes
└─pom.xml
说明:
目录 | 描述 |
---|---|
basedir | maven项目根目录 |
basedir/src/main/java | 项目的Java源码 |
basedir/src/main/resources | 项目的资源文件 |
basedir/src/test/java | 项目的测试类 |
basedir/src/test/resources | 项目测试用的资源 |
basedir/target | 打包输出目录 |
basedir/target/classes | 编译输出目录 |
basedir/pom.xml | pom文件 |
构建生命周期
Maven 构建生命周期定义了一个项目构建跟发布的过程。
一个典型的 Maven 构建(build)生命周期是由以下几个阶段的序列组成的:

阶段 | 描述 |
---|---|
验证 validate | 验证项目是否正确且所有必须信息是可用的 |
编译 compile | 源代码编译在此阶段完成 |
测试 test | 使用单元测试框架(如JUnit)运行测试 |
打包 package | 创建JAR/WAR包如在 pom.xml 中定义提及的包 |
检查 verify | 对集成测试的结果进行检查,以保证质量达标 |
安装 install | 安装打包的项目到本地仓库,以供其他项目使用 |
部署 deploy | 拷贝最终的工程包到远程仓库中,以共享给其他开发人员和工程 |
Maven 有以下三个标准的生命周期:
- **default(或 build)**:项目部署的处理
- clean:项目清理的处理
- site:项目站点文档创建的处理
重要概念:
当一个阶段通过 Maven 命令调用时,例如 mvn compile
,该阶段以及该阶段之前的所有阶段会被执行。
Build 生命周期
Build 生命周期是 Maven 的主要生命周期,被用于构建应用,包括23 个阶段。其中包含了 验证、编译、测试、打包、检查、安装、部署 在内的各个阶段。
生命周期阶段 | 描述 |
---|---|
validate(校验) | 校验项目是否正确并且所有必要的信息可以完成项目的构建过程。 |
initialize(初始化) | 初始化构建状态,比如设置属性值。 |
generate-sources(生成源代码) | 生成包含在编译阶段中的任何源代码。 |
process-sources(处理源代码) | 处理源代码,比如说,过滤任意值。 |
generate-resources(生成资源文件) | 生成将会包含在项目包中的资源文件。 |
process-resources (处理资源文件) | 复制和处理资源到目标目录,为打包阶段最好准备。 |
compile(编译) | 编译项目的源代码。 |
process-classes(处理类文件) | 处理编译生成的文件,比如说对Java class文件做字节码改善优化。 |
generate-test-sources(生成测试源代码) | 生成包含在编译阶段中的任何测试源代码。 |
process-test-sources(处理测试源代码) | 处理测试源代码,比如说,过滤任意值。 |
generate-test-resources(生成测试资源文件) | 为测试创建资源文件。 |
process-test-resources(处理测试资源文件) | 复制和处理测试资源到目标目录。 |
test-compile(编译测试源码) | 编译测试源代码到测试目标目录. |
process-test-classes(处理测试类文件) | 处理测试源码编译生成的文件。 |
test(测试) | 使用合适的单元测试框架运行测试(Juint是其中之一)。 |
prepare-package(准备打包) | 在实际打包之前,执行任何的必要的操作为打包做准备。 |
package(打包) | 将编译后的代码打包成可分发格式的文件,比如JAR、WAR或者EAR文件。 |
pre-integration-test(集成测试前) | 在执行集成测试前进行必要的动作。比如说,搭建需要的环境。 |
integration-test(集成测试) | 处理和部署项目到可以运行集成测试环境中。 |
post-integration-test(集成测试后) | 在执行集成测试完成后进行必要的动作。比如说,清理集成测试环境。 |
verify (验证) | 运行任意的检查来验证项目包有效且达到质量标准。 |
install(安装) | 安装项目包到本地仓库,这样项目包可以用作其他本地项目的依赖。 |
deploy(部署) | 将最终的项目包复制到远程仓库中与其他开发者和项目共享。 |
Clean 生命周期
当我们执行 mvn post-clean 命令时,Maven 调用 clean 生命周期,它包含以下阶段:
- pre-clean:执行一些需要在clean之前完成的工作
- clean:移除所有上一次构建生成的文件
- post-clean:执行一些需要在clean之后立刻完成的工作
mvn clean
中的 clean 就是上面的 clean,在一个生命周期中,运行某个阶段的时候,它之前的所有阶段都会被运行,也就是说,如果执行 mvn clean 将运行以下两个生命周期阶段:
pre-clean, clean
Site 生命周期
Maven Site 插件一般用来创建新的报告文档、部署站点等。
- pre-site:执行一些需要在生成站点文档之前完成的工作
- site:生成项目的站点文档
- post-site: 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
- site-deploy:将生成的站点文档部署到特定的服务器上
这里经常用到的是site阶段和site-deploy阶段,用以生成和发布Maven站点.
构建配置文件
配置文件有三类:
类型 | 说明 |
---|---|
全局 | 定义在 Maven 全局的设置 xml 文件中 (%M2_HOME%/conf/settings.xml) |
用户级 | 定义在Maven的设置xml文件中 (%USER_HOME%/.m2/settings.xml) |
项目级 | 定义在项目的POM文件pom.xml中 |
POM.xml
父POM
引入其他POM
依赖
依赖范围
在maven的pom.xml配置文件中,依赖有一个可选参数scope,它标识的是依赖的范围
scope的值有以下几个:
范围 | 说明 |
---|---|
provided | 在编译和测试时会import依赖,运行时不会 |
test | 只有在运行测试(例如junit)时才会import依赖 |
runtime | 在测试和运行时会import依赖,编译时不会 |
compile | (默认值)在编译、运行、测试阶段都需要依赖的jar包在classpath下 |
system | 依赖范围和provided一致,但是该依赖是存放于本地,需要由systemPath元素来显示指定依赖文件的路径 |
import |
编译依赖范围(compile)
该范围就是默认依赖范围,此依赖范围对于编译、测试、运行三种classpath都有效。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3</version>
<scope>compile</scope> <!--默认为该依赖范围,无须显示指定-->
</dependency>
测试依赖范围(test)
顾名思义就是针对于测试的,使用此依赖范围的依赖,只对测试classpath有效,在编译主代码和项目运行时,都将无法使用该依赖。最典型的例子就是 Junit,,构件在测试时才需要,所以它的依赖范围是测试,因此它的依赖范围需要显示指定为
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
运行时依赖范围(runtime)
使用该依赖范围的maven依赖,只对测试和运行的classpath有效,对编译的classpath无效,典型例子就是JDBC的驱动实现,项目主代码编译的时候只需要JDK提供的JDBC接口,只有在测试和运行的时候才需要实现上述接口的具体JDBC驱动。
已提供依赖范围(provided)
使用该依赖范围的maven依赖,只对编译和测试的classpath有效,对运行的classpath无效。典型的例子就是servlet-api, 编译和测试该项目的时候需要该依赖,但是在运行时,web容器已经提供的该依赖,所以运行时就不再需要此依赖,如果不显示指定该依赖范围,并且容器依赖的版本和maven依赖的版本不一致的话,可能会引起版本冲突,造成不良影响。
<dependency>
<groupId>javax-servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>4.0</version>
<scope>provided</scope>
</dependency>
系统依赖范围(system)
该依赖与classpath的关系与provided依赖范围完全一致,但是系统依赖范围必须通过配置systemPath元素来显示指定依赖文件的路径,此类依赖不是由maven仓库解析的,而且往往与本机系统绑定,可能造成构件的不可移植,因此谨慎使用,systemPath元素可以引用环境变量:
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stext</artifactId>
<version>3.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
注:${project.basedir} 代表根目录
如果依赖的第三方Jar包只有几个,那么选用这种方式是合适的。但如果数量很多,而又不想编写太多的 systemPath 便签来重复指定,可以选择在pom文件中指定一个本地位置的仓库,这样不需要额外写 scope 和 systemPath 便签就可以引入依赖了。
<repositories>
<repository>
<!-- DO NOT set id to "local" because it is reserved by Maven -->
<id>lib</id>
<url>file://${project.basedir}/lib</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.mylib</groupId>
<artifactId>bccs-api-lib</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
导入依赖范围(import)
该依赖范围不会对三种classpath产生影响,该依赖范围只能与dependencyManagement元素配合使用,其功能为将目标pom文件中dependencyManagement的配置导入合并到当前pom的dependencyManagement中。
<project>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
总结
编译 | 测试 | 运行 | |
---|---|---|---|
complie | ✓ | ✓ | ✓ |
test | ✓ | ||
runtime | ✓ | ✓ | |
provided | ✓ | ✓ | |
system | ✓ | ✓ |
Maven 仓库
Maven仓库存放了所有的jar 包,可以是本地仓库,也可以是远程仓库;其中远程仓库可以使用中央仓库,也可以自己搭建私服仓库。
- Maven 仓库有三种类型:
- 本地(local)
- 中央(central)
- 远程(remote)
中央仓库:https://mvnrepository.com
- 中央仓库由 Maven 社区管理。
- 不需要配置。
- 需要通过网络才能访问。
settings.xml 中配置仓库
pom.xml 中配置仓库
<repositories>
<repository>
<id>fb-nexus</id>
<name>fb-central</name>
<url>https://maven.aliyun.com/nexus/content/repositories/central/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
setting.xml配置
在这里只要配置登陆nexus的用户名密码,不然没有用户名和密码怎么能将jar包发送到私服呢。
<!--此处设置的用户名和密码都是nexus的登陆配置-->
<servers>
<server>
<id>releases</id> <!--对应pom.xml中id=releases的仓库-->
<username>xuxiaoxiao</username>
<password>xuxiaoxiao123</password>
</server>
</servers>
阿里仓库
Gradle 引用Maven仓库
build.gradle
allprojects {
repositories {
maven {
url 'https://maven.aliyun.com/repository/public/'
}
mavenLocal()
mavenCentral()
}
}
使用 gradle dependencies 安装依赖
注:Gradle 只能从Maven 仓库读取资源,但不能更新Maven资源。Gradle 有自己单独的存储路径。
Maven 插件
插件和生命周期阶段绑定
maven只是定义了生命周期中的阶段,而没有定义每个阶段中具体的实现,这些实现是由插件的目标来完成的。所以需要将阶段(phase)和插件目标(goal)进行绑定,来让插件目标帮助生命周期的阶段做具体的工作,生命周期中的每个阶段支持绑定多个插件的多个目标。
当我们将生命周期中的阶段和插件的目标进行绑定的时候,执行mvn 阶段就可以执行和这些阶段绑定的插件目标。
内置插件及绑定
maven 本身提供了一些内置的插件让我们不用做任何配置就可以实现一些基本的构建操作,比如运行mvn clean就可以帮我们清理代码,运行mvn install 就可以将构件安装到本地仓库,所以maven帮我们做了一些事情,默认将插件目标和对应的阶段进行绑定。
clean生命周期阶段与插件绑定关系:
生命周期阶段 | 插件:目标 |
---|---|
clean | maven-clean-plugin:clean |
compile | maven-compiler-plugin:compile |
test-compile | maven-compiler-plugin:testCompile |
test | maven-surefile-plugin:test |
package | maven-jar-plugin:jar |
install | maven-install-plugin:install |
deploy | maven-deploy-plugin:deploy |
Maven 实际上是一个依赖插件执行的框架,每个任务实际上是由插件完成。Maven 插件通常被用来:
- 创建 jar 文件
- 创建 war 文件
- 编译代码文件
- 代码单元测试
- 创建工程文档
安装
导入第三方Jar包到仓库中
mvn install:install-file -Dfile=D:\document\IdeaProjects\myTestDocument\jar\transaction-springcloud-4.2.0-SNAPSHOT.jar -DgroupId=com.codingapi -DartifactId=transaction-springcloud -Dversion=4.2.0-SNAPSHOT -Dpackaging=jar
部署
将本地Jar包上传到远程Maven仓库:
mvn deploy:deploy-file -DgroupId=com.beecode -DartifactId=amino.boot -Dversion=3.2.0 \
-Dpackaging=jar -Dfile=E:\temp\amino.boot-3.2.0.jar \
-Durl=http://maven.ifengban.com/repository/maven-all/ -DrepositoryId=fb-nexus
注意:
你要安装的jar和pom如果位于本地repository的目录下,这个命令就会出错。 解决方法:将要安装的jar和pom copy到其它目录再安装,只要不在本地仓库目录都应该可以。
Maven 命令
用法:mvn 选项 生命周期阶段 目标
mvn [options] [<goal(s)>] [<phase(s)>]
用户可以通过两种方式调用Maven插件的目标:
- 将插件目标与生命周期阶段
lifecycle phase
绑定,这样用户在命令行只是输入生命周期阶段而已,例如Maven默认将maven-compiler-plugin
的compile
目标与compile
生命周期阶段绑定,因此命令mvn compile
实际上是先定位到compile
这一生命周期阶段,然后再根据绑定关系调用maven-compiler-plugin
的compile
目标。 - 直接在命令行指定要执行的插件目标,例如
mvnarchetype:generate
就表示调用maven-archetype-plugin
的generate
目标,这种带冒号的调用方式与生命周期无关。
命令 | 描述 |
---|---|
mvn –v | 显示版本信息 |
mvn clean | 清理项目生产的临时文件,一般是模块下的target目录 |
mvn compile | 编译源代码,一般编译模块下的src/main/java目录 |
mvn package | 项目打包工具,会在模块下的target目录生成jar或war等文件 |
mvn test | 测试命令,或执行src/test/java/下junit的测试用例. |
mvn install | 将打包的jar/war文件复制到你的本地仓库中,供其他模块使用 |
mvn deploy | 将打包的文件发布到远程参考,提供其他人员进行下载依赖 |
mvn site | 生成项目相关信息的网站 |
mvn eclipse:eclipse | 将项目转化为Eclipse项目 |
mvn dependency:tree | 打印出项目的整个依赖树 |
mvn archetype:generate | 创建Maven的普通java项目 |
mvn tomcat:run | 在tomcat容器中运行web应用 |
mvn jetty:run | 调用 Jetty 插件的 Run 目标在 Jetty Servlet 容器中启动 web 应用 |
常用选项
-e 显示maven运行出错的信息
-X 显示maven允许的debug信息
-o 离线执行命令,即不去远程仓库更新包
-P 使用指定的Profile配置
-D 传入属性参数
编译源代码: mvn compile
编译测试代码:mvn test-compile
打包:mvn package
运行测试:mvn test
# 清除eclipse的一些系统设置
mvn eclipse:clean
生成idea项目:mvn idea:idea
生成eclipse项目:mvn eclipse:eclipse
清除eclipse的一些系统设置:mvn eclipse:clean
发布第三方Jar到本地库中:
mvn install:install-file -DgroupId=com -DartifactId=client -Dversion=0.1.0 -Dpackaging=jar -Dfile=d:\client-0.1.0.jar
部署:
mvn deploy:deploy-file -DgroupId=com -DartifactId=client -Dversion=0.1.0 -Dpackaging=jar -Dfile=d:\client-0.1.0.jar -DrepositoryId=maven-repository-inner -Durl=ftp://xxxxxxx/opt/maven/repository/
多环境指定 Profile 环境编译打包
通过mvn –P参数指定 profile,只对当前指定的生效。
高级进阶
多模块开发
mutiple-project
├── module-a
│ ├── pom.xml
│ └── src
├── module-b
│ ├── pom.xml
│ └── src
└── module-c
├── pom.xml
└── src
项目模板
Maven 使用 archetype(原型) 来创建自定义的项目结构,形成 Maven 项目模板。
在前面章节我们学到 Maven 使用下面的命令来快速创建 java 项目:
mvn archetype:generate
多 Profile
在 Maven 中,-P
是用于激活 Profile(配置文件)的命令行参数。它的作用是根据项目需求,动态启用或禁用预先定义在 pom.xml
或 settings.xml
中的 Profile,从而实现不同环境(如开发、测试、生产)的差异化构建。
mvn clean install -P dev # 激活 dev Profile
mvn package -P prod,security # 同时激活 prod 和 security Profile
<profiles>
<!-- 开发环境 Profile -->
<profile>
<id>dev</id>
<properties>
<environment>development</environment>
<app.port>8080</app.port>
</properties>
<!-- 默认激活(可选) -->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!-- 生产环境 Profile -->
<profile>
<id>prod</id>
<properties>
<environment>production</environment>
<app.port>80</app.port>
</properties>
</profile>
</profiles>
资源过滤
Maven项目资源过滤中的资源指的是位于src/main/resources文件中的相关配置文件,
资源拷贝
- 在打包测试或者部署时Maven会将位于resources目录中的配置文件都复制到目标文件target/classes目录下。
- 这个拷贝的工作是由maven resource plugin插件来完成的
- 默认情况下该插件只是将资源进行复制,不会对资源文件做任何操作
资源过滤
资源过滤就是在将src/main/resources文件复制到target/classes目录过程中,对拷贝的资源做一些修改。
在配置文件中设置变量,在项目打包测试或者运行时使用Maven动态的给变量赋值:
- 在application.properties配置文件中配置变量
- 在pom文件中使用profile配置变量不同的值
- 开启资源拷贝插件的过滤功能:拷贝资源时将pom中的变量设置到资源文件中。
在Spring Boot项目的bootstrap.yml
配置文件中,@artifactId@
是Maven资源过滤(Resource Filtering)的语法,用于在构建时将占位符替换为Maven项目的pom.xml
中定义的属性值。以下是详细解释和应用方法:
语法作用
@artifactId@
表示引用Maven项目的<artifactId>
值。- 当启用Maven资源过滤时,构建过程中会自动将
@变量名@
替换为pom.xml
中对应的属性值。
应用场景
- 动态配置:在配置文件中直接引用项目元数据(如应用名称、版本等),避免硬编码。
- 多环境适配:通过Maven属性动态生成不同环境的配置。
如何启用
需在pom.xml
中配置资源过滤和分隔符:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering> <!-- 启用过滤 -->
</resource>
</resources>
</build>
README
作者:银法王
修订:
2020-04-27 第一次