引言

maven项目的核心配置文件是 pom.xml,POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述如何构建、声明项目依赖以及其他所有的项目相关因素。

可以说,pom.xml包括了项目的所有信息。

基础 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.fuoshuo</groupId>  
  <artifactId>data-extract</artifactId>  
  <packaging>jar</packaging>  
  <version>1.0</version> 

</project>
以上的配置打包会获得一个格式如:data-extract-1.0.jar 的包
  • project:pom.xml 的根元素。此外还声明了一些如xmlns=…等与 POM 相关的命名空间及 xsd 元素属性,用来指定标签书写规范和遵循的语法,这些属性不是必须的。
  • modelVersion:项目描述符。声明了遵循哪一个 POM 模型版本。模型本身的版本很少改变,虽然如此,但它仍然是必不可少的,这是为了当 Maven 引入了新的特性或者其他模型变更的时候,确保稳定性。对于 maven2 和 maven3 来说,它只能是4.0.0
  • groupId:全球唯一标识符、组织ID。往往和项目所在的组织或公司存在关联,一般格式为域名倒写。
  • artifactId:当前项目在 groupId 中的唯一标识符。一般命名格式为项目名-模块名,避免使用.等特殊符号。
  • packaging:打包类型。描述了项目打包后的输出类型。常见的类型有:pom,jar,war。如果省略,默认为jar
  • version:版本号,顾名思义。

详解 packaging 标签

以上常见的属性都不陌生,只有packaging标签比较重要。

要知道如下几点:

  1. packaging 如果没有设置,默认为<packaging>jar<packaging>
  2. 如果 maven 项目为“父子”工程,父工程的打包类型必须为<packaging>pom<packaging>。父级的pom文件只作项目的子模块的整合,在mvn package时不会生成jar/war压缩包。
  3. 一般项目作为内部调用或者作服务使用,打包方式为<packaging>jar<packaging>
  4. 作为需要 web 中间件部署的项目,打包方式为<packaging>war<packaging>。它会将项目中依赖的所有jar包都放在WEB-INF/lib这个文件夹下。

项目信息

此部分标签可省略,仅限于描述项目的详细信息。以下不是全部:

<!-- 项目描述名 -->
<name></name>
<!-- 项目地址 -->
<url>https://www.fuoshuo.com</url>
<!-- 项目描述 -->
<description>a little project,is very cool!</description>
<!-- 项目开发年份 -->
<inceptionYear>2020</inceptionYear>
<!-- 开源协议 -->
<licenses>
    <license>
        <!-- license用于法律上的名称 -->
        <name>Apache License, Version 2.0</name>
        <!-- 官方的 license 正文页面的 URL -->
        <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
        <!-- 项目分发的主要方式:
                repo,可以从Maven库下载
                manual, 用户必须手动下载和安装依赖 -->
        <distribution>repo</distribution>
        <!-- 关于license的补充信息 -->
        <comments>A business-friendly OSS license</comments>
    </license>
</licenses>
<!-- 组织信息(如公司、开源组织等) -->
<organization>
    <name>my</name>
    <url>...</url>
</organization>
<!-- 开发者列表 -->
<developers>
    <developer>
        <id>victor</id>
        <name>ZS</name>
        <email>zs@fuoshuo.com</email>
        <url>https://fuoshuo.com</url>
        <!-- 项目开发者所属组织 -->
        <organization>...</organization>
        <!-- 项目开发者所属组织的URL -->
        <organizationUrl>...</organizationUrl>
        <!-- 项目开发者在项目中扮演的角色,角色元素描述了各种角色 -->
        <roles>
            <role>architect</role>
            <role>developer</role>
        </roles>
        <!-- 项目开发者所在时区, -11到12范围内的整数 -->
        <timezone>+8</timezone>
        <!-- 项目开发者属性,如即时消息如何处理等 -->
        <properties>
            <dept>No</dept>
        </properties>
    </developer>
</developers>
<!-- 代码贡献者列表 -->
<contributors>
    <contributor>
        <!-- 标签内容和 <developer> 内相同 -->
    </contributor>
</contributors>

依赖配置

在 maven 中,根据 groupIdartifactIdversion 组合成 groupId:artifactId:version 来唯一识别一个 jar 包。称之为 maven 坐标。

parent

maven 支持继承功能(传递性依赖)。子 POM 必须使用 parent 指定父 POM ,然后继承其配置。

子 pom 的packaging不一定要是pom,但一定有parent标签
<parent>
    <groupId>com.fuoshuo</groupId>
    <artifactId>zs-server</artifactId>
    <version>1.0</version>
    <!-- <relativePath></relativePath> -->
</parent>
  • relativePath:不是必需的,在 maven 搜索本地和远程存储库之前,可以用作指示符指定该父级项目的路径,优先搜索该路径

properties

可自定义属性,如版本属性用于统一管理版本,使用方式如${java.version}

<properties>
    <java.version>1.8</java.version>
    <spring-boot.version>2.1.6.RELEASE</spring-boot.version>
    <!-- ... -->
</properties>

内置属性:

属性说明
${project.basedir}项目的根路径,与当前使用的pom.xml同级
${project.version}项目的 version,与${version}等价
${project.build.sourceDirectory}项目的主源码目录,默认为src/main/java
${project.build.testSourceDirectory}项目的测试源码目录,默认为src/test/java
${project.build.directory}项目构件输出目录,默认为target/
${project.outputDirectory}项目主代码编译输出目录,默认为target/classes/
${project.testOutputDirectory}项目测试代码编译输出目录,默认为target/test-classes/
${project.groupId}项目的groupId
${project.artifactId}项目的artifactId
${project.build.fianlName}项目打包输出文件的名称,默认为"${project.artifactId} - ${project.version}"

扩展:

  1. 用户使用${settings.}开头的属性可以引用 maven settings.xml 文件中XML元素的值
  2. 用户使用${java.}开头的属性可以引用 Java系统属性,如${java.home}
  3. 用户使用${env.}开头的属性可以引用系统环境变量,如${env.JAVA_HOME}

modules

多模块(聚合)列表。用于一次构建所有想要构建的项目。

聚合 pom 文件的packaging标签一定要是pom,其工程就只是一个 pom 文件,没有其他的实现代码
<modules>
    <module>zs-server</module>
    <!-- ... -->
</modules>

dependencies

<dependencies>
    <!--Lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
    <!-- ... -->
    <dependency>
        <groupId>xxx</groupId>
        <artifactId>xxx</artifactId>
        <version>x.x.x</version>
        <type>jar</type>
        <scope>import</scope>
        <optional>true</optional>
        <exclusions>
            <exclusion>
                <groupId>org.apache.maven</groupId>
                <artifactId>maven-core</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- ... -->
</dependencies>
在 maven 的依赖管理中,有两种方式可以对依赖关系进行管理,分别是可选依赖(Optional Dependencies)以及依赖排除(Dependency Exclusions)
  • type:依赖类型,一般省略默认是 jar。其他还有pom, war, ejb-client, test-jar
  • scope

    • compile:默认值。适用于所有阶段,会随着项目一起发布。而且这些依赖关系都会被传播到依赖项目中。
    • provided:表示您希望 jdk 或容器在运行时提供它。它只适用于编译和测试 classpath,不可传递。如lombok
    • runtime:只在运行时使用,如 JDBC驱动,适用运行和测试阶段。
    • test:只在测试时使用,用于编译和运行测试代码,不会随项目发布。
    • system:表示此依赖不需在仓库中查找,而是手动指定路径且始终可用。
  • optional:声明为可选依赖,默认值false。如果你在项目B中把C依赖声明为true可选,你就需要在依赖于B的项目(例如项目A)中显式的引用对C的依赖。
  • exclusions:排除项目依赖冲突时使用,不依赖该项目。包含一个或多个exclusion排除元素,每个排除元素都包含一个表示要排除的依赖关系的 groupIdartifactId,主动从依赖关系树中删除自己。
  • systemPath:仅当scopesystem时才使用。来指定依赖包的特定路径,为绝对路径。使用mvn help:system可查看所有可使用变量

dependencyManagement

声明 Jar 类型,但不会加载该依赖,此声明可以被子 POM 继承。

<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>

dependencyManagement 的一个使用案例是当有父子项目的时候,父项目中可以利用 dependencyManagement 声明子项目中需要用到的依赖 jar 包,之后,当某个或者某几个子项目需要加载该依赖的时候,就可以在子项目中 dependencies 节点只配置 groupIdartifactId 就可以完成依赖的引用。

dependencyManagement 主要是为了统一管理依赖包的版本,确保所有子项目使用的版本一致,类似的还有pluginspluginManagement

注意:

dependencyManagement里只是声明依赖,并不实现引入。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且versionscope都读取自父 pom。

构建配置

build

build 可以分为project buildprofile build(环境配置篇讲)。主要标签为pluginsresources。这里只说明project build

<!-- "Project Build" contains more elements than just the BaseBuild set -->
<build>...</build>

<profiles>
    <profile>
        <!-- "Profile Build" contains a subset of "Project Build"s elements -->
        <build>...</build>
    </profile>
</profiles>

springboot 项目常用构建配置:

方式一:第三方 jar 和应用一起打包,可直接使用java -jar ****.jar启动

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <!-- 参数配置,可省略 -->
            <configuration>
                <!-- 指定去掉后缀的工程名字,默认${artifactId}-${version} -->
                <finalName>${project.name}-${project.version}</finalName>
            </configuration>
        </plugin>
    <plugins>
</build>

方式二:第三方 jar 包单独打包到 lib,大大减小项目的体积。使用时需要指定 lib 目录如java -jar ***.jar -Dloader.path='lib'

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>target/lib</outputDirectory>
                        <excludeTransitive>false</excludeTransitive>
                        <stripVersion>false</stripVersion>
                        <includeScope>runtime</includeScope>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <layout>ZIP</layout>
                <includes>
                    <include>
                        <groupId>non-exists</groupId>
                        <artifactId>non-exists</artifactId>
                    </include>
                </includes>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                    <configuration>
                        <classifier>classes</classifier>
                        <attach>false</attach>
                        <finalName>tmp</finalName>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${basedir}/target/lib</outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <!-- 打jar包的main方法配置 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                        <mainClass>com.fuoshuo.xxx.xxx.xxxApplication</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>
属性说明
extensions决定是否要加载这个 plugin 的executions,默认为true
inherited是否让子 pom 继承,默认为true
configuration用于详细配置 plugin
dependenciesplugin 依赖,只作用于 plugin。子标签dependencie
executions设定 plugin 的执行方式(任务),子标签execution

详解 plugins & pluginManagement

maven 通过pluginspluginManagement(类似 dependenciesdependencyManagement)。在父项目中可以声明插件和版本信息,子项目可通过groupIdartifactId直接引用,且子 POM 有权重写。

pluginspluginManagement 隶属于 build 标签下,而不是最外层的 project

plugins 举例:使用 springboot 通用打包方式,且需要使用 Dockerfile 进行构建时,可使用 Ant 脚本在打包后将 Jar 复制到指定目录:

<!-- 使用Ant脚本进行打包时文件复制,用于 dockerfile 构建-->
<plugin>
    <!-- 官方的可以省略 groupId 标签 -->
    <artifactId>maven-antrun-plugin</artifactId>
    <!-- 没有 version 配置时,会默认下载最新的 release 版本 -->
    <!-- 插件执行的任务,可为多个 -->
    <executions>
        <execution>
            <!--绑定目标的生命周期阶段,如果省略,目标会被绑定到源数据里配置的默认阶段-->
            <phase>package</phase>
            <!-- 插件配置:在构建生命周期中执行一组目标的配置。每个目标可能有不同的配置。 -->
            <configuration>
                <tasks>
                    <copy todir="src/main/docker/jar/"
                          file="target/${project.name}-${project.version}.jar"></copy>
                </tasks>
            </configuration>
            <!-- 启用该插件的 xxx 功能 -->
            <goals>
                <goal>run</goal>
            </goals>
        </execution>
    </executions>
</plugin>

详解 resources & testResources

工程中各种文件资源的配置 。

<resources>
    <!-- 每个资源元素描述与此项目关联的文件和何处包含文件 -->
    <resource>
        <!-- 目标路径:将要包装在 jar 中的资源,通常指定的目标路径是 META-INF -->
        <targetPath>META-INF/plexus</targetPath>
        <!-- 是否要为此资源启用过滤 -->
        <filtering>false</filtering>
        <!-- 定义了资源的路径 默认是 ${basedir}/src/main/resources -->
        <directory>${basedir}/src/main/plexus</directory>
        <!-- 指定目录中要包括的文件,使用*作为通配符 -->
        <includes>
            <include>configuration.xml</include>
        </includes>
        <!-- 指定目录中要排除的文件,使用*作为通配符 -->
        <excludes>
            <exclude>**/*.properties</exclude>
        </excludes>
    </resource>
</resources>
<!-- testResources 与 resources 功能类似,指定的资源仅用于 test 阶段 -->
<!-- 默认资源目录为:${basedir}/src/test/resources -->
<testResources>
    <!-- 子标签与 resource 类似,不再举例-->
</testResources>

如果 includeexclude 发生冲突,maven 会以 exclude 作为有效项。

环境配置

repositories

maven 项目私服仓库地址配置。

maven 寻找 Jar 的大致顺序:

  1. 本地仓库中寻找,如果没有则进入下一步
  2. 全局应用的私服仓库中寻找,如果没有则进入下一步
  3. 项目自身的私服仓库中寻找,如果没有则进入下一步
  4. 中央仓库中寻找,如果没有则终止寻找

如果在找寻的过程中,如果发现该仓库有镜像设置,则用镜像的地址代替。

如果仓库的id设置成central,则该配置会覆盖 maven 默认的中央仓库配置。

<repositories>
    <!-- 阿里云国内仓库 -->
    <repository>
        <id>aliyun</id>
        <name>aliyun</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </repository>
    <!-- 你的私服仓库配置 -->
    <id>xxx</id>
    <name>xxx</name>
    <url>xxxxxx</url>
    <!-- 用于定位和排序构件的仓库布局类型-可以是 default(默认)或者 legacy(遗留)。Maven 2为其仓库提供了一个默认的布局;然而,Maven 1.x有一种不同的布局。我们可以使用该元素指定布局是 default(默认)还是legacy(遗留)。一般省略 -->
    <layout>default</layout>
    <snapshots>
        <!-- 允许从这个仓库下载 snapshot 版本的构件 -->
        <enabled>true</enabled>
        <!-- 指定更新发生的频率。Maven会比较本地POM和远程POM的时间戳。这里的选项是:always(一直),daily(默认,每日),interval:X(这里X是以分钟为单位的时间间隔),或者never(从不)。-->
        <updatePolicy>always</updatePolicy>
        <!-- 当Maven验证构件校验文件失败时该怎么做:ignore(忽略),fail(失败),或者warn(警告)。 -->
        <checksumPolicy>fail</checksumPolicy>
    </snapshots>
    <releases>
        <!-- 参考如上。例如,可能有人会决定只为开发目的开启对快照版本下载的支持 -->
        <enabled>true</enabled>
        <updatePolicy>always</updatePolicy>
        <checksumPolicy>warn</checksumPolicy>
    </releases>
    <!-- ... -->
</repositories>

distributionManagement

发布仓库配置。如果需要认证,则在setting.xml文件中增加一个元素进行配置,在配置完成后,执行mvn clean deploy 就会根据当前版本类型,发布到相应的仓库中。

<distributionManagement>
    <repository>
        <id>xxx-releases</id>
        <name>xxx-releases</name>
        <url>http://xxxxxx/repository/xxx-releases/</url>
    </repository>
    <snapshotRepository>
        <id>xxx-snapshots</id>
        <name>xxx-snapshots</name>
        <url>http://xxxxxx/repository/xxx-snapshots/</url>
    </snapshotRepository>
</distributionManagement>

profiles

activation属性 是一个 profile 的关键,它指定了这个属性来激活profile来修改基础 POM。

例如:选择不同的打包方式来激活不同的profile

<packaging>${packaging.type}</packaging>
<profiles>
    <!-- 若打包方式为 jar -->
    <profile>
        <id>jar</id>
        <properties>
            <packaging.type>jar</packaging.type>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-freemarker</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.5.9.RELEASE</version>
                <type>pom</type>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>2.2.4.RELEASE</version>
                    <configuration>
                        <executable>true</executable>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
    <!-- 若打包方式为 war -->
    <profile>
        <id>war</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <packaging.type>war</packaging.type>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-freemarker</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-log4j2</artifactId>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>2.2.4.RELEASE</version>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

pluginRepositories

repositories 类似。根据官方文档所描述:每个pluginRepository元素都指定 maven 可以在其中找到新插件的远程位置。

...

这里列举了一些常见的标签,但并不是全部。有兴趣的话可自行了解!


参考