赞
踩
Apache开源项目,Java项目的构建和管理工具。跨平台的项目管理工具,帮助实现项目的构建、测试、打包和部署。
Maven 提供标准的软件生命周期模型和构建模型,通过配置就能对项目进行全面管理。跨平台保证在不同os使用相同命令来完成相应任务。Maven 将构建的过程抽象成一个个生命周期过程,在不同阶段使用不同已实现插件来完成相应工作,极大避免设计和脚本编码的重复,实现复用。
本文介绍基于 Apache Maven 3 的项目构建的基本概念和方法。Maven 是一套标准的项目构建和管理工具,使用统一规范的脚本进行项目构建,简单易用,摒弃 Ant 中繁琐的构建元素,并具有较高可重用性。读完本文,你将了解 Maven 的基本概念和使用它进行项目构建的基本方法。
Ant 也是 Apache 组织下的一个跨平台的项目构建工具,它是一个基于任务和依赖的构建系统,是过程式的。开发者需要显示的指定每一个任务,每个任务包含一组由 XML 编码的指令,必须在指令中明确告诉 Ant 源码在哪里,结果字节码存储在哪里,如何将这些字节码打包成 JAR 文件。Ant 没有生命周期,你必须定义任务和任务之间的依赖,还需要手工定义任务的执行序列和逻辑关系。这就无形中造成了大量的代码重复。
Maven 不仅是一个项目构建工具还是一个项目管理工具。它有约定的目录结构(表 1)和生命周期,项目构建的各阶段各任务都由插件实现,开发者只需遵照约定的目录结构创建项目,再配置文件中生命项目的基本元素,Maven 就会按照顺序完成整个构建过程。Maven 的这些特性在一定程度上大大减少了代码的重复。
可将项目本身编译并打包到本地仓库,其他项目引用本项目的jar包时不用去私服下载jar包,直接从本地就能拿到刚编译打包好的项目jar包,避免了每次都需要重新往私服发布jar包的痛苦。
修改服务端,如manage层、dao层的项目时,若IDEA没有自动编译,则调试时易出奇怪错误,明明代码已改好,但debug时还在报错,就是项目没有编译完成造成,看到的改好的代码没变成class文件,因此,服务端的文件改动后,若发现没效果,可能是没编译,这时就能使用maven install编译。
IDEA可很方便创建project、module,但修改各module的版本时,会遇到import报错,这就是maven仓库没有对应包,仍需使用到install,注意是lifecycle里的install:
就能将已有的module打包到maven仓库,再修改版本号,不会影响项目里其他module。
清理环境,清除target文件夹。
编译,将Java源文件编译成class文件。
执行test目录下的测试用例。
将Java工程打成jar包。
[INFO] Scanning for projects... [INFO] [INFO] ------------------< com.javaedge.bigdata:sparksql-train >------------------ [INFO] Building sparksql-train 1.0 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ sparksql-train --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 2 resources [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ sparksql-train --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to /Users/javaedge/Downloads/sparksql-train/target/classes [INFO] [INFO] --- scala-maven-plugin:3.1.3:compile (default) @ sparksql-train --- [WARNING] Expected all dependencies to require Scala version: 2.11.8 [WARNING] com.javaedge.bigdata:sparksql-train:1.0 requires scala version: 2.11.8 [WARNING] com.twitter:chill_2.11:0.9.3 requires scala version: 2.11.12 [WARNING] Multiple versions of scala libraries detected! [INFO] /Users/javaedge/Downloads/sparksql-train/src/main/scala:-1: info: compiling [INFO] Compiling 31 source files to /Users/javaedge/Downloads/sparksql-train/target/classes at 1679561149150 [WARNING] peopleRowRDD [WARNING] ^ [WARNING] warning: there was one deprecation warning; re-run with -deprecation for details [WARNING] two warnings found [INFO] prepare-compile in 0 s [INFO] compile in 20 s [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ sparksql-train --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /Users/javaedge/Downloads/sparksql-train/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ sparksql-train --- [INFO] No sources to compile [INFO] [INFO] --- scala-maven-plugin:3.1.3:testCompile (default) @ sparksql-train --- [WARNING] Expected all dependencies to require Scala version: 2.11.8 [WARNING] com.javaedge.bigdata:sparksql-train:1.0 requires scala version: 2.11.8 [WARNING] com.twitter:chill_2.11:0.9.3 requires scala version: 2.11.12 [WARNING] Multiple versions of scala libraries detected! [WARNING] No source files found. [INFO] [INFO] --- maven-surefire-plugin:2.13:test (default-test) @ sparksql-train --- [INFO] Tests are skipped. [INFO] [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ sparksql-train --- [INFO] Building jar: /Users/javaedge/Downloads/sparksql-train/target/sparksql-train-1.0.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 27.695 s [INFO] Finished at: 2023-03-23T16:46:10+08:00 [INFO] ------------------------------------------------------------------------ Process finished with exit code 0
跳过单元测试。
多模块的项目,注意父 pom 会设置 JDK 版本,注意对齐版本号:
Maven 本质是插件框架,核心并不执行任何具体的构建任务,仅定义抽象的生命周期,所有这些任务都交给插件。每个插件都能完成至少一个任务,每个任务即是一个功能,将这些功能应用在构建过程的不同生命周期,保证了:
将生命周期的阶段与插件目标绑定,特定阶段完成具体构建任务。如下代码在 validate 阶段执行 maven-antrun-plugin 的 run 目标,具体任务在 <target></target>
元素中定义。
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.6</version> <executions> <execution> <id>version</id> <phase>validate</phase> <configuration> <target> 具体任务 </target> </configuration> <goals> <goal> run </goal> </goals> </execution> </executions> </plugin> </plugins>
Maven 项目中的插件,依赖和项目构建的输出都可由 Maven 坐标唯一区分,基于这种机制,Maven 将所有项目的构建文件放置一个统一位置 - Maven 仓库。所有 Maven 项目可从同一 Maven 仓库中获取自己所需要的依赖 JAR,这节省了磁盘资源。实际 Maven 项目不需要存储依赖的文件,只需在 POM 文件生成依赖关系,在构建时 Maven 就会自动去仓库中下载。
在安装了 Maven 的机器上,会生成一个 ~\.m2\repository
目录,即本地仓库,当 Maven 查找需要的依赖时:
当个人所在的网络无法访问公共的 Maven 仓库时,可在 settings.xml 设置代理服务器。打开 ~\.m2\settings.xml
,若没有则复制 $Maven_HOME/conf/settings.xml
到此路径下,加入:
<proxies>
<proxy>
<active>true</active>
<protocol>http</protocol>
<host> 代理地址 </host>
<port>8080</port>
<username> 用户名 </username>
<password> 密码 </password>
</proxy>
</proxies>
项目中依赖的 Jar 包可通过依赖的方式引入,通过在 dependencies 元素下添加 dependency 子元素,可声明一或多个依赖。通过控制依赖的范围,可指定该依赖在什么阶段有效。
Maven 的几种依赖范围:
依赖具有传递性,如 Project A 依赖 Project B,B 依赖 C,则 B 对 C 的依赖关系也会传递给 A。
若我们的项目引用了一个jar包,而该jar包又引用了其他jar包。则默认情况下,项目编译时,Maven会把直接引用、间接引用的jar包都下载到本地( ~/.m2/repository )。
若不需要这种传递性依赖,可用 <optional>
去除这种依赖传递:
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<optional>true<optional>
</dependency>
假设第三方的 jar 包中没有使用 <optional>
去除某些依赖的传递性,则可在当前 POM 文件中使用 <exclusions>
元素声明排除依赖,exclusions 可以包含一个或者多个 exclusion 子元素,因此可以排除一个或者多个传递性依赖。如
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
若项目中多个jar同时引用相同的jar时,会产生依赖冲突,但Maven采用两种避免冲突的策略,因此在Maven中不存在依赖冲突:
本项目 =》 A.jar =》B.jar =》 X.jar
本项目=》 C.jar =》 Xjar
引用路径长度相同时,pom.xmI中谁先被声明,就使用谁。
现实中一个项目往往由多个 project 构成,构建时,当然不想针对多个 project 分别执行多次构建命令,易产生遗漏也大大降低效率。Maven 的聚合功能可通过一个父模块将所有要构建的模块整合。
packaging 类型须是pom:
<groupId>com.java.ad</ groupId>
<artifactId>java-ad</artifactId>
<packaging>pom</ packaging>
<version>1.O-SNAPSHOT</version>
使用modules标签:
<modules>
<module>ad-eureka</module>
<module>java-ad-service</module>
<module>ad-gateway</module>
</modules>
将父模块的打包类型声明为 POM,通过 <modules>
将各模块集中到父 POM。如下其中 <module></module>
中间的内容为子模块工程名的相对路径。
<modules>
<module>../com.javaedge.project1</module>
<module>../com.javaedge.project2</module>
</modules>
父类型的模块,不需要有源代码和资源文件,即没有 src/main/java 和 src/test/java 目录。Maven 会:
继承是可重用性即消除重复编码的行为。Maven 中继承的用意和OOP一致。与聚合的实现类似,通过构建父模块将子模块共用的依赖,插件等进行统一声明,在聚合和继承同时使用时,可用同一父模块来完成这两个功能。
如将 com.javaedge.parent 模块声明为 project1、project2 的父模块,那么我们在 project1、2 中用如下代码声明父子关系,如:
<parent>
<groupId>com.javaedge.mavenproject</groupId>
<artifactId>com.javaedge.parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../com.javaedge.parent/pom.xml</relativePath>
</parent>
父模块只是用来声明一些可共用的配置和插件信息,所以它也像聚合模块一样,只需包括一个 POM 文件,其它项目文件如 src/main/java 都不需要。
聚合和继承存在一些共性和潜在联系,实际应用中,经常将聚合模块的父模块和继承的父模块定义为同一个。并非所有 POM 元素都能被继承,如下是可继承的元素列表。
名称 | 描述 |
---|---|
groupId | 项目组 ID |
version | 项目版本 |
description | 描述信息 |
organization | 组织信息 |
inceptionYear | 创始年份 |
url | 项目的 url 地址 |
developers | 开发者 |
contributors | 贡献者信息 |
distributionManagerment | 部署信息 |
issueManagement | 缺陷跟踪系统 |
ciManagement | 持续继承信息 |
scm | 版本控制信息 |
mailingList | 邮件列表信息 |
properties | 自定义的属性 |
dependencies | 依赖配置 |
dependencyManagement | 依赖管理配置 |
repositories | 仓库配置 |
build | 源码目录,插件管理等配置 |
reporting | 报告配置 |
如只想下载直接引用的jar包,在 pom.xml 做如下配置(给出需要排除的坐标):
在 POM 文件中常需要引用已定义的属性以降低代码冗余,提高代码可重用性:
有些属性是用户自定义的,有些属性是可直接引用的已定义变量。
这种属性跟 Maven Project 自身有关,比如要引入当前 Project 的版本信 息,那么只需要在使用的位置引用 ${version} 就行。
上文中已经提到 Maven 自身有一个 settings.xml 配置文件,它里面含有包括仓库,代理服务器等一些配置信息,利用 ${settings.somename} 就可以得到文件里相应元素的值。
这种属性对应 POM 文件中对应元素的值,如
p
r
o
j
e
c
t
.
g
r
o
u
p
I
d
对应
‘
<
g
r
o
u
p
I
d
>
<
/
g
r
o
u
p
I
d
>
‘
中的值,
{project.groupId} 对应 `<groupId></groupId>` 中的值,
project.groupId对应‘<groupId></groupId>‘中的值,{project.artifactId} 对应了 <artifactId> </artifactId >
中的值。
可用 env.${name} 获得相应 name 对应的环境变量的值,如 ${env.JAVA_HOME} 得到的就是 JAVA_HOME 的环境变量值。
使用最频繁和广泛的变量,完全由用户定义。POM 文件加入 元素并将自定义属性作为其子元素。格式如:
<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>
<java.version>1.8</java.version>
<skipTests>true</skipTests>
<springfox-swagger.version>2.9.2</springfox-swagger.version>
</properties>
Maven 3 在性能和灵活性方面都比 Maven2 有很大提升,新特性总结:
兼容低版本 Maven,即向后兼容,因此用户可以将 Maven2 的项目移植到 Maven3
性能优化。CPU 利用率更高,内存消耗更小,经过优化的 Maven3 比 Maven2 构建速度快出 50% 以上,这对于构建大型项目的开发者来说无疑会节省大量时间
在早先的版本中,开发者必须在子模块中指定父版本,当进行代码的迁移或升级时,这会带来额外的维护工作,Maven3.1 将会消除在子模块上指定父版本需要
Maven3 改善了错误报告,它会在错误报告中提供指向 Maven Wiki 页面的链接,这样开发者可以方便的查看更全面的错误描述和可能的原因。
增加 Maven Shell,通常可在系统自带 console 里执行 Maven 命令,但通过自安装 Maven Shell 提高生成速度,它是一个是 Maven 的命令行接口工具,可以缓存解析过的 POM,避免了重复调用 Maven 的启动成本。Maven Shell 不属于 Maven 发行包的一部分,需要单独下载
IDEA 实现 Maven 和 IDEA 的集成,与一个使用更广泛的 IDE 进行集成从而为开发者带来的便利是不言而喻的
Maven 有着许多实用的特点,它使用了标准的目录结构和部署。这就使得开发人员能够适应不同的项目,并且不用学习任何结构方面新的东西,也不用掌握特殊的指令来构建结构。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。