二、Maven 进阶


文档摘要

二、Maven 进阶 二、Maven 进阶 2.1 Maven Profile:环境与构建的灵活切换 在实际项目开发中,我们通常需要面对不同的构建环境,例如开发环境、测试环境、生产环境等。这些环境的配置可能存在差异,例如数据库连接、资源文件路径、插件配置等等。如果每次构建都需要手动修改 文件,这将变得非常繁琐且容易出错。Maven Profile 的出现正是为了解决这个问题。 2.1.1 Profile 概述 Maven Profile 允许我们在 文件中定义多个构建配置方案(Profile),每个 Profile 可以包含不同的配置信息,例如: 属性(Properties): 定义不同环境下的属性值,例如数据库连接字符串、版本号等。

二、Maven 进阶

二、Maven 进阶

2.1 Maven Profile:环境与构建的灵活切换

在实际项目开发中,我们通常需要面对不同的构建环境,例如开发环境、测试环境、生产环境等。这些环境的配置可能存在差异,例如数据库连接、资源文件路径、插件配置等等。如果每次构建都需要手动修改 pom.xml 文件,这将变得非常繁琐且容易出错。Maven Profile 的出现正是为了解决这个问题。

2.1.1 Profile 概述

Maven Profile 允许我们在 pom.xml 文件中定义多个构建配置方案(Profile),每个 Profile 可以包含不同的配置信息,例如:

  • 属性(Properties): 定义不同环境下的属性值,例如数据库连接字符串、版本号等。

  • 依赖(Dependencies): 根据环境引入不同的依赖,例如开发环境引入 mock 依赖,生产环境排除 mock 依赖。

  • 插件(Plugins): 根据环境配置不同的插件,例如不同的资源处理插件、打包插件等。

  • 构建目录(Build): 定制不同环境下的构建输出目录、资源目录等。

  • 报告(Reporting): 定制不同环境下的报告生成配置。

通过激活不同的 Profile,我们可以轻松切换构建环境,而无需修改 pom.xml 的核心配置。

2.1.2 Profile 的定义

Profile 可以定义在 pom.xml 文件中的 <profiles> 标签下。每个 Profile 使用 <profile> 标签包裹,并需要定义一个唯一的 <id> 作为 Profile 的标识。

<profiles> <profile> <id>dev</id> <!-- 开发环境 Profile 配置 --> </profile> <profile> <id>test</id> <!-- 测试环境 Profile 配置 --> </profile> <profile> <id>prod</id> <!-- 生产环境 Profile 配置 --> </profile> </profiles>

2.1.3 Profile 的激活

Maven 提供了多种方式来激活 Profile:

  • 命令行激活: 使用 -P 参数,例如 mvn clean install -Pdev 激活 dev Profile。可以同时激活多个 Profile,用逗号分隔,例如 -Pdev,test

  • Settings.xml 激活:settings.xml 文件中配置 <activeProfiles> 标签,可以设置默认激活的 Profile。

  • 环境变量激活: 通过设置环境变量来激活 Profile,例如定义一个名为 env 的环境变量,值为 dev,然后在 Profile 中使用 <activation> 标签进行判断。

  • 操作系统属性激活: 根据操作系统类型激活 Profile。

  • JDK 版本激活: 根据 JDK 版本激活 Profile。

  • 文件存在/不存在激活: 根据文件是否存在或不存在激活 Profile。

2.1.4 代码实践:使用 Profile 管理不同环境配置

假设我们有一个 Web 应用,需要连接不同的数据库,并且在开发环境和生产环境中使用不同的日志级别。

1. 定义 Profile:pom.xml 中定义 devprod 两个 Profile。

<profiles> <profile> <id>dev</id> <properties> <db.url>jdbc:mysql://localhost:3306/dev_db</db.url> <db.username>dev_user</db.username> <db.password>dev_password</db.password> <log.level>DEBUG</log.level> </properties> </profile> <profile> <id>prod</id> <properties> <db.url>jdbc:mysql://prod.example.com:3306/prod_db</db.url> <db.username>prod_user</db.username> <db.password>prod_password</db.password> <log.level>INFO</log.level> </properties> </profile> </profiles>

2. 使用 Profile 属性:pom.xml 或资源文件中使用 ${property.name} 引用 Profile 中定义的属性。例如,在 src/main/resources/application.properties 中配置数据库连接:

spring.datasource.url=${db.url} spring.datasource.username=${db.username} spring.datasource.password=${db.password} logging.level.root=${log.level}

3. 命令行激活 Profile 构建:

  • 开发环境构建:mvn clean install -Pdev

  • 生产环境构建:mvn clean install -Pprod

Maven 会根据激活的 Profile,替换资源文件中的占位符,并使用 Profile 中定义的属性值进行构建。

2.1.5 Profile 激活逻辑优先级

当存在多种激活方式时,Maven 的 Profile 激活逻辑优先级如下(从高到低):

  1. 命令行 -P 参数激活的 Profile

  2. Settings.xml 中 <activeProfiles> 标签激活的 Profile

  3. 环境变量、操作系统属性、JDK 版本、文件存在/不存在等 <activation> 标签激活的 Profile

2.1.6 graph TD 图示 Profile 激活流程

2.2 Maven Modules:构建大型模块化项目

当项目规模增大,功能模块增多时,将所有代码放在一个 Maven 项目中会变得难以维护和管理。Maven Modules (模块) 允许我们将一个大型项目拆分成多个独立的子模块,每个子模块可以是一个独立的 Maven 项目,拥有自己的 pom.xml 文件和代码结构。

2.2.1 Modules 概述

Maven Modules 的核心思想是将大型项目分解为更小、更易于管理的模块。每个模块可以独立编译、测试、打包和部署,模块之间可以存在依赖关系。

2.2.2 Module 的定义

要创建一个多模块项目,需要创建一个父模块 (Parent POM),父模块的 pom.xml 文件中需要使用 <modules> 标签来声明子模块。子模块需要在父模块的目录下创建独立的文件夹,并在子模块文件夹中创建 pom.xml 文件。

父模块 pom.xml 示例:

<groupId>com.example</groupId> <artifactId>parent-project</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <!-- 父模块必须使用 pom packaging --> <modules> <module>module-a</module> <module>module-b</module> <module>module-c</module> </modules>

子模块 pom.xml 示例 (例如 module-a/pom.xml):

<parent> <groupId>com.example</groupId> <artifactId>parent-project</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> <!-- 指向父模块 pom.xml 的相对路径 --> </parent> <artifactId>module-a</artifactId> <packaging>jar</packaging> <!-- 子模块可以是 jar, war, pom 等 packaging --> <!-- 子模块的依赖、插件等配置 -->

项目结构示例:

parent-project/ ├── pom.xml (父模块 pom) ├── module-a/ │ └── pom.xml (子模块 A pom) │ └── src/ │ └── ... (子模块 A 代码) ├── module-b/ │ └── pom.xml (子模块 B pom) │ └── src/ │ └── ... (子模块 B 代码) └── module-c/ └── pom.xml (子模块 C pom) └── src/ └── ... (子模块 C 代码)

2.2.3 Module 间的依赖

子模块之间可以相互依赖。例如,module-b 可以依赖 module-a。在 module-bpom.xml 文件中,可以像依赖普通的 Maven 项目一样,声明对 module-a 的依赖。

module-b/pom.xml 依赖 module-a 示例:

<dependencies> <dependency> <groupId>com.example</groupId> <artifactId>module-a</artifactId> <version>${project.version}</version> <!-- 使用父模块的版本号 --> </dependency> <!-- 其他依赖 --> </dependencies>

注意: 在多模块项目中,模块之间的依赖关系应该清晰明确,避免循环依赖。

2.2.4 Module 的构建

在父模块目录下执行 Maven 命令 (例如 mvn clean install),Maven 会自动按照模块依赖关系构建所有子模块。默认情况下,Maven 会按照 <modules> 标签中声明的顺序构建模块,但实际构建顺序会受到模块间依赖关系的影响。Maven 会自动处理模块间的依赖关系,确保被依赖的模块先构建。

2.2.5 代码实践:创建多模块 Web 应用

假设我们要创建一个简单的 Web 应用,包含 web 模块 (负责 Web 层)、service 模块 (负责业务逻辑层) 和 dao 模块 (负责数据访问层)。

1. 创建父模块 parent-webapp

mvn archetype:generate -DgroupId=com.example -DartifactId=parent-webapp -DarchetypeArtifactId=maven-archetype-pom -DinteractiveMode=false

修改 parent-webapp/pom.xml

<groupId>com.example</groupId> <artifactId>parent-webapp</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>webapp</module> <module>service</module> <module>dao</module> </modules> <dependencyManagement> <dependencies> <!-- 定义公共依赖版本,子模块继承 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.20</version> </dependency> <!-- ... 其他公共依赖 ... --> </dependencies> </dependencyManagement> <build> <pluginManagement> <plugins> <!-- 定义公共插件配置,子模块继承 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <!-- ... 其他公共插件 ... --> </plugins> </pluginManagement> </build>

2. 创建子模块 dao

cd parent-webapp mvn archetype:generate -DgroupId=com.example -DartifactId=dao -DarchetypeArtifactId=maven-archetype-jar -DinteractiveMode=false

修改 dao/pom.xml

<parent> <groupId>com.example</groupId> <artifactId>parent-webapp</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <artifactId>dao</artifactId> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <!-- DAO 模块依赖,例如数据库连接驱动等 --> </dependencies>

3. 创建子模块 service

cd parent-webapp mvn archetype:generate -DgroupId=com.example -DartifactId=service -DarchetypeArtifactId=maven-archetype-jar -DinteractiveMode=false

修改 service/pom.xml

<parent> <groupId>com.example</groupId> <artifactId>parent-webapp</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <artifactId>service</artifactId> <packaging>jar</packaging> <dependencies> <dependency> <groupId>com.example</groupId> <artifactId>dao</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <!-- Service 模块依赖 --> </dependencies>

4. 创建子模块 webapp

cd parent-webapp mvn archetype:generate -DgroupId=com.example -DartifactId=webapp -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

修改 webapp/pom.xml

<parent> <groupId>com.example</groupId> <artifactId>parent-webapp</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <artifactId>webapp</artifactId> <packaging>war</packaging> <dependencies> <dependency> <groupId>com.example</groupId> <artifactId>service</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- Webapp 模块依赖,例如 Spring MVC, JSP 等 --> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </build>

5. 构建整个 Web 应用:parent-webapp 目录下执行 mvn clean install

2.2.6 graph TD 图示多模块项目结构

2.3 Maven Dependency Management:高级依赖管理

Maven 的依赖管理是其核心功能之一。在进阶章节,我们需要深入了解更高级的依赖管理技巧,例如依赖范围 (Scope)、可选依赖 (Optional Dependencies)、依赖管理 (Dependency Management) 和依赖排除 (Exclusions)。

2.3.1 依赖范围 (Scope)

依赖范围 (Scope) 用于控制依赖在不同构建阶段 (例如编译、测试、运行时) 的可见性和作用。Maven 提供了以下几种常用的依赖范围:

  • compile (默认范围): 编译依赖范围。在所有构建阶段都可用,并且会被打包到最终的构件中。

  • provided: 已提供依赖范围。编译和测试时可用,但运行时由容器 (例如 Web 服务器) 提供,不会被打包到最终的构件中。例如 Servlet API。

  • runtime: 运行时依赖范围。编译时不需要,但在运行时需要。例如 JDBC 驱动。

  • test: 测试依赖范围。只在测试阶段可用,不会被打包到最终的构件中。例如 JUnit。

  • system: 系统依赖范围。类似于 provided,但需要显式指定依赖的系统路径,不推荐使用。

  • import: 导入依赖范围。只在 <dependencyManagement> 中使用,用于导入其他 POM 中的依赖配置,类似于继承。

代码示例:不同依赖范围的声明

<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <scope>compile</scope> <!-- 默认 compile 范围,可以省略 --> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.29</version> <scope>runtime</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies>

2.3.2 可选依赖 (Optional Dependencies)

可选依赖 (Optional Dependencies) 允许模块声明一些依赖,但这些依赖不是强制的。当其他模块依赖该模块时,可以选择是否引入这些可选依赖。

代码示例:声明可选依赖

module-a/pom.xml 中声明可选依赖:

<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.76</version> <optional>true</optional> <!-- 声明为可选依赖 --> </dependency> <!-- ... 其他依赖 ... --> </dependencies>

module-b 依赖 module-a 时,默认情况下不会传递 fastjson 这个可选依赖。如果 module-b 也需要使用 fastjson,则需要在 module-b/pom.xml 中显式声明对 fastjson 的依赖。

2.3.3 依赖管理 (Dependency Management)

<dependencyManagement> 标签用于在父模块中集中管理依赖的版本信息。在 <dependencyManagement> 中声明的依赖不会被实际引入,而是作为依赖版本管理的模板。子模块可以通过继承父模块的 <dependencyManagement> 来统一管理依赖版本,避免版本冲突。

代码示例:使用 <dependencyManagement> 管理依赖版本

在父模块 pom.xml 中:

<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.20</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.20</version> </dependency> <!-- ... 其他依赖版本管理 ... --> </dependencies> </dependencyManagement>

在子模块 pom.xml 中,只需要声明 groupIdartifactId,版本号会自动从父模块的 <dependencyManagement> 中继承:

<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <!-- ... 其他依赖 ... --> </dependencies>

2.3.4 依赖排除 (Exclusions)

当引入一个依赖时,该依赖可能会传递性地引入其他依赖。有时我们可能需要排除某些传递性依赖,可以使用 <exclusions> 标签来实现。

代码示例:排除传递性依赖

假设 module-a 依赖了 module-b,而 module-b 又传递性地依赖了 log4j:log4j:1.2.17。如果我们不想引入 log4j:log4j:1.2.17 这个传递性依赖,可以在 module-a/pom.xml 中排除它:

<dependencies> <dependency> <groupId>com.example</groupId> <artifactId>module-b</artifactId> <version>1.0-SNAPSHOT</version> <exclusions> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> </dependency> <!-- ... 其他依赖 ... --> </dependencies>

2.3.5 graph TD 图示依赖范围

2.4 Maven Plugin:深入插件与构建生命周期

Maven 的核心概念之一就是插件 (Plugin)。Maven 的构建过程实际上是由一系列插件的执行组成的。Maven 插件可以扩展 Maven 的功能,完成各种构建任务,例如编译代码、运行测试、打包构件、部署应用等。

2.4.1 插件目标 (Goal) 与生命周期 (Lifecycle)

Maven 插件通常包含多个目标 (Goal),每个目标代表一个特定的构建任务。例如 maven-compiler-plugin 插件包含 compiletestCompile 两个目标,分别用于编译主代码和测试代码。

Maven 构建生命周期 (Lifecycle) 定义了构建过程的阶段 (Phase)。Maven 内置了三个核心生命周期:

  • clean: 清理项目。

  • default (build): 构建项目。

  • site: 生成项目站点文档。

每个生命周期又包含多个阶段 (Phase),例如 default 生命周期包含 compiletestpackageinstalldeploy 等阶段。

插件的目标可以绑定到生命周期的阶段。当执行某个生命周期阶段时,Maven 会执行绑定到该阶段的所有插件目标。

2.4.2 插件的配置

插件可以通过 <configuration> 标签进行配置。不同的插件有不同的配置参数。

代码示例:配置 maven-compiler-plugin 插件

<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <!-- 指定源代码版本 --> <target>1.8</target> <!-- 指定目标 JVM 版本 --> <encoding>UTF-8</encoding> <!-- 指定字符编码 --> </configuration> </plugin> </plugins> </build>

2.4.3 插件的绑定 (Binding)

插件目标可以绑定到生命周期的阶段。Maven 已经为常用的插件目标预定义了默认的绑定关系。例如 maven-compiler-plugin:compile 默认绑定到 compile 阶段,maven-surefire-plugin:test 默认绑定到 test 阶段。

我们也可以自定义插件目标的绑定关系,例如将某个插件目标绑定到特定的生命周期阶段。

代码示例:自定义插件目标绑定

<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.0.0</version> <executions> <execution> <id>my-execution</id> <phase>process-resources</phase> <!-- 绑定到 process-resources 阶段 --> <goals> <goal>java</goal> <!-- 执行 exec-maven-plugin 的 java 目标 --> </goals> <configuration> <mainClass>com.example.MyMainClass</mainClass> </configuration> </execution> </executions> </plugin> </plugins> </build>

2.4.4 常用的 Maven 插件

  • maven-compiler-plugin: 编译 Java 源代码。

  • maven-surefire-plugin: 运行 JUnit 测试。

  • maven-war-plugin: 打包 Web 应用为 WAR 文件。

  • maven-jar-plugin: 打包 Java 应用为 JAR 文件。

  • maven-deploy-plugin: 部署构件到远程仓库。

  • maven-resources-plugin: 处理资源文件。

  • maven-clean-plugin: 清理项目。

  • maven-install-plugin: 安装构件到本地仓库。

  • maven-dependency-plugin: 处理项目依赖。

  • exec-maven-plugin: 执行系统命令或 Java 程序。

2.4.5 graph TD 图示 Maven 构建生命周期与插件

2.5 Maven Repository:仓库与依赖查找

Maven 仓库 (Repository) 用于存储 Maven 项目的构件 (Artifacts) 和元数据 (Metadata)。Maven 仓库分为本地仓库 (Local Repository) 和远程仓库 (Remote Repository)。

2.5.1 本地仓库 (Local Repository)

本地仓库是存储在开发者本地机器上的仓库,用于缓存从远程仓库下载的构件,以及存储本地构建生成的构件。默认情况下,本地仓库位于用户目录下的 .m2/repository 目录。

2.5.2 远程仓库 (Remote Repository)

远程仓库是存储在网络上的仓库,用于共享构件。Maven 默认配置了一个中央仓库 (Maven Central Repository),地址为 https://repo.maven.apache.org/maven2/。我们也可以配置其他的远程仓库,例如私有仓库 (Nexus, Artifactory) 或镜像仓库。

2.5.3 仓库类型

  • 中央仓库 (Maven Central Repository): Maven 官方提供的公共仓库,存储了大量的开源 Java 构件。

  • 私有仓库 (Private Repository): 企业或组织内部搭建的仓库,用于存储内部构件和管理第三方依赖。常用的私有仓库管理工具包括 Nexus 和 Artifactory。

  • 镜像仓库 (Mirror Repository): 远程仓库的镜像,用于加速构件下载。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U