内容简介:翻译:知识的积累、专家、内行。跨平台的项目管理工具。Apache 组织的开源项目。主要服务于基于 Java 平台的项目构建、依赖管理和项目信息管理。何为构建:编译、运行单元测试、生成文档、打包、部署的过程,这就是构建。
Maven 与构建
什么是 Maven
翻译:知识的积累、专家、内行。跨平台的项目管理工具。Apache 组织的开源项目。
主要服务于基于 Java 平台的项目构建、依赖管理和项目信息管理。
什么是构建
何为构建:编译、运行单元测试、生成文档、打包、部署的过程,这就是构建。
构建的步骤:
- 清理 clean:将以前编译得到的旧文件 class 字节码文件删除。
- 编译 compile:将 java 源程序编译成 class 字节码文件。
- 测试 test:自动测试,自动调用 junit 程序。
- 报告 report:测试程序执行的结果。
- 打包 package:动态 Web 工程打 War 包,java 工程打 jar 包。
- 安装 install:将打包得到的文件复制到 “仓库” 中的指定位置(Maven特定的概念)。
- 部署 deploy:将动态 Web 工程生成的 war 包复制到 Servlet 容器下,使其可以运行。
项目骨架
pom:Project Object Model
根目录:工程名 |---src:源码 |---|---main:主程序 |---|---|---java:主程序代码路径 |---|---|---resource:主程序配置文件路径 |---|---test:测试 |---|---|---java:测试代码路径 |---|---|---resource:测试配置文件路径 |---pom.xml:maven 配置文件 复制代码
简单演示
## 1. 使用 archetype 命令生成 maven 简单骨架 mvn archetype:generate -DarchetypeCatalog=internal ## 2. 编译当前生成的项目 mvn compile ## 3. 使用其他命令 mvn test-compile mvn package mvn clean mvn install mvn depoly 暂时不演示 复制代码
- 构建速度很慢: blog.csdn.net/codercaicai…
- mvn install 命令结合招行情况,进行演示。
坐标与依赖
什么是坐标
类比为数学中平面几何,坐标(x、y ),任何一个坐标都能唯一标识该平面中的一个点。该点对应到 maven 就是 .jar、.war 等文件的文件。Maven 使用 groupId、artifactId、version、packaging、classifier 等元素来组成自己的坐标,并定义一组这样的规则,只要能提供正确坐标元素 Maven 就能找到对应的构件。
坐标元素:
- groupId :定义当前 Maven 项目隶属的实际项目。
- artifactId :定义实际项目中的一个 Maven 项目(模块)。
- packaging :定义 Maven 项目当前所处的版本。
- version :定义 Maven 项目打包方式。jar、war、pom。默认为 jar,
- classifier :区分从同一 artifact 构建的具有不同内容的构件。
classifier 使用场景
- 区分基于不同 JDK 版本的包
<dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.2.2</version> <classifier>jdk13</classifier> <!--<classifier>jdk15</classifier>--> </dependency> 复制代码
- 区分项目的不同组成部分
<dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.2.2</version> <classifier>jdk15-javadoc</classifier> <!--<classifier>jdk15-sources</classifier> --> </dependency> 复制代码
构件名与坐标是对应的,一般规则是:artifactId-version[-classifier].packaging。
依赖声明
<dependencies> <dependency> <groupId></groupId> <artifactId></artifactId> <version></version> <type></type> <optional></optional> <exclusions> <exclusion> <artifactId></artifactId> <groupId></groupId> </exclusion> ... </exclusions> </dependency> ... </dependencies> 复制代码
- groupId、artifactId、version :依赖的基本坐标。
- type :依赖的类型,对应项目对应的 packaging,一般不必声明。
- scope :依赖的范围,后面详解。
- optional :标记依赖是否可选。
- exclusions :用来排除传递性依赖。
依赖范围
-
compile:编译依赖范围
如果没有指定, 默认 使用该依赖范围。对于编译、测试、运行三种 classpath 都有效。如:spring-core。
-
test:测试依赖范围
只对于测试 classpath 有效,只需要在编译测试及运行测试才需要,在打包的时候不会打进去。如:JUnit。
-
provided:已提供依赖范围
对于编译和测试 classpath 有效,但运行时无效。如:servlet-api 编译和测试项目的时候都需要,但在实际运行中,容器已经提供,不需要 maven 重复的引用。
-
runtime:运行时依赖范围
对于测试和运行的 classpath 有效,但在编译主代码时无效。如:JDBC 驱动的实现包。只有在执行测试或者运行项目时,才需要具体的 JDBC 驱动。
-
system:系统依赖范围
与 provided 依赖范围完全一致,但是使用该范围时必须通过 systemPath 元素显式地指定依赖文件的路径。由于此类依赖不是通过 maven 仓库解析的,而且往往与本机系统绑定,可能造成构建不可移植,因此应该谨慎使用。systemPath 元素可以引用环境变量,如:
<dependencies> <dependency> <groupId>javax.sql</groupId> <artifactId>jdbc-stdxt</artifactId> <version>2.0</version> <scope>system</scope> <systemPath>${java.home}/lib/rt.jar</systemPath> </dependency> </dependencies> 复制代码
-
import:导入依赖范围
只在 dependencyManagement 标签中生效,导入已经定义好的 pom 文件中 dependencyManagement 节点内容
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-framework-bom</artifactId> <version>4.3.16.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 复制代码
依赖机制与特性
依赖传递
A->B(compile):第一直接依赖
B->C(compile):第二直接依赖
A->C(compile):传递性依赖
当在A中配置
<dependency> <groupId>com.B</groupId> <artifactId>B</artifactId> <version>1.0</version> </dependency> 复制代码
则会自动导入 C 包。
传递性依赖的范围如下图所示:
依赖调解
当传递性依赖出现问题时,能够清楚地知道该传递性依赖是从哪条依赖路径中引入的。
- 路径最近者优先原则
- A->B->C->X(1.0)
- A->D->X(2.0)
由于只能导入一个版本的包,按照最短路径选择导入 X(2.0)
- 第一声明者优先原则
- A->B->Y(1.0)
- A->C->Y(2.0)
此时由于依赖路径长度一致,按照第一声明者优先原则。在路径长度一致的前提下,如果 B 依赖在 POM 文件中声明顺序在 C 依赖之前,那么 Y(1.0) 则会被引入。
可选依赖
A->B、B->X(可选)、B->Y(可选)。项目 A 依赖于项目 B,项目 B 依赖于项目 X 和 Y。理论上项目 A 中,会把 B、X、Y 项目都依赖进来。但是 X、Y 两个依赖对于 B 来讲可能是互斥的,如 B 是数据库隔离包,支持多种数据库 MySQL 、Oracle,在构建 B 项目时,需要这两种数据库的支持,但在使用这个 工具 包时,只会依赖一个数据库。此时就需要在 B 项目 pom 文件中将 X、Y 声明为可选依赖,如下:
<dependency> <groupId>com.X</groupId> <artifactId>X</artifactId> <version>1.0</version> <optionnal>true</optionnal> </dependency> <dependency> <groupId>com.Y</groupId> <artifactId>Y</artifactId> <version>1.0</version> <optionnal>true</optionnal> </dependency> 复制代码
使用 optionnal 元素标识以后,只会对当前项目 B 产生影响,当其他的项目依赖 B 项目时,这两个依赖都不会被传递。项目 A 依赖于项目 B,如果实际应用数据库是 X, 则在 A 的 pom 中就需要显式地声明 X 依赖。
仓库
仓库分类:包括本地仓库和远程仓库。其中远程仓库包括:私服和中央仓库。搜索构建的顺序:
- 本地仓库
- maven settings profile 中的 repository;
- pom.xml 中 profile 中定义的repository;
- pom.xml 中 repositorys (按定义顺序找);
- maven settings mirror;
- central 中央仓库;
演示:pom 中本地仓库
生命周期与插件
Maven 的生命周期是为了对所有构建过程进行的抽象和统一,其中包含项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有的构建步骤。
Maven 的生命周期是抽象的,本身是不做任何实际的工作。实际的任务都交给插件来完成。意味着 Maven 只在父类中定义了算法的整体结构,子类通过重写父类的方法,来控制实际行为(设计模式中的模板方法 Template Method)。伪代码如下:
public abstract class AbstractBuilder { public void build() { init(); compile(); test(); package(); integrationTest(); deploy(); } protected abstract void init(); protected abstract void compile(); protected abstract void test(); protected abstract void package(); protected abstract void integrationTest(); protected abstract void deploy(); } 复制代码
三套生命周期
Maven 的生命周期并不是一个整体,Maven 拥有 三套相互独立的生命周期 ,它们分别为 clean、default 和 site。
- clean 生命周期的目的是清理项目;
- default 生命周期的目的是构建项目;
- site 生命周期的目的是建立项目站点;
单个生命周期执行顺序
每个生命周期包含一些阶段(phase),这些阶段是有顺序的,并且 后面的阶段依赖于前面的阶段 。以 clean 生命周期为例,它包含的阶段有 pre-clean、clean和post-clean。当调用 pre-clean 时,只有 pre-clean 阶段得以执行;当调用 clean 的时候,pre-clean和clean阶段会得以顺序执行,以此类推。
各个生命周期之间的关系
三套生命周期本身是相互独立的,用户可以仅调用 clean 生命周期的某个阶段,或者仅仅调用 default 生命周期的某个阶段,而 不会对其他生命周期产生任何影响 。例如,当用户调用 clean 生命周期的 clean 阶段的时候,不会触发 default 生命周期的任何阶段,反之亦然。
生命周期各个阶段详解
clean
生命周期阶段 | 描述 |
---|---|
pre-clean | 执行一些清理前需要完成的工作。 |
clean | 清理上一次构建生成的文件。 |
post-clean | 执行一些清理后需要完成的工作。 |
default
包含 23 个阶段,此处只介绍重点步骤,如下表:
生命周期阶段 | 描述 |
---|---|
validate | 检查工程配置是否正确,完成构建过程的所有必要信息是否能够获取到。 |
initialize | 初始化构建状态,例如设置属性。 |
generate-sources | |
process-sources | 处理项目资源文件,处理项目主资源文件。一般来说,是对src/main/resources目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中。 |
generate-resources | |
process-resources | |
compile | 编译项目的主源码。一般来说,是编译src/main/java目录下的Java文件至项目输出的主classpath目录中。 |
process-classes | 处理编译生成的文件,例如 Java Class 字节码的加强和优化。 |
generate-test-sources | |
process-test-sources | 处理项目测试资源文件。一般来说,是对src/test/resources目录的内容进行变量替换等工作后,复制到项目输出的测试classpath目录中。 |
test-compile | 编译项目的测试代码。一般来说,是编译src/test/java目录下的Java文件至项目输出的测试classpath目录中。 |
process-test-classes | |
test | 使用适当的单元测试框架(例如JUnit)运行测试。 |
prepare-package | 在真正打包之前,为准备打包执行任何必要的操作。 |
package | 获取编译后的代码,并按照可发布的格式进行打包,例如 JAR、WAR 或者 EAR 文件。 |
pre-integration-test | 在集成测试执行之前,执行所需的操作。例如,设置所需的环境变量。 |
integration-test | 处理和部署必须的工程包到集成测试能够运行的环境中。 |
post-integration-test | 在集成测试被执行后执行必要的操作。例如,清理环境。 |
verify | 运行检查操作来验证工程包是有效的,并满足质量要求。 |
install | 安装工程包到本地仓库中,该仓库可以作为本地其他工程的依赖。 |
deploy | 拷贝最终的工程包到远程仓库中,以共享给其他开发人员和工程。 |
site
生命周期阶段 | 描述 |
---|---|
pre-site | 执行一些在生成项目站点之前需要完成的工作。 |
site | 生成项目站点文档。 |
post-site | 执行一些在生成项目站点之后需要完成的工作。 |
site-deploy | 将生成的项目站点发布到服务器上。 |
插件
Maven 三套生命周期定义各个阶段不做任何实际工作,实际工作都是由插件来完成的, 每个生命周期阶段都是由插件的目标来完成 。在 pom 文件中声明如下(打包源码文件插件):
<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-dependency-plugin 有十多个目标,每个目标对应了一个功能。插件的目标为 dependency:analyze、dependency:tree和dependency:list。通用写法:冒号前面是插件前缀,冒号后面是插件的目标。比如 compiler:compile。
插件绑定
内置绑定
为实现快速构建,Maven 有一套内置的插件绑定。三套生命周期的插件绑定具体如下(其实是各个生命周期阶段与插件的目标的绑定)。其中 default 生命周期的构建方式会其打包类型有关、打包类型在POM中 packaging 指定。一般有 jar、war 两种类型。下面是默认绑定插件与生命周期关系图:
自定义绑定
自定义绑定允许我们自己掌控插件目标与生命周期的结合.以生成项目主代码的源码 jar 为例。使用到的插件和它的目标为:maven-source-plugin:jar-no-fork。将其绑定到 default 生命周期阶段 verify 上(可以任意指定三套生命周期的任意阶段)。
<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 命令中加入 -D 参数,并伴随一个参数键=参数值的形式,来配置插件目标参数。如:maven-surefire-plugin 插件提供一个 maven.test.skip 参数,当值为 true 时会跳过执行测试:
-- 对比 mvn install mvn install –Dmaven.test.skip=true 复制代码
- 使用 pom 全局配置
在声明插件的时候,对插件进行一个全局配置,后面所有使用该插件的都要遵循这个配置。比如指定 maven-compile-plugin 编译 1.7 版本的源文件:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <fork>true</fork> <source>1.7</source> <target>1.7</target> </configuration> </plugin> 复制代码
- 获取插件信息
官方网站
命令行获取
- 使用 maven-help-plugin 插件
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-compiler-plugin mvn help:describe -Dplugin=compiler mvn help:describe -Dplugin=compiler -Dgoal=compile mvn help:describe -Dplugin=compiler -Ddetail 复制代码
- 使用插件内置 help 目标
mvn compiler:help -Ddetail=true -Dgoal=compile 复制代码
- 插件前缀
如果插件的名称命名为 {prefix}-plugin 这样的格式的话,可以直接用 mvn prefix:goal 调用。
如何查看插件的前缀:${MAVEN_HOME}\org\apache\maven\plugins\maven-metadata-central.xml
聚合与继承
聚合: 为了一次构建多个项目模块 ,就需要对多个项目模块进行聚合
<modules> <module>模块一</module> <module>模块二</module> <module>模块三</module> </modules> 复制代码
继承: 为了消除重复 ,把很多相同的配置提取出来,例如:dependency、grouptId,version 等
<parent> <groupId>com.xxxx.maven</groupId> <artifactId>parent-project</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath>../ParentProject/pom.xml</relativePath> </parent> 复制代码
以下的元素是可以被继承的:
groupId,项目组ID;
version,项目版本;
description,项目描述信息;
organazation,项目的组织信息;
inceptionYear,项目的创始年份;
developers,项目开发者信息;
contributors,项目的贡献者信息;
distributionManagement,项目的部署信息;
issueManagement,项目的缺陷跟踪系统信息;
ciManagement,项目的持续集成系统信息;
scm,项目的版本控制系统信息;
mailingLists,项目的邮件列表信息;
properties,自定义的Maven属性;
dependencies,项目的依赖配置;
dependencyManagement,项目的依赖管理配置;
repositories,项目的仓库配置;
build,包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等;
reporting,包括项目的报告输出目录配置、报告插件配置。
注意下面的元素,这些都是不能被继承的。
- artifactId
- name
- prerequisites
聚合与继承之间的关系:
- 两者共同点为,打方式必须都是 pom
- 在实际的项目中,一个 pom 既是聚合 pom 又是父 pom
灵活构建
使用 resources 插件资源过滤功能(filter)和 Maven 的 profile 功能,实现环境的灵活切换
fileter
开启资源过滤
<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build> 复制代码
属性
1. 内置属性
{version} 等同于 {pom.version} 表示项目版本
2. POM 属性
所有 pom 中的元素都可以用 project.
例如 ${project.artifactId}
对应了 < project><artifactId>
元素的值。常用的 POM 属性包括:
{project.build.testSourceDirectory}:项目的测试源码目录,默认为/src/test/java/.
{project.build.outputDirectory}:项目主代码编译输出目录,默认为target/classes/.
{project.groupId}:项目的groupId.
{project.version}:项目的version,等同于 {project.build.finalName}:项目打包输出文件的名称,默认为{project.version}.
3. 自定义属性
在 pom 中 <properties>
元素下自定义的 Maven 属性
<properties> <swagger.version>2.2.2</swagger.version> </properties> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>${swagger.version}</version> </dependency> 复制代码
4. Settings 属性
所有用的的 settings.xml 中的设定都可以通过 settings.
前缀进行引用与 POM 属性同理。如 ${settings.localRepository}
指向用户本地仓库的地址
5. Java 系统属性
所有 Java 系统属性都可以使用 Maven 属性引用,例如 ${user.home}
指向了用户目录。 可以通过命令行 mvn help:system
查看所有的 Java 系统属性
6. 环境变量属性
所有环境变量都可以使用以 env.
开头的 Maven 属性引用。例如 ${env.JAVA_HOME}
指代了 JAVA_HOME 环境变量的值。也可以通过命令行 mvn help:system
查看所有环境变量。
7. 父级工程属性
上级工程的 pom 中的变量用前缀 ${project.parent }
引用. 上级工程的版本也可以这样引用: ${parent.version }
Profile
profile 特性可以让我们定义一系列的配置信息,然后指定其激活条件。可以定义多个 profile,然后每个 profile 对应不同的激活条件和配置信息,从而达到不同环境使用不同配置信息的效果。profile 定义的位置:
- 针对于特定项目的 profile 配置,定义在该项目的 pom.xml 中。(下面举例是这种方式)
- 针对于特定用户的 profile 配置,定义在用户的 settings.xml 文件中定义profile。
- 全局的 profile 配置。全局的 profile 是定义在Maven安装目录下的 "conf/settings.xml" 文件中的。
示例:
<project> ... <profiles> <profile> <id>dev</id> <properties> <active.profile>dev</active.profile> <key1>value1</key1> <key2>value2</key2> </properties> <!-- 默认激活配置 --> <activation> <activeByDefault>true</activeByDefault> </activation> <!-- 在该 profile 下才会引入的依赖 --> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.2.4.RELEASE</version> </dependency> <dependencies> <!-- 在该 profile 下才会加载的变量文件 --> <build> <filters> <filter>../profile/test-pre.properties</filter> </filters> </build> </profile> </profiles> ... </project> 复制代码
高级特性
-
使用 SNAPSHOT 来管理未正式发布的构件
在 version 后面加上 "-SNAPSHOT"
实用插件
-
使用 shade 插件做配置文件合并等工作
-
使用 versions 插件批量修改父 pom 版本号
-- https://blog.csdn.net/GGBomb2/article/details/78316068 -- 在顶级 pom 目录中设置版本号 mvn versions:set -DnewVersion='1.1.3-RELEASE' -- 设置不正确时可以撤销新版本号的设置 mvn versions:revert -- 确认新版本号无误后提交新版本号的设置 mvn versions:commit 复制代码
-
如何分析出项目中未被使用的依赖(没有用到的类)
-
多模块打包问题(只打 api 不打 provider)
项目启发
- 从 Maven 插件的设计上,体现了模板的方法的设计思想,是不是可以用在我们的项目上?
备忘
- 打包语句
mvn clean install -Ptest -Dmaven.test.skip=true -X -DskipTests package 复制代码
- setting 文件
<?xml version="1.0" encoding="utf-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <!-- localRepository Default: ~/.m2/repository --> <localRepository>本地 maven 仓库地址</localRepository> <pluginGroups> <pluginGroup>org.codehaus.cargo</pluginGroup> <pluginGroup>org.apache.maven.plugins</pluginGroup> <pluginGroup>org.codehaus.mojo</pluginGroup> </pluginGroups> <proxies> </proxies> <!-- 当需要上传构建才需要进行身份认证 --> <!--<servers> <server> <id>xxx_release</id> <username></username> <password></password> </server> <server> <id>xxx_snapshots</id> <username></username> <password></password> </server> </servers>--> <!-- 设置私服才需要放开这段配置 --> <!-- <profiles> <profile> <id>nexus</id> <repositories> <repository> <id>仓库id</id> <url>私服地址</url> <releases> <enabled>true</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>warn</checksumPolicy> </releases> <snapshots> <enabled>true</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>warn</checksumPolicy> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>仓库id</id> <url>私服地址</url> <releases> <enabled>true</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>warn</checksumPolicy> </releases> <snapshots> <enabled>false</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>warn</checksumPolicy> </snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> <activeProfiles> <activeProfile>nexus</activeProfile> </activeProfiles> --> <!-- 使用阿里云 maven 镜像仓库 --> <mirrors> <mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors> </settings> 复制代码
参考资料:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Flutter 完整开发实战详解(十六、详解自定义布局实战)
- 数据结构 1 线性表详解 链表、 栈 、 队列 结合JAVA 详解
- 详解Openstack环境准备
- Java泛型详解
- iOS RunLoop 详解
- Raft协议详解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。