mvn package -Dmaven.test.skip=true
构建一个项目通常包含了依赖管理、测试、编译、打包、发布等流程,构建工具可以自动化进行这些操作,从而为我们减少这些繁琐的工作。
Java 主流构建工具
Ant 具有编译、测试和打包功能,其后出现的 Maven 在 Ant 的功能基础上又新增了依赖管理功能,而最新的 Gradle 又在 Maven 的功能基础上新增了对 Groovy 语言的支持。
Maven 主要用处一:相同的项目结构
使用Maven管理的Java 项目都有着相同的项目结构
- 有一个pom.xml 用于维护当前项目都用了哪些jar包
- 所有的java代码都放在 src/main/java 下面
- 所有的测试代码都放在src/test/java 下面
Maven 主要用处二:统一维护jar包
maven风格的项目,首先把所有的jar包都放在【仓库】里,然后哪个项目需要用到这个jar包,只需要给出jar包的名称和版本号就行了。 这样jar包就实现了共享
配置:https://how2j.cn/k/idea/idea-maven-config/1353.html
Maven 仓库能帮助我们管理构件(主要是JAR),它就是放置所有JAR文件(WAR,ZIP,POM等等)的地方。
Maven 仓库有三种类型:
-
本地(local)
运行 Maven 的时候,Maven 所需要的任何构件都是直接从本地仓库获取的。如果本地仓库没有,它会首先尝试从远程仓库下载构件至本地仓库,然后再使用本地仓库的构件。
-
中央(central)
由 Maven 社区提供的仓库,其中包含了大量常用的库。要浏览中央仓库的内容,maven 社区提供了一个 URL:https://search.maven.org/#browse
-
远程(remote)
如果 Maven 在中央仓库中也找不到依赖的文件,它会停止构建过程并输出错误信息到控制台。为避免这种情况,Maven 提供了远程仓库的概念,它是开发人员自己定制仓库,包含了所需要的代码库或者其他工程中用到的 jar 文件。
POM( Project Object Model,项目对象模型 ) 是 Maven 工程的基本工作单元,是一个XML文件,保存在项目根目录的 pom.xml 文件中,包含了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等。
执行任务或目标时,Maven 会在当前目录中查找 POM。它读取 POM,获取所需的配置信息,然后执行目标。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>[groupId, artifactId, version, packaging, classifier] 称为一个项目的坐标,其中 groupId、artifactId、version 必须定义,packaging 可选(默认为 Jar),classifier 不能直接定义的,需要结合插件使用。
| 节点 | 描述 |
|---|---|
| project | 工程的根标签 |
| modelVersion | 模型版本,需要设置为 4.0 |
| groupId | 工程组的标识。它在一个组织或者项目中通常是唯一的。例如,一个银行组织 com.companyname.project-group 拥有所有的和银行相关的项目 |
| artifactId | 工程的标识。它通常是工程的名称。例如,消费者银行。groupId 和 artifactId 一起定义了 artifact 在仓库中的位置 |
| version | 工程的版本号。在 artifact 的仓库中,它用来区分不同的版本。例如: com.company.bank:consumer-banking:1.0 com.company.bank:consumer-banking:1.1 |
| packaging | 项目打包方式。默认 jar |
依赖原则
-
依赖路径最短优先原则
A -> B -> C -> X(1.0) A -> D -> X(2.0)
由于 X(2.0) 路径最短,所以使用 X(2.0)。
-
声明顺序优先原则
A -> B -> X(1.0) A -> C -> X(2.0)
在 POM 中最先声明的优先,上面的两个依赖如果先声明 B,那么最后使用 X(1.0)。
-
覆写优先原则
子 POM 内声明的依赖优先于父 POM 中声明的依赖。
依赖关系
Maven定义了几种依赖关系,分别是compile、test、runtime和provided:
| scope | 说明 | 示例 |
|---|---|---|
| compile | 编译时需要用到该jar包(默认) | commons-logging |
| test | 编译Test时需要用到该jar包 | junit |
| runtime | 编译时不需要,但运行时需要用到 | mysql |
| provided | 编译时需要用到,但运行时由JDK或某个服务器提供 | servlet-api |
在<dependency>下的<scope>中体现
Maven不但有标准化的项目结构,而且还有一套标准化的构建流程,可以自动化实现编译,打包,发布,等等
Maven的**【生命周期(lifecycle)】由一系列【阶段(phase)】**构成,
内置的生命周期 【default】 包含以下 phase:
validate;initialize;generate-sources;process-sources;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;deploy
如果我们运行mvn package,Maven就会执行default生命周期,它会从开始一直运行到package这个phase为止
Maven另一个常用的生命周期是**【clean】**,它会执行3个phase:
pre-clean;clean;post-clean
更复杂的例子是指定多个phase,例如,运行mvn clean package,Maven先执行clean生命周期并运行到clean这个phase,然后执行default生命周期并运行到package这个phase
在实际开发过程中,经常使用的命令有:
mvn clean:清理所有生成的class和jar;
mvn clean compile:先清理,再执行到compile;
mvn clean test:先清理,再执行到test,因为执行test前必须执行compile,所以这里不必指定compile;
mvn clean package:先清理,再执行到package。
执行一个phase又会触发一个或多个**【goal】**:compile 对应的 goal —— compiler:compile
每个phase会执行自己默认的一个或多个goal,goal是最小任务单元
【插件(plugin)】
实际上,执行每个phase,都是通过某个 plugin 来执行的,Maven本身其实并不知道如何执行compile,它只是负责找到对应的compiler插件,然后执行默认的compiler:compile这个goal来完成编译。
Maven已经内置了一些常用的标准插件,如果标准插件无法满足需求,我们还可以使用自定义插件。使用自定义插件的时候,需要声明
在软件开发中,把一个大项目分拆为多个模块是降低软件复杂度的有效方法
对于Maven工程来说,原来是一个大项目:
single-project
├── pom.xml
└── src
现在可以分拆成3个模块:
mutiple-project
├── module-a
│ ├── pom.xml
│ └── src
├── module-b
│ ├── pom.xml
│ └── src
└── module-c
├── pom.xml
└── src
Maven可以有效地管理多个模块,我们只需要把每个模块当作一个独立的Maven项目,它们有各自独立的pom.xml
例如,模块A的pom.xml:
<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.itranswarp.learnjava</groupId>
<!-- diff -->
<artifactId>module-a</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<!-- diff -->
<name>module-a</name>
<properties>
...
</properties>
<dependencies>
...
</dependencies>
</project>模块B的pom.xml:
<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.itranswarp.learnjava</groupId>
<!-- diff -->
<artifactId>module-b</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<!-- diff -->
<name>module-b</name>
<properties>
...
</properties>
<dependencies>
...
</dependencies>
</project>可以看出来,模块A和模块B的pom.xml高度相似,因此,我们可以提取出共同部分作为parent:
<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.itranswarp.learnjava</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
<!-- attention -->
<packaging>pom</packaging>
<name>parent</name>
<properties>
...
</properties>
<dependencies>
...
</dependencies>
</project>注意到parent的<packaging>是pom而不是jar,因为parent本身不含任何Java代码。编写parent的pom.xml只是为了在各个模块中减少重复的配置。现在我们的整个工程结构如下:
multiple-project
├── pom.xml
├── parent
│ └── pom.xml
├── module-a
│ ├── pom.xml
│ └── src
├── module-b
│ ├── pom.xml
│ └── src
└── module-c
├── pom.xml
└── src
这样模块A就可以简化为:
<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>
<!-- attention -->
<parent>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
<artifactId>module-a</artifactId>
<packaging>jar</packaging>
<name>module-a</name>
</project>如果模块A依赖模块B,则模块A需要模块B的jar包才能正常编译,我们需要在模块A中引入模块B:
...
<dependencies>
<dependency>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>module-b</artifactId>
<version>1.0</version>
</dependency>
</dependencies>最后,在编译的时候,需要在根目录创建一个pom.xml统一编译:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>build</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>build</name>
<modules>
<module>parent</module>
<module>module-a</module>
<module>module-b</module>
<module>module-c</module>
</modules>
</project>这样,在根目录执行mvn clean package时,Maven根据根目录的pom.xml找到包括parent在内的共4个<module>,一次性全部编译。
