1788 字
9 分钟
Maven 以及 pom

Maven 是一个项目管理和构建的自动化工具,主要服务于 JAVA 项目。其特性在于

  • 标准化项目管理 (统一的项目目录结构)
  • 依赖管理 (自动管理第三方库及其版本、传递性依赖)
  • 构建生命周期 (标准的构建流程-编译、测试、打包、部署)
  • 插件机制 (通过插件扩展功能)

pom.xml 详解#

POM (Project Object Model) 是 Maven 的核心配置,定义了项目的基本信息、依赖、构建配置等。

基础结构#

<?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">
<!-- POM 版本,通常固定为 4.0.0 -->
<modelVersion>4.0.0</modelVersion>
<!-- 项目坐标:唯一标识一个项目 -->
<groupId>com.example</groupId> <!-- 组织/公司标识 -->
<artifactId>my-app</artifactId> <!-- 项目名称 -->
<version>1.0.0-SNAPSHOT</version> <!-- 项目版本 -->
<packaging>jar</packaging> <!-- 打包方式:jar/war/pom等 -->
</project>

坐标详解#

元素说明示例
groupId组织标识,通常为域名倒写com.example, cc.ieaf
artifactId项目唯一标识my-app, springsecurity-demo
version项目版本号1.0.0, 2.0.0-beta
packaging打包方式 (可选)jar, war, pom, maven-plugin
classifier分类器 (可选)sources, javadoc

版本号规范#

Maven 版本号规范允许使用数字、点、连字符、下划线、字母的组合。

<version>2.5.6.RELEASE</version> <!-- Spring 传统格式 -->
<version>2.5.6</version> <!-- 纯数字格式 -->
<version>2.5.6-Final</version> <!-- 带标识符 -->
<version>2.5.6.20201231.123456</version> <!-- 时间戳版本 -->

版本号排序规则(从小到大):

  1. 2.5.6-SNAPSHOT (开发版)
  2. 2.5.6-alpha (内测版)
  3. 2.5.6-beta (公测版)
  4. 2.5.6-milestone (里程碑版)
  5. 2.5.6.RELEASE2.5.6 (正式版)
  6. 2.5.6.RELEASE.1 (补丁版)

依赖管理#

<dependencies>
<!-- 一个依赖项 -->
<dependency>
<!-- 组织标识 -->
<groupId>org.springframework.boot</groupId>
<!-- 项目唯一标识 -->
<artifactId>spring-boot-starter-web</artifactId>
<!-- 项目版本号 -->
<version>2.7.0</version>
<!-- 依赖范围 -->
<scope>compile</scope>
<!-- 是否可选 -->
<optional>false</optional>
<!-- 排除传递性依赖 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

依赖范围 (scope)#

范围说明
compile默认值,所有阶段都有效
provided编译时有效,运行时由 JDK 或容器提供
runtime编译时不需要,运行时需要
test测试时需要
system类似 provided 需要显式指定路径
import仅用于 dependencyManagement, 导入依赖

是否可选 (optional)#

这个选项表示这个依赖是否应该被传递继承

  • optional=true: 表示当前项目需要这个依赖,但是不强制要求引用我这个项目的使用者也被传递需要这个依赖
  • optional=false: (默认值)表示当前项目需要这个依赖,同时引用我这个项目的使用者也会自动获得这个依赖 举个例子:我写一个项目的时候用到了 lombok, 但是它仅在我编译代码的时候需要,如果别人要使用我写的包不一定就需要它,这种时候就可以使用 optional=true

属性配置#

<properties>
<!-- 项目编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Java 版本 -->
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<!-- 自定义属性:统一版本管理 -->
<spring.version>5.3.20</spring.version>
<junit.version>5.8.2</junit.version>
</properties>
<!-- 使用属性 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>

配置属性中,一些是内置属性,直接配置使用。一些是自定义配置,需要自己在用到的地方通过 ${spring.version} 这样的形式进行引用。

常见的内置属性#

属性名说明示例值
project.build.sourceEncoding源码编码UTF-8
project.reporting.outputEncoding报告编码UTF-8
maven.compiler.sourceJava 源码版本1.8,11,17,21
maven.compiler.targetJava 目标版本1.8,11,17,21
maven.test.skip是否跳过测试true/false
maven.javadoc.skip是否跳过javadoctrue/false

构建配置#

<build>
<!-- 最终打包的文件名-->
<!-- 不写的话,默认就是这个命名 -->
<finalName>${project.artifactId}-${project.version}</finalName>
<!-- 资源配置文文件 -->
<!-- 不写的话,资源文件路径默认是下面这个 -->
<!-- filtering 默认是 false -->
<!-- 但是包含的文件默认是所有,下面限定了.yaml和.xml后缀的 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.yaml</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<!-- 插件配置 -->
<plugins>
<plugin>
<!-- 和上述依赖类似 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<!-- 插件的具体配置,要根据插件决定 -->
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 和依赖类似,可以排除传递包 -->
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

filtering=true 的作用: 允许在构建时替换资源文件中的占位符,例如

src/main/resources/application.yaml
app:
name: ${project.name}
version: ${project.version}
env: ${env}
url: ${db.url}
  • 用来替换这些占位符的可以来自 pom.xml:

    <properties>
    <!-- 可以是内置属性 -->
    <project.version>1.0.0</project.version>
    <!-- 也可以是自定义属性 -->
    <env>dev</env>
    </properties>
    <build>
    <ressources>
    <resource>
    <directory>src/main/resources</directory>
    <filtering>true</filtering>
    </resource>
    </ressources>
    </build>
  • 也可以用命令行,通过 -D参数 传递

    Terminal window
    mvn package -ddb.url=jdbc:mysql://localhost:3306/test
  • 还可以是来自系统变量 例如系统变量里有 $OPENAI_API=sk...,在资源文件里也能使用

    src/main/resources/application.yaml
    openai_api: ${OPENAI_API}

仓库配置#

<!-- 定义项目特有的仓库 -->
<repositories>
<repository>
<id>aliyun</id>
<name>阿里云仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
<!-- 允许使用正式发行版本 -->
<releases>
<enabled>true</enabled>
</releases>
<!-- 禁止使用开发快照版本 -->
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<!-- 插件仓库 -->
<pluginRepositories>
<pluginRepository>
<id>aliyun-plugin</id>
<url>https://maven.aliyun.com/repository/public</url>
</pluginRepository>
</pluginRepositories>

用于自定义项目的依赖仓库和插件仓库,一般不使用,通常是在 Maven 的 settings.xml 文件里配置。

多模块项目#

大多数时候一些大型项目会分成多个模块构建,减小项目的耦合性,增大开发效率和灵活性。其实就是把项目里的文件分散到几个独立的模块里去:

spring-boot-multi-module/
├── pom.xml (父)
├── common/
│ ├── pom.xml
│ └── src/main/java/com/example/common/...
├── service/
│ ├── pom.xml
│ └── src/main/java/com/example/service/...
└── web/
├── pom.xml
└── src/main/java/com/example/web/Application.java

父POM (聚合模块)#

<!-- 父模块定义 -->
<groupId>com.example</groupId>
<artifactId>parent-project</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<!-- 定义子模块 -->
<module>common</module>
<module>core</module>
<module>web</module>
<!-- 子模块公用配置 -->
<properties>
<java.version>11</java.version>
</properties>

子 POM#

<parent>
<groupId>com.example</groupId>
<artifactId>parent-project</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>common</artifactId>
<packaging>jar</packaging>
  • Maven 是基于坐标识别项目,所以显式地声明父模块的 groupId、artifactId、version 是必须的。
  • 子模块通过 <parent> 声明继承了父模块的 groupId、artifactId、version 等。
  • relativePath 只是一个辅助提示,如果没找到 Maven 会转而去依赖仓库中去寻找

多模块依赖管理#

<!-- 通常用在父POM中 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
</dependencies>
</dependencyManagement>

dependencyManagement 中的是声明的依赖版本,可以让子模块引用时无需声明版本,而在子模块里的 dependencies 一般则是直接引入的依赖。

常用插件#

插件用途常用命令
maven-compiler-plugin编译Java代码compile, testCompile
maven-surefire-plugin运行单元测试test
maven-jar-plugin打包成JARpackage
maven-war-plugin打包成WARpackage
maven-source-plugin生成源码包source
maven-javadoc-plugin生成JavaDocjavadoc
spring-boot-maven-pluginSpringBoot打包spring-boot

Maven 的生命周期#

Maven 有三个内置的生命周期:

  • default (核心生命周期)
validate -> compile -> test -> package -> verify -> install -> deploy
阶段说明
validate验证项目是否正确
compile编译源代码
test运行单元测试
package打包成 jar/war
verify检查包是否有效
install安装到本地仓库
deploy部署到远程仓库
  • clean (清理)
pre-clean -> clean -> post-clean
阶段说明
pre-clean执行清理前需要完成的工作(例如清除之前的日志)
clean删除上一次构建生成的所有文件(核心阶段,默认删除 target 目录)
post-clean执行清理之后的工作(例如清理完成后的通知)
  • site (生成站点)

    Ps. 现在前后端分离基本不会用这个吧

pre-site -> site -> post-site -> site-deploy

常用命令#

Terminal window
# 清理
mvn clean
# 编译
mvn compile
# 运行测试
mvn test
# 打包
mvn package
# 安装到本地仓库
mvn install
# 部署到远程仓库
mvn deploy
# 跳过测试
mvn package -DskipTests
# 指定settings文件
mvn clean install -s /path/to/settings.xml
# 查看依赖树
mvn dependency:tree
# 查看项目信息
mvn help:effective-pom