maven中的pom详述
什么是POM?
POM(Project Object Model)是maven的基本工作单元。它是一个 XML 文件,其中包含有关项目的信息以及 Maven 用于构建项目的配置详细信息。简单来说:POM(pom.xml)就是整个工程的项目规划书 ,它定义了项目的所有细节:需要什么材料(依赖)、由谁建造(开发者信息)、如何建造(构建配置)、以及项目的版本等。
超级POM
超级 POM 是 Maven 的默认 POM。除非显式设置,否则所有 POM 都会扩展超级 POM,这意味着超级 POM 中指定的配置将为项目创建的 POM 继承。超级POM 可以理解为MAVEN世界的宪法,所有maven项目都必须遵守。超级POM中定义了一些默认配置,下面列举几个:
默认目录结构
xml
<!-- 源代码目录 -->
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<!-- 测试代码目录 -->
<testSourceDirectory>src/test/java</testSourceDirectory>
<testResources>
<testResource>
<directory>src/test/resources</directory>
</testResource>
</testResources>
<!-- 输出目录 -->
<outputDirectory>target/classes</outputDirectory>
<testOutputDirectory>target/test-classes</testOutputDirectory>
这就是为什么所有 Maven 项目都长得一样的原因。
默认仓库配置
xml
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories>
所有依赖默认都从 Maven 中央仓库下载。
默认插件配置及默认插件版本管理
| 构建阶段 | 默认插件 | 作用 |
|---|---|---|
| compile | maven-compiler-plugin | 编译源代码 |
| test | maven-surefire-plugin | 运行单元测试 |
| package | maven-jar-plugin | 打包成 JAR 文件 |
| install | maven-install-plugin | 安装到本地仓库 |
对不同构建阶段绑定了不同的插件。
默认打包配置
xml
<!-- 默认打包方式就是 JAR -->
<packaging>jar</packaging>
最小的 POM
POM 用三坐标(groupId、artifactId、version) 的方式唯一标识 一个项目。可以理解成项目的身份证。如:
xml
<project> <!-- 说:这是个项目 -->
<modelVersion>4.0.0</modelVersion> <!-- 说:用第4版规则 -->
<groupId>com.mycompany.app</groupId> <!-- 姓:公司/组织名 -->
<artifactId>my-app</artifactId> <!-- 名:项目具体叫啥 -->
<version>1</version> <!-- 排行:这是第几个版本 -->
</project>
POM核心元素
一个标准的 POM 文件包含以下核心元素:
坐标(Coordinates) - 项目的唯一标识
- groupId: 定义项目所属的实际组织或公司,通常使用反向域名。例如:com.google.guava。
- artifactId: 定义实际项目(模块)的名称。例如:guava。
- version: 定义项目的当前版本。例如:31.1-jre。SNAPSHOT:表示不稳定、尚在开发中的版本。RELEASE:表示稳定的发布版本。
- packaging: 定义项目的打包方式。默认为 jar。其他常见值:war, ear, pom(用于聚合或父POM)。
这三个元素(groupId, artifactId, version)合称为GAV,是 Maven 世界的唯一身份证。
依赖管理(Dependencies)
xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
<optional>false</optional>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
- scope : 依赖范围 : compile : 默认值。对编译、测试、运行都有效。provided : 表示 JDK 或容器在运行时已提供(如 servlet-api)。只在编译和测试时使用。runtime : 编译时不需要,但测试和运行时需要(如 JDBC 驱动)。test : 只在测试时使用(如 JUnit)。system : 与 provided 类似,但必须通过 systemPath 显式指定路径(不推荐)。import: 仅用于 dependencyManagement 部分,用于从其他 POM 导入依赖管理。
- optional: 标记依赖是否为可选。如果为 true,其他项目依赖本项目时,该依赖不会被传递。【设你开发了一个核心软件,这个软件支持多种数据库(MySQL、PostgreSQL等),但是你不希望强制使用你软件的人必须包含所有数据库驱动。你可以把数据库驱动设置为可选。这样,当别人在他的项目中引入你的软件时,他不会自动获得所有这些数据库驱动,他需要明确指定他需要哪个驱动(在自己的pom里面单独引入)】,具体而言:
xml
<project>
<groupId>com.example</groupId>
<artifactId>project-A</artifactId>
<version>1.0.0</version>
...
<dependencies>
<!-- 可选依赖:MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
<optional>true</optional>
</dependency>
<!-- 可选依赖:PostgreSQL驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.6</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>
现在,有另一个项目project-B依赖了project-A:
xml
<project>
<groupId>com.example</groupId>
<artifactId>project-B</artifactId>
<version>1.0.0</version>
...
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>project-A</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
那么,project-B不会自动传递依赖MySQL和PostgreSQL驱动。如果project-B需要用到MySQL,那么它必须在自己的POM中显式声明MySQL驱动。
- exclusions: 排除传递性依赖,用于解决依赖冲突。
父 POM 与继承(Parent)
用于从父 POM 继承配置,实现统一管理。类似于JAVA中的继承。
注意点:
- 父POM的packaging必须是pom。
- 子POM通过元素指定父POM,其中groupId、artifactId、version必须与父POM的坐标一致。
- relativePath:指定查找父POM的相对路径。默认值是../pom.xml,即先从本地文件系统查找,如果没找到,再从仓库查找。
- 如果设置为空,则表示不从相对路径查找,直接从仓库查找。
- 继承的内容:父POM中定义的依赖、插件、属性、依赖管理、插件管理等都可以被继承。
总的来说:
- 父POM:统一配置、依赖版本、默认设置。
- 子POM:继承配置、使用依赖、覆盖配置【可以有个性】。
relativePath规则:
| 设置方式 | 策略 | 适用场景 |
|---|---|---|
| 空标签 | 1. 去本地仓库找 2. 去远程仓库找 | 父POM是知名框架(Spring Boot) |
| ../pom.xml | 1. 去../pom.xml找 2. 找不到再去仓库 | 父POM在本地项目里 |
示例1:
子pom文件:
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<artifactId>my-app</artifactId>
<!-- 只写了自己的名字,其他都没写 -->
实际生效的配置:
xml
<!-- 自动获得老爸的配置 -->
<properties>
<java.version>11</java.version> <!-- Java 11 -->
<maven.compiler.source>11</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 自动获得依赖版本管理 -->
<dependencyManagement>
<!-- Spring Boot 2.7.0 兼容的所有版本 -->
<spring.version>5.3.20</spring.version>
<jackson.version>2.13.3</jackson.version>
<!-- ... -->
</dependencyManagement>
<!-- 自动获得插件配置 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
项目聚合(Modules)
目的: 用于将多个模块/子项目聚合在一起,以便一次性构建整个项目。
xml
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>my-app-aggregator</artifactId>
<version>1.0</version>
<packaging>pom</packaging> <!-- 必须为pom -->
<modules>
<module>core-module</module> <!-- 相对路径,表示聚合项目目录下的core-module目录 -->
<module>service-module</module>
<module>web-module</module>
</modules>
</project>
这通常用在父 POM(其 packaging 为 pom)中。聚合项目本身可以没有源码,它只是作为一个构建的入口。在聚合项目目录下执行mvn命令,Maven会根据模块间的依赖关系自动确定构建顺序,依次构建每个模块。
聚合和继承经常结合使用:聚合项目同时作为父项目,提供统一的配置管理。这种情况下,聚合项目的pom.xml中既有也有等配置。
示例:
- 父pom
xml
<project>
<modelVersion>4.0.0</modelVersion>
<!-- 聚合项目的身份证 -->
<groupId>com.mycompany</groupId>
<artifactId>ecommerce-platform</artifactId>
<version>1.0.0</version>
<!-- 关键:打包方式必须是pom -->
<packaging>pom</packaging>
<!-- 聚合的核心:列出所有小弟 -->
<modules>
<module>user-service</module> <!-- 用户服务模块 -->
<module>product-service</module> <!-- 商品服务模块 -->
<module>order-service</module> <!-- 订单服务模块 -->
<module>common</module> <!-- 公共模块 -->
</modules>
<!-- 注意:聚合项目可以同时是父项目 -->
<properties>
<java.version>11</java.version>
<spring-boot.version>2.7.0</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 统一的依赖版本管理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
- 子模块的配置(以user-service为例)
xml
<!-- user-service/pom.xml -->
<project>
<modelVersion>4.0.0</modelVersion>
<!-- 既可以认父(继承),又可以被聚合 -->
<parent>
<groupId>com.mycompany</groupId>
<artifactId>ecommerce-platform</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<!-- 自己的身份证 -->
<artifactId>user-service</artifactId>
<packaging>jar</packaging> <!-- 子模块通常是jar或war -->
<dependencies>
<!-- 依赖common模块 -->
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
<!-- 其他业务依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
- 对比:
不使用聚合:
bash
# 要跑4次命令,还要注意构建顺序
cd common && mvn clean install
cd ../user-service && mvn clean install
cd ../product-service && mvn clean install
cd ../order-service && mvn clean install
使用后:
bash
# 在聚合项目根目录,一次搞定
cd ecommerce-platform
mvn clean install
# Maven自动处理:
# 1. 分析模块依赖关系(common → user-service → ...)
# 2. 按正确顺序构建
# 3. 一次性输出所有结果
- 一般微服务项目结构示例:
sql
platform/
├── pom.xml(聚合所有微服务)
├── gateway/(网关服务)
├── user-service/(用户服务)
├── order-service/(订单服务)
└── product-service/(商品服务)
属性(Properties)
用于定义变量,方便统一管理和复用,常见在父POM中定义公共属性。
xml
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<spring.version>5.3.20</spring.version>
</properties>
<!-- 在依赖中使用属性 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
属性的常见用法:
- 定义公共属性,如上。
- 多环境配置【开发、测试、生产环境用不同配置】
xml
<properties>
<!-- 默认开发环境配置 -->
<env>dev</env>
<server.port>8080</server.port>
<database.url>jdbc:mysql://localhost:3306/dev</database.url>
</properties>
<profiles>
<profile>
<id>prod</id>
<properties>
<!-- 生产环境覆盖默认值 -->
<env>prod</env>
<server.port>80</server.port>
<database.url>jdbc:mysql://prod-server:3306/prod</database.url>
</properties>
</profile>
</profiles>
构建配置(Build)
用于配置构建过程中的插件和行为。
构建配置可以包含两个主要部分:
- :用于管理插件的版本和配置,类似于依赖管理,它本身不会实际引入插件,只是提供一种统一管理的方式。子项目可以继承并引用这些插件,而无需指定版本。
- :实际使用的插件列表,在这里配置的插件会在构建过程中执行。
此外,构建配置还包括:
- : 定义资源文件(非代码文件)如何处理,例如配置文件、图片等。
- :定义测试资源文件如何处理。
- : 构建输出目录,默认为target。
- : 编译后的class文件输出目录,默认为target/classes。
- : 测试类编译输出目录,默认为target/test-classes。
- : 源代码目录,默认为src/main/java。
- : 测试代码目录,默认为src/test/java。
一般为:
xml
<build>
<!-- 1. 源代码目录(可以改默认位置) -->
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<!-- 2. 资源文件处理 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering> <!-- 是否替换占位符 -->
</resource>
</resources>
<!-- 3. 插件管理(父POM中用) -->
<pluginManagement>
<plugins>
<!-- 定义插件版本和基础配置 -->
</plugins>
</pluginManagement>
<!-- 4. 实际使用的插件 -->
<plugins>
<!-- 具体配置每个插件 -->
</plugins>
<!-- 5. 扩展(自定义组件) -->
<extensions>
<!-- 扩展Maven核心功能 -->
</extensions>
</build>
- 示例1:
xml
<build>
<plugins>
<!-- Spring Boot打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.0</version>
<configuration>
<mainClass>com.mycompany.app.Application</mainClass>
<!-- lombok只在编译时起作用,已经变成class了,不需要再打jar包依赖了 -->
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 生成可执行JAR -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
- 示例2:
xml
<!-- 在父POM中统一管理插件版本 -->
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<!-- 在子POM中引用,不需要写版本 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- 版本从父POM继承 -->
</plugin>
</plugins>
</build>
- 示例3:
xml
<profiles>
<profile>
<id>production</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests> <!-- 生产环境跳过测试 -->
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources-prod</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</profile>
</profiles>
依赖管理(DependencyManagement)
: 只是一个声明,它并不实际引入依赖。它主要用于统一管理子模块或项目的依赖版本。子模块需要显式声明依赖,但可以省略 version。如:
示例1:
- 父pom
xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
</dependencies>
</dependencyManagement>
- 子模块 POM:
xml
<dependencies>
<!-- 不需要写版本,版本由父POM统一管理 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
示例2:
xml
<!-- 直接使用Spring Boot定义的所有版本 -->
<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>
</dependencies>
</dependencyManagement>
<!-- 现在所有Spring Boot相关的依赖都不用写版本了 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 自动使用2.7.0对应的版本 -->
</dependency>
</dependencies>
关键标签:合并另一个POM的依赖管理。类似于继承别人的dependencyManagement一样。
当父POM中配置了,子模块继承的是依赖的管理规则(主要是版本信息),而不是依赖本身。这带来了几个显著优势:
- 统一版本管理: 所有子模块在引用父POM中声明过的依赖时,只需指定groupId和artifactId,版本号会统一使用父POM中的定义。这能有效避免多模块间的依赖版本冲突。
- 版本灵活性与覆盖: 如果某个子模块需要特殊版本,它可以在自己的中明确声明版本号,此时子模块的版本号具有优先级,会覆盖父POM中的定义。这为特殊需求的模块提供了灵活性。
- 按需引入依赖: 子模块只声明自己真正需要的依赖,避免了父POM中所有依赖被自动继承可能导致的冗余和潜在冲突。这让每个模块的依赖更清晰、更精简。
Profile
Profile 允许为不同的环境(如开发、测试、生产)定义不同的配置。它能够覆盖 POM 中的默认配置。
xml
<profiles>
<profile>
<id>dev</id>
<properties>
<db.url>jdbc:mysql://localhost:3306/dev</db.url>
</properties>
<activation>
<activeByDefault>true</activeByDefault> <!-- 默认激活 -->
</activation>
</profile>
<profile>
<id>prod</id>
<properties>
<db.url>jdbc:mysql://prod-server:3306/prod</db.url>
</properties>
</profile>
</profiles>
# 用命令激活
mvn clean install -P prod
一个相对完整的POM
xml
<?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">
<modelVersion>4.0.0</modelVersion>
<!-- 坐标 -->
<groupId>com.example</groupId>
<artifactId>my-webapp</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>My Web Application</name>
<!-- 父POM -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<!-- 属性 -->
<properties>
<java.version>11</java.version>
<junit.version>5.8.2</junit.version>
</properties>
<!-- 依赖管理 -->
<dependencyManagement>
<dependencies>
<!-- 可以在这里管理非Spring Boot管理的依赖 -->
</dependencies>
</dependencyManagement>
<!-- 依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- 构建配置 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- 多环境配置 -->
<profiles>
<profile>
<id>dev</id>
<properties>
<activatedProperties>dev</activatedProperties>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>prod</id>
<properties>
<activatedProperties>prod</activatedProperties>
</properties>
</profile>
</profiles>
</project>
比如:
sip-boot依赖了sip-application
sip-application依赖了sip-orm,sip-sieengine
则sip-boot中会引入sip-orm,sip-sieengine


