Maven Core Lecture

发布于:2024-05-19 ⋅ 阅读:(158) ⋅ 点赞:(0)

Maven 简介

Maven 是一款基于 Java 平台的项目构建,依赖管理的工具,使用 Maven 可以自动化构建,测试,打包和发布项目,大大提高了程序员的开发效率。

依赖管理

Maven 可以管理项目的依赖,包括自动下载依赖库,自动下载依赖需要的依赖并保证版本没有冲突,依赖版本管理等。

构建管理

项目构建指将源代码,配置文件,资源文件等转化为能够运行或部署的应用程序或库的过程。

Maven 可以管理项目的编译、测试、打包、部署等构建过程。通过实现标准的生命周期,Maven 可以确保每一个构建过程都遵循同样的规则和最佳实践。同时,Maven 的插件机制也使得开发者可以对构建过程进行扩展和定制。
image_OSOE45UACw.png

Maven 原理

image_6AVFQbaXLj.png

Maven 坐标

Maven 中的坐标(GAVP) 是指 GroupId、ArtifactId、Version、Packaging 等四个属性缩写,其中 GAV 是必要的,Packaging 是可选的。

GAV 遵循以下规则:

  • GroupId:com.company.业务线.子也无线,最多四级
  • ArtifactId:产品线名-模块名,不重复
  • Version:主版本号.次版本号.修订号
  • Packaging:Maven 项目的打包方式,默认 jar。通过 packaging 配置来打包,packaging = jar时,打成 jar 包;packaging=war,打成 war 包;packaging=pom 时,代表不会打包,用来做项目聚合或父工程。

Maven 依赖

依赖配置

pom 文件中跟元素 project 下的 dependencies 可以包含一个或多个dependency 元素来声明多个项目依赖。每个依赖包含的元素如下:

  • groupId、artifactId、version:依赖的基本坐标
  • type:依赖的类型,对应于项目坐标定义的 packaging。大部分情况不用声明,默认值 jar
  • scope:依赖范围
  • optional:标记依赖是否可选
  • exclusions:排除传递性依赖

依赖配置示例

<project>
  ...
  <dependencies>
    <dependency>
      <groupId>...</groupId>
      <artifactId>...</artifactId>
      <version>...</version>
      <!--如下可选-->
      <type>...</type>
      <scope>...</scope>
      <optional>...</optional>
      <exclusions>
        <exclusion>
          <groupId>...</groupId>
          <artifactId>...</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>
</project>

依赖范围

依赖范围就是用来控制依赖和三种 classpath(编译 classpath、测试 classpath 和运行 classpath)的关系。

Maven 有以下几种依赖范围

  • compile:编译依赖范围,默认的依赖范围,对编译、测试和运行三种 classpath 均有效。
  • test:测试依赖范围。只对测试 classpath 有效
  • provided:已提供依赖范围,对编译和测试 classpath 有效
  • runtime:运行时依赖范围,对测试和运行 classpath 有效
  • system:系统依赖范围,和 provided 依赖范围完全一致。但使用 system 时必须通过 systemPath 元素显示指定依赖文件的路径
  • import:导入依赖范围,该依赖不会对三种 classpath 产生实际影响。

上述除 import 外的依赖范围与三种 classpath 的关系如下表

| 依赖范围

(Scope) 对于编译 classpath 有效 对于测试 classpath 有效 对于运行 classpath 有效 示例
compile Y Y Y spring-core
test - Y - Junit
provided Y Y - servlet-api
runtime - Y Y JDBC驱动实现
system Y Y - 本地的,Maven 仓库之外的类库文件

依赖范围和传递性依赖

Maven 的传递性依赖就是解析直接依赖的 POM ,将那些必要的间接依赖,以传递性依赖的形式引入到当前项目中。

依赖范围不仅可以控制依赖和三种 classpath 的关系,还对传递性依赖产生影响。如下表

第二直接依赖
compile test provided runtime
第一直接依赖 compile compile - - runtime
test test - - test
provided provided - provided provided
runtime runtime - - runtime

规律如下:

依赖调解

传递性依赖一方面大大简化了依赖声明,大部分情况下我们只关心直接依赖,不用考虑传递性依赖。但有时候传递性依赖也会造成问题,我们就需要清楚知道传递性依赖是从哪条依赖路径引入的。
Maven 依赖调解(Dependency Mediation)有两个原则。

  • 原则一:路径最近者优先
  • 原则二:第一声明优先

原则一说明:A -> B -> X(1.0),A -> D -> E -> X(2.0),X 是 A 的传递性依赖,但两条路径上有两个版本 X。Maven 会根据第一原则进行调解,X(1.0)的路径长度是 2,X(2.0) 的路径长度是 3,因此 X(1.0) 会被解析使用。

原则二说明:A -> B -> Y(1.0)、A -> C -> Y(2.0),Y 的路径都是 2,Maven 会根据在 POM 中依赖声明的顺序决定谁会被解析使用。

排除依赖

通过 Maven 的依赖调解后,还可能存在引入多个版本的依赖。此时可以通过手动配置的方式来进行依赖排除、

<dependency>
  <groupId>com.hongguo.maven</groupId>
  <artifactId>pro01-maven-java</artifactId>
  <version>1.0-SNAPSHOT</version>
  <scope>compile</scope>
  <!-- 使用excludes标签配置依赖的排除  -->
  <exclusions>
    <!-- 在exclude标签中配置一个具体的排除 -->
    <exclusion>
      <!-- 指定要排除的依赖的坐标(不需要写version) -->
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
    </exclusion>
  </exclusions>
</dependency>

Maven 生命周期和插件

Maven 的生命周期是抽象的,本身不做任何实际工作,实际的任务都是交由插件来完成的。

三套生命周期

Maven 拥有三套生命周期,分别是 clean、default 和 site。官网文档

  • clean:目的是清理项目
  • default:目的是构建项目
  • site:目的是建立项目站点
clean生命周期

clean 生命周期是清理项目,包括三个阶段:

  • pre-clean:清理前需要完成的工作
  • clean:清理构建生成的文件
  • post-clean:清理后需要完成的工作
default生命周期

default 生命周期定义了构建时所执行的所有步骤,包含如下阶段:

  • validate
  • initialize
  • generate-sources
  • process-sources:处理项目主资源文件,一般来说,是对 src/main/resources 目录的内容进行变量替换等工作后,复制到项目输出的主 classpath 目录中
  • generate-resources
  • process-resources
  • compile:编译项目的主源码。一般来说,是编译 src/main/java 目录下的 java 文件至项目输出的主 classpath 目录中
  • process-classes
  • generate-test-sources
  • process-test-sources:处理项目测试资源文件,一般来说,是对 src/ test/resources 目录的内容进行变量替换等工作后,复制到项目输出的测试 classpath 目录中。
  • generate-test-resources
  • process-test-resources
  • test-compile:编译项目的测试代码,一般来说,是编译 src/test/java 目录下的 java 文件至项目输出的测试 classpath 目录中
  • process-test-classes
  • test:使用单元测试框架进行测试,测试代码不会被打包或部署
  • prepare-package
  • package:接受编译好的代码,打包成可发布的格式,如 JAR 或 WAR
  • pre-integration-test
  • integration-test
  • post-integration-test
  • verify
  • install:将包安装到 Maven 本地仓库,供本地其他 Maven 项目使用
  • deploy:将最终的包复制到远程仓库,供其他开发人员和 Maven 项目使用
site生命周期

site 生命周期目的是建立和发布项目站点,Maven 基于 POM 所包含的信息,自动生成一个友好的站点,方便团队交流和发布项目信息,包含如下阶段:

  • pre-site:执行生成站点之前需要完成的工作
  • site:生成项目的站点文档
  • post-site:执行生成项目站点之后需要完成的工作
  • site-deploy:将生成的项目站点发布到服务器上

命令行和生命周期

从命令行执行 Maven 任务的最主要方式就是调用 Maven 的生命周期阶段

mvn clean:该命令调用 clean 生命周期的 clean 阶段。实际执行阶段为 clean 生命周期的 pre-clean 和 clean。

mvn test:该命令调用 default 生命周期的 test 阶段。实际执行的阶段为 default 生命周期的 validate、initialize 等,直到 test 的所有阶段。

mvn clean install:该命令调用 clean 生命周期的 clean 阶段和 default 生命周期的 install 阶段。实际执行的阶段为 clean 生命周期的 pre-clean、clean 阶段,以及 default 生命周期的从 validate 至 install 的所有阶段。

插件

插件目标

对于插件本身,为了能够复用代码,因此将多个功能聚集到一个插件里,每个功能就是一个插件目标。执行插件的命令通用写法为:插件名:目标名,例如:compile:compile(maven-compiler-plugin 的 compile 目标)

插件绑定

Maven 的生命周期与插件互相绑定,用以完成实际的构建任务。具体而言,是生命周期的阶段与插件的目标相互绑定,以完成某个具体的构建任务。如下图:生命周期阶段与插件目标绑定

内置绑定

Maven 的核心的生命周期阶段绑定了很多插件的目标。
clean 生命周期与插件目标绑定关系

生命周期阶段 插件目标
pre-clean
clean maven-clean-plugin:clean
post-clean

site生命周期与插件目标绑定关系

生命周期阶段 插件目标
pre-site
site maven-site-plugin:site
post-site maven-site-plugin:deploy
site-deploy

default生命周期与插件目标绑定关系

生命周期阶段 插件目标 执行任务
process-resources maven-resources-plugin:resources 复制主资源文件至主输出目录
compile maven-compiler-plugin:compile 编译主代码至主输出目录
process-test-resources maven-resources-plugin:testResources 复制测试资源文件至测试输出目录
test-compile maven-compiler-plugin:testCompile 编译测试代码至测试输出目录
test maven-surefire-plugin:test 执行测试用例
package maven-jar-plugin:jar 创建项目 jar 包
install maven-install-plugin:install 将项目输出构件安装到本地仓库
deploy maven-deploy-plugin:deploy 将项目输出构件部署到远程仓库

help 插件可以查看插件目标默认绑定的生命周期阶段

$mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin -Ddetail

......
source:jar-no-fork
  Description: This goal bundles all the sources into a jar archive. This
    goal functions the same as the jar goal but does not fork the build and is
    suitable for attaching to the build lifecycle.
  Implementation: org.apache.maven.plugins.source.SourceJarNoForkMojo
  Language: java
  Bound to phase: package
......

自定义绑定

除了内置绑定之外,用户可以选择将某个插件目标绑定到生命周期的某个阶段上。示例:创建项目的源码 jar 包,可以将 maven-source-plugin 插件的目标 jar-no-fork 绑定到default 的生命周期的 verify 阶段上。

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-source-plugin</artifactId>
      <version>...</version>
      <executions>
        <execution>
          <id>attach-sources</id>
          <phase>verify</phase>
          <goals>
            <goal>jar-no-fork</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

maven-help-plugin 插件,官网

  • 重点的目标(待细化)
  • help:describe
  • help:evaluate

聚合和继承

聚合

概念

Maven 聚合是指将多个项目组织到一个父级项目中,以便一起构建和管理的机制。

作用
  • 管理多个子项目
  • 构建和发布一组相关的项目
  • 优化构建顺序
  • 统一管理依赖项
语法

父项目中包含子项目列表

<project>
  <groupId>com.hongguo.maven</groupId>
  <artifactId>parent-project</artifactId>
  <version>1.0.0</version>

  <modules>
    <!-- 引入子项目 -->
    <module>child-project1</module>
    <module>child-project2</module>
  </modules>
</project>

继承

概念

Maven 继承是指在 Maven 的项目中,让一个项目从另一个项目中继承配置信息的机制

作用
  • 父工程中统一管理项目中的信息,包括但不限于依赖信息,版本信息等 POM 中可定义的所有配置
语法
  • 父工程
<project>
  <groupId>com.hongguo.maven</groupId>
  <artifactId>parent-project</artifactId>
  <version>1.0.0</version>
  <!-- 父工程打包方式为 pom -->
  <packaging>pom</packaging>
</project>
  • 子工程
<project>
  <parent>
    <!-- 父工程坐标 -->
    <groupId>com.hongguo.maven</groupId>
    <artifactId>parent-project</artifactId>
    <version>1.0.0</version>
  </parent>

  <!-- 子工程坐标 -->
  <!-- 子工程坐标中 groupId 和 version 与父工程一致,则可以省略 -->
  <!-- <groupId>com.hongguo.maven</groupId> -->
  <artifactId>chile-project</artifactId>
  <!-- <version>1.0.0</version> -->
</project>
父工程依赖统一管理
  • 父工程声明依赖
<project>
  <properties>
    <spring-core.version>6.0.10</spring-core.version>
  </properties>
  
  <!-- 使用dependencyManagement标签配置对依赖的管理 -->
  <!-- 被管理的依赖并没有真正被引入到工程 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring-core.version}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring-core.version}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring-core.version}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>${spring-core.version}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring-core.version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>
  • 子工程引入依赖
<project>
  <!-- 子工程引用父工程中的依赖信息时,可以把版本号去掉。  -->
  <!-- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定。 -->
  <!-- 具体来说是由父工程的dependencyManagement来决定。 -->
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
    </dependency>
  </dependencies>
</project>

约定优于配置

原因之一是,使用约定可以大量减少配置。

版本管理

Maven的版本号约定

Maven 的版本号定义约定如下:

<主版本号>.<次版本号>.<增量版本号>-<里程碑版本号>

  • 主版本号:表示项目的重大架构变更
  • 次版本号:表示较大范围的功能增加和变化,以及 Bug 修复
  • 增量版本号:一般表示重大 Bug 的修复
  • 里程碑版本号:指某一个版本的里程碑

主干、标签和分支

  • 主干:开发代码主体
  • 分支:从主干上某个点分离出来的代码
  • 标签:用来标识主干或分支的某个点状态

三者之间的关系如下:
2007520174.jpg

Maven 灵活构建

Maven 属性

POM 中通过元素进行自定义Maven 属性,在 POM 中其他地方使用**${属性名称}**方式引用该属性。
Maven 中包含六类属性

  • 内置属性:主要有两个常用内置属性
    • ${basedir}:项目根目录
    • ${version}:项目版本
  • POM 属性:指 POM 文件中对应元素的值
    • ${project.artifactId}
    • ${project.build.sourceDirectory}
    • ${project.build.testSourceDirectory}
    • ${project.build.directory}
    • ${project.outputDirectory}
    • ${project.testOutputDirectory}
    • ${project.groupId}
    • ${project.versioin}
    • ${project.build.finalName}
  • 自定义属性:在 POM 中元素下自定义 Maven 属性
    • ${hello.world}
  • Settings 属性:指 settings.xml 文件中 XML 元素的值,可以通过 settings.开头的属性来引用
  • Java 系统属性:java 系统属性可以使用 Maven 属性引用
    • ${user.home}
  • 环境变量属性:所有环境变量可以使用 env. 开头的 Maven 属性引用
    • ${env.JAVA_HOME}

Maven Profile

profile 能够在构建的时候修改 POM 的子集,或者添加额外的配置元素。用户可以使用很多方式激活 profile,以实现构建在不同环境下的移植

profile 配置

POM 中的元素可以定义多个 profile

<project>
  ...
  <profiles>
    <profile>
      <id>dev</id>
      <properties>
        <k1>v1</k1>
      </properties>
    </profile>
    <profile>
      <id>test</id>
      <properties>
        <k1>v2</k1>
      </properties>
    </profile>
  </profiles>
  ...
</project>
激活 profile
  • 命令行激活(-P)
    • mvn clean package -Pdev-x,dev-y(激活 dev-x和 dev-y 两个 profile)
  • settings 文件显式激活

配置 settings.xml 文件的 activeProfiles 元素,对于所有项目都处于激活状态

<settings>
  <activeProfiles>
    <activeProfile>dev-x</activeProfile>
    <activeProfile>dev-y</activeProfile>
  </activeProfiles>
</settings>
  • 系统属性激活

用户可以配置当某个系统属性存在时,自动激活 profile。

<profiles>
  <profiler>
    <activation>
      <property>
        <name>test</name>
      </property>
    </activation>
  </profiler>
</profiles>

可以进一步配置当某系统属性 test 存在,且值等于 x 的时候激活 profile

<profiles>
  <profiler>
    <activation>
      <property>
        <name>test</name>
        <value>x</value>
      </property>
    </activation>
  </profiler>
</profiles>

可以在命令行声明系统属性来激活 profile

mvn clean install -Dtest=x

  • 操作系统环境激活
<profiles>
  <profiler>
    <activation>
      <os>
        <name>Windows XP</name>
        <family>Windows</family>
        <arch>x86</arch>
        <version>5.1.2600</version>
      </os>
    </activation>
  </profiler>
</profiles>
  • 文件存在是否激活

Maven 能够根据项目中某个文件存在与否来决定是否激活 profile

<profiles>
  <profiler>
    <activation>
      <file>
        <missing>x.properties</missing>
        <exists>y.properties</exists>
      </file>
    </activation>
  </profiler>
</profiles>
  • 默认激活

用户可以在定义 profile 的时候指定其默认激活

<project>
  ...
  <profiles>
    <profile>
      <id>dev</id>
      <properties>
        <k1>v1</k1>
      </properties>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
    </profile>
    <profile>
      <id>test</id>
      <properties>
        <k1>v2</k1>
      </properties>
    </profile>
  </profiles>
  ...
</project>
profile 种类

根据具体需要,可以在以下位置声明 profile

  • pom.xml:pom.xml 中声明的 profile 只对当前项目有效
  • 用户 settings.xml:用户目录下 .m2/settings.xml 中的 profile 对该用户所有 Maven 项目有效
  • 全局 settings.xml:Maven 安装目录下 conf/settings.xml 中的 profile 对本机上所有 Maven 项目有效

Maven 插件

Maven 的核心是一个插件执行框架,所有工作都是由插件来完成。

核心插件

maven-clean-plugin

作用:清理编译生成的目标文件和目录
绑定:clean 生命周期中的 clean 阶段,绑定的是 clean 目标,实际执行是 pre-clean 和 clean 目标

  • 执行命令
$mvn clean
  • 清理自动化

将 clean 绑定到 default 生命周期的 initialize 阶段,配置如下

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-clean-plugin</artifactId>
      <version>...</version>
      <executions>
        <execution>
          <id>auto-clean</id>
          <phase>initialize</phase>
          <goals>
            <goal>clean</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>
  • 跳过删除工作目录
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-clean-plugin</artifactId>
      <version>...</version>
      <configuration>
        <skip>true</skip>
      </configuration>
    </plugin>
  </plugins>
</build>
  • 删除其他文件或目录
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-clean-plugin</artifactId>
      <version>...</version>
      <filesets>
        <fileset>
          <directory>${basedir}/report</directory>
        </fileset>
      </filesets>
    </plugin>
  </plugins>
</build>
maven-compiler-plugin

作用:完成项目工程编译任务
绑定:default 生命周期的 compile 阶段,按顺序执行直到 compile 阶段的所有阶段。compile 阶段本身执行 compile 目标

  • 指定编译的 java 版本(方式一)
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>...</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>
  • 指定编译的 java 版本(方式二)
<properties>
  <maven.compiler.source>1.8</maven.compiler.source>
  <maven.compiler.target>1.8</maven.compiler.target>
</properties>
  • 增加编译参数
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>...</version>
      <configuration>
        <compilerArgument>-Xlint:deprecation</compilerArgument>
      </configuration>
    </plugin>
  </plugins>
</build>
maven-surefire-plugin

作用:运行单元测试的插件
绑定:default 生命周期的 test 阶段,按顺序执行直到 test 阶段的所有阶段。test 阶段本身执行 test 目标

  • 跳过 test (方式一):会编译测试代码,但不执行
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-test-plugin</artifactId>
      <version>...</version>
      <configuration>
        <skipTest>true</skipTest>
      </configuration>
    </plugin>
  </plugins>
</build>
  • 跳过 test(方式二):会编译测试代码,但不执行
$mvn -DskipTests test
  • 跳过 test(方式三):不编译测试代码,也不执行(打包线上版本时推荐使用
$mvn package -Dmaven.test.skip=true
maven-failsafe-plugin

作用:执行集成测试
绑定:default 生命周期的 verify 阶段,按顺序执行直到 verify 阶段的所有阶段。verify 阶段本身执行intefration-test 和 verify 目标

  • 配置
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-failsafe-plugin</artifactId>
      <version>...</version>
      <executions>
        <execution>
          <id>integration-tests</id>
          <goals>
            <goal>integration-test</goal>
            <goal>verify</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>
maven-resources-plugin

作用:复制项目资源文件到输出目录
绑定:default 生命周期的resource 阶段

  • 默认配置
<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
    </resource>
  </resources>
</build>
  • 增加其他资源目录
<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
    </resource>
    <resource>
      <directory>src/main/additional</directory>
    </resource>
  </resources>
</build>
  • 过滤资源文件
<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
    </resource>
    <resource>
      <directory>src/main/additional</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
</build>

网站公告

今日签到

点亮在社区的每一天
去签到