maven pom.xml解析、命令说明、依赖传递、继承、聚合、properties、build、依赖范围、版本仲裁、profile

maven是当前Java项目中用到最多的依赖管理工具。最开始的项目比较小引入的依赖包也少所以可以通过手动加载jar包的方式来管理依赖包;但随着项目越来越复杂各种大小框架层出不穷,一个项目的开发往往依赖的jar包成百上千,此时再去手动依赖找到这些jar包并且找到它们相互依赖的版本无疑是一个巨大的工程,此时需要一个能够简单解决掉这些依赖关系的工具;maven就解决了这一类的难题,并且maven还可以做项目构建,比如一个spring boot项目打包成一个jar包给项目部署也带了极大的方便。

一、POM文件解析

下面的示例文件列举了大部分常用的pom.xml的标签,不一定每个项目都会用到所有的标签
三者关系:
my-test-member 是父工程/总工程(聚合角度)
my-test-member-apimy-test-member-biz 都是子工程/聚合工程(聚合角度)

my-test-member 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>org.my.test.member</groupId>
    <artifactId>my-test-member</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>    

    <!-- modules:配置工程聚合-->
    <modules>
        <!-- module:配置一个具体的聚合模块 -->
        <module>my-test-member-api</module>
        <module>my-test-member-biz</module>
    </modules>

    <!-- properties:定义属性值 -->
    <properties>
        <!-- 自定义属性mysql版本号,对于自定义属性来说名称自己写 -->
        <mysql.version>8.0.17</mysql.version>
        <swagger.version>2.8.0</swagger.version>
        <mybatis.plus.version>3.3.2</mybatis.plus.version>
        <spring.boot.version>2.3.5.RELEASE</spring.boot.version>
    </properties>

    <!-- 使用dependencyManagement标签配置对依赖的管理 -->
    <!-- 被管理的依赖并没有真正被引入到工程 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <!-- 使用properties中自定义的属性值mysql版本号,好处是如果有多处使用该版本号,直接修改properties中的mysql版本号属性值即可 -->
                <version>${mysql.version}</version>
            </dependency>
            
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger.version}</version>
            </dependency>

            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis.plus.version}</version>
            </dependency>

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>${spring.boot.version}</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

</project>

my-test-member-api 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">
    <parent>
        <artifactId>my-test-member</artifactId>
        <groupId>org.my.test.member</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.my.test.member.api</groupId>
    <artifactId>my-test-member-api</artifactId>

    <dependencies>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

    </dependencies>
</project>

my-test-member-biz pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- project:根标签,表示对当前工程进行配置、管理 -->
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <!-- parent:给当前工程配置父工程 -->
    <parent>
        <!-- 通过坐标信息定位父工程 -->
        <artifactId>my-test-member</artifactId>
        <groupId>org.my.test.member</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <!-- modelVersion:从maven2.x版本开始固定4.0.0,代表当前pom.xml所采用的标签结构 -->
    <modelVersion>4.0.0</modelVersion>

    <!-- 当前Maven工程的坐标信息,其他项目如果要依赖当前maven项目则使用以下的坐标信息 -->
    <!-- groupId:一般表示公司或组织开发的某个项目 -->
    <!-- artifactId:一般表示项目下的某个模块 -->
    <!-- version:当前模块的版本 -->
    <!-- 也是安装该jar包后在本地仓库中的路径 如:com\my\test\member\my-test-product-biz\0.0.1-SNAPSHOT\my-test-member-biz-0.0.1-SNAPSHOT.jar -->
    <!-- 如果配置了父工程(parent标签),如果groupId和父工程一样则可以不写groupId,如果version和父工程一样也可以不写version,但是artifactId必须要写 -->
    <groupId>com.my.test.member</groupId>
    <artifactId>my-test-member-biz</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <!-- 当前Maven工程的打包方式,可选值有下面四种:默认值jar -->
    <!-- jar:表示这个工程是一个Java工程,打包结果jar包,创建Java工程命令:mvn archetype:generate  -->
    <!-- war:表示这个工程是一个Web工程(传统的jsp工程),打包结果war包,创建Web工程命令:mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.4
    选择创建方式编号比如quickstart,会提示输入groupId、artifactId、version等,会生成web.xml、WEB-INF、index.jsp等文件或目录,如果没有java目录的话,需要自己在main目录下创建java目录;Web工程可依赖Java工程,也就是war包里面可以依赖jar包,不能jar包依赖war包 -->
    <!-- pom:表示这个工程是“管理其他工程”的工程,比如该工程是一个父级工程则定义为pom,创建命令:mvn archetype:generate但是packaging时jar需要改成pom -->
    <!-- maven-plugin:定义当前工程为maven插件即自定义maven插件的时候用到,但是基本不会有自定义插件的场景 -->
    <packaging>jar</packaging>

    <!-- name:当前工程的名称 -->
    <name>my-test-member-biz</name>
    <!-- url:maven官网地址 -->
    <url>http://maven.apache.org</url>
    <!-- description:当前工程的说明 -->
    <description>测试微服务用户服务</description>
    <!-- properties:在maven中定义属性值,可以是指定maven自带的属性值,也可以是自定义属性值;标签中间是属性值,标签本身是属性 -->
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <!-- dependencies:指定依赖信息 -->
    <dependencies>
        <!-- dependency:指定一个具体的依赖信息,通过maven的坐标信息指定 -->
        <dependency>
            <groupId>org.my.test.member.api</groupId>
            <artifactId>my-test-member-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <!-- scope:指定当前依赖的范围,默认值compile,总共以下6种可选值:compile/test/provided/system/runtime/import-->
            <!-- compile:默认值,compile范围的jar包,可以在开发时正常使用该jar包里面的类方法等(可以在main/test目录下的程序中正常使用),并且项目打包的时候会将该jar包打入到项目生成的jar或war包中,即该jar包会被main目录中的程序使用且不属于provided的情况如:spring-boot-starter -->
            <!-- test:test范围的jar包,开发时可以在test目录下的程序正常使用该jar包里面的类方法等,但是main目录下的程序不能正常使用,并且项目打包的时候不会将该jar包打入到项目生成的jar或war包中,即仅作测试用的jar包定义该范围如:junit -->
            <!-- provided:provided范围的jar包, 可以在开发时正常使用该jar包里面的类方法等(可以在main/test目录下的程序中正常使用),并且项目打包的时候不会将该jar包打入到项目生成的jar或war包中,
            一般对于服务器已提供的jar包,但是开发过程中又需要用到该jar包则定义为provided(因为服务器本身已有该jar包,所以不需要在打包时带入,以避免包臃肿和版本冲突),比如:servlet-api、jsp-api -->
            <!-- system:本文后面有介绍 -->
            <!-- runtime:本文后面有介绍 -->
            <!-- import:本文后面有介绍 -->
            <scope>test</scope>
            <!-- excludes标签配置依赖的排除	-->
            <exclusions>
                <!-- exclude标签中配置一个具体的依赖排除 -->
                <exclusion>
                    <!-- 指定要排除的依赖的坐标(不需要写version) -->
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>


        <dependency>
            <!-- 此处不写版本在于父工程my-test-member中dependencyManagement中定义了该包的版本信息,如果写上版本号则使用该pom中定义的版本,不写则使用父工程定义的版本 -->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <!-- configuration 标签:配置 spring-boot-maven-plugin 插件 -->
                <configuration>
                    <!-- 具体配置信息会因为插件不同、需求不同而有所差异 -->
                    <!-- source:要求编译器使用指定的jdk版本来兼容写的源代码 -->
                    <source>1.8</source>
                    <!-- target:源文件编译后,生成的 *.class 字节码文件要符合指定的 JVM 版本 -->
                    <target>1.8</target>
                    <!-- encoding:工程构建过程中读取源码时使用的字符集 -->
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

约定优于配置

maven可以做依赖管理与项目构建,其中的项目构建的方便得益于maven提供的约定优于配置,比如项目打包需要编译源文件、测试等等,那么需要知道源文件在哪、测试类在哪,这就体现了maven本身对于项目目录结构的一个约定
在这里插入图片描述
约定优于配置优于编码

二、maven常用命令

注意:使用 Maven 构建操作相关的命令时,必须进入到 pom.xml 所在的目录。如果没有在 pom.xml 所在的目录运行 Maven 的构建命令,则会报错(mvn -v这种和构建操作无关的命令则配置了PATH之后可以在任何目录执行):
在这里插入图片描述
如果一个项目中有多个pom.xml则在哪个pom.xml目录执行构建命令,就是对哪个工程的构建操作。

mvn clean 删除 target 目录,可以用作清除上一次构建记录,不会删除已安装在本地仓库中的jar包
mvn compile 主程序编译(对src/main目录代码进行编译),结果存放在target/classes目录
mvn test-compile 测试程序编译(对src/test目录代码进行编译),结果存放在target/test-classes
mvn test 测试项目,执行src/test目录下的测试方法(会输出测试结果比如运行多少个、失败多少、报错多少、跳过多少,如果失败会输出失败信息),测试报告存放在target/surefire-reports目录
mvn package 将项目打成jar/war包存放在 target 目录,不会在maven本地仓库中存入该jar包
mvn install 和mvn package具有相同的作用,但是会在maven本地仓库中存入/安装该jar包,并且会将 pom.xml 也安装在本地仓库中

maven命令组合,比如:
mvn clean install -Dmaven.test.skip=true 作用:清除上次构建记录并安装jar包,并且跳过测试环节
解释:
# -D 表示后面要附加命令的参数,字母 D 和后面的参数是紧挨着的,中间没有任何其它字符
# maven.test.skip=true 表示在执行命令的过程中跳过测试
# mvn clean install 是clean和install操作的组合,为什么是这种组合,这个和maven的生命周期有关

maven生命周期
生命周期的设定是为了让maven工程构建过程自动化完成,总共设定了三个生命周期,生命周期中的每一个环节对应构建过程中的一个操作;一个工程构建一般会包含编译、测试、打包、部署等等,如果没有生命周期的设定;我们想要完成工程的部署,需要将编译、测试、打包按照顺序分别执行,有了生命周期的设定之后,我们只需要执行我们想要的结果操作即可,要实现该结果前面需要进行的操作,按照生命周期执行即可,而不需要手动去执行。比如某个生命周期包含有1->5这五个操作,那么我们执行操作3的话maven会在执行3之前将该生命周期中1和2顺序执行。
在这里插入图片描述
注意:执行某个操作时maven会将该生命周期中,该操作之前的操作都执行,只是执行该生命周期前面的操作,不会执行其他生命周期的操作;比如:执行 install 命令,它属于Default这个生命周期的命令,那么会将Default中install前面的命令都按照顺序执行(也就是除了最后一步deploy,其他的操作都会按照顺序执行),正因为这个设定执行maven命令的时候,每个生命周期执行一个命令即可,如果想要执行其他生命周期的操作需要和另外生命周期中的命令进行组合,而mvn clean install这个组合命令中,install属于Default、clean属于Clean这两个命令不属于一个生命周期,所以想要他们都执行需要进行组合命令,而因为会执行命令所属生命周期的前面所有操作,所以没必要将生命周期中的每个命令都写上

maven插件
maven中构建工程相关的命令都是由maven插件最终执行,maven本身仅仅负责调度。使用方式如:
创建maven工程的命令:mvn archetype:generate 其中mvn是主命令、archetype:generate是子命令、archetype是插件、generate是目标。
一个插件可以对应多个目标,而每一个目标都和生命周期中的某一个环节对应。
比如Default 生命周期中有 compile 和 test-compile 两个和编译相关的环节,这两个环节对应 compile 和 test-compile 两个目标,而这两个目标都是由 maven-compiler-plugin-3.1.jar 插件来执行的。
在这里插入图片描述

三、依赖传递与排除依赖

maven进行依赖时是通过本地maven仓库进行的jar包导入,比如A依赖了B,如果B工程做了修改(不论是代码或pom的修改),需要先安装B工程到本地仓库,否则本地仓库还是上一次B的版本,即使A进行构建依赖的B还是未修改的;所以当被依赖工程B做了修改需要先安装到本地仓库,然后再构建A。

依赖传递

比如:A 依赖 B,B 依赖 C, A 没有配置对 C 的依赖的情况下,A 里面是否可以使用 C
分情况看:
B 依赖 C 时使用 compile 范围:可以传递
B 依赖 C 时使用 test 或 provided 范围:不能传递,所以A需要C的 jar 包时,就必须在A里面明确配置依赖C才可以。
换言之就是A里面想要使用C的话,需要A依赖的这个B的jar包里面有C的jar包,也就是需要B依赖C的时候使用compile范围,而不能是test 或 provided范围。

依赖排除

比如:A 依赖 B,B 依赖 C, C可以传递到A中,但是A不需要B依赖的C,比如A中已经有一个其他版本的C的依赖了,为了避免jar包冲突,所以A需要使用本身配置的C而不是B依赖的C,那么此时就需要在A依赖B时排除掉B依赖的C,即在A的pom.xml中依赖B时使用exclusions标签排除C。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <!-- excludes标签配置依赖的排除	-->
            <exclusions>
                <!-- exclude标签中配置一个具体的依赖排除 -->
                <exclusion>
                    <!-- 指定要排除的依赖的坐标(不需要写version) -->
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

四、继承

和Java的继承类似,Java继承子类可以拥有父类的变量及方法;在maven中的继承就发生在父工程和子工程中,子工程可以继承父工程的依赖以及使用父工程中定义的版本信息,一般用于在父工程中做版本统一管理,子工程使用父工程定义的版本信息,避免整个项目中相同jar包各个子工程版本五花八门,最终导致依赖冲突或意料之外的异常信息;所以一般情况下父工程中需要将所有子工程的依赖都进行版本规范,每个子工程依赖本工程需要的依赖包即可(如果子工程导入依赖时不写版本号则采用父工程定义的版本号,子工程写了版本号则使用子工程定义的版本号),也可以考虑将所有子工程都会用到的依赖,放在父工程中,这样所有子工程也会通过继承的方式拥有这个依赖(不推荐,还是在父工程中只定义版本管理);子工程中使用 parent 标签定义父工程;每个POM都有且仅有一个父POM(未定义parent的话就是默认的超级POM作为父POM),并且可以多级继承;比如A工程继承B工程,B工程继承C工程,那么A工程中可以用到C工程定义的 dependencyManagement
父工程:

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.my.test.member</groupId>
    <artifactId>my-test-member</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <!-- properties:定义属性值 -->
    <properties>
        <!-- 自定义属性mysql版本号,对于自定义属性来说名称自己写 -->
        <mysql.version>8.0.17</mysql.version>
    </properties>

    <!-- 使用dependencyManagement标签配置对依赖的管理 -->
    <!-- 被管理的依赖并没有真正被引入到工程 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <!-- 使用properties中自定义的属性值mysql版本号,好处是如果有多处使用该版本号,直接修改properties中的mysql版本号属性值即可 -->
                <version>${mysql.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

子工程:

<!-- parent:给当前工程配置父工程 -->
    <parent>
        <!-- 通过坐标信息定位父工程 -->
        <artifactId>my-test-member</artifactId>
        <groupId>org.my.test.member</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.my.test.member</groupId>
    <artifactId>my-test-member-biz</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    
    <dependencies>
        <dependency>
            <!-- 此处不写版本在于父工程my-test-member中dependencyManagement中定义了该包的版本信息,如果写上版本号则使用该pom中定义的版本,不写则使用父工程定义的版本 -->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

POM的四个层级:
超级POM、父POM、当前POM、有效POM
超级POM:类似于Java中的Object类,超级POM是所有POM的顶级父类、如果某个POM未定义父POM,那么它的父POM就是这个超级POM;超级POM定义了如源文件存放的目录、测试源文件存放的目录、构建输出的目录……等等。
父POM:在POM中通过 parent 标签指定的POM就是该POM的父POM。一个POM有且仅有一个父POM。
当前POM:当前工程的POM。
有效POM(effective POM):因为有继承关系及依赖传递的存在,所以我们当前工程的POM定义的内容,并不是当前工程所有的POM内容,POM会按照继承关系及依赖传递做叠加操作,最终得到的POM就是有效POM,也就是对当前工程依赖及相关设定最终产生效果的POM。
有效POM的叠加关系:在 POM 的继承关系中,子 POM 可以覆盖父 POM 中的配置;如果子 POM 没有覆盖,那么父 POM 中的配置将会被继承(也就是说如果在继承关系中有多个POM都对同一个内容进行了定义则离当前POM距离越近的配置生效,如果多个POM中只有一个POM对内容进行了定义则不管有多少级的继承关系,这个内容都会生效。比如当前工程A继承B继承C,ABC都对a依赖做了配置,那么A中对a依赖的配置生效,如果只有C中做了c依赖配置,那么C中对c依赖的配置会生效,如果B和C都对b依赖做了配置,那么B中对b依赖的配置会生效)。按照这个规则,继承关系中的所有 POM 叠加到一起,就得到了一个最终生效的 POM。
查看有效POM的命令:

mvn help:effective-pom

五、聚合

定义一个“总工程”将各个“模块工程”汇集起来,作为一个完整的项目;一般来说总工程也是父工程(继承角度)乃至于最顶级的父工程,子工程则是模块工程。好处是可以在总工程中执行maven命令,从而对总工程中定义的模块工程都执行该命令,并且会自动梳理依赖关系,进行执行命令。通过在总工程中使用 modules 标签定义模块工程即可。
总工程/父工程:

<!-- modules:配置工程聚合,聚合的好处是:我们在哪个工程下执行maven命令则是对该工程的操作,但是在总工程中执行则可以对所有modules定义的模块操作,比如执行mvn install就是安装哪个工程,如果这个工程有modules定义,则会在安装的时候自动梳理聚合模块工程之间的依赖关系,
    比如父子工程,依赖传递等,然后先去安装父工程和被依赖工程,再安装工程本身,而不需要我们自己手动按照顺序安装每一个工程,注意别发生循环依赖,比如A依赖B,B依赖C,然后C又依赖了A,这样会报错 -->
    <modules>
        <!-- module:配置一个具体的聚合模块 -->
        <module>my-test-member-api</module>
        <module>my-test-member-biz</module>
    </modules>

循环依赖报错如下:

DANGER

[ERROR] [ERROR] The projects in the reactor contain a cyclic reference:

六、properties 属性声明及引用

使用 properties 标签可以定义系统自带的属性值以及自定义属性及属性值,方式:

    <properties>
        <!-- 自定义属性mysql版本号,对于自定义属性来说名称自己写 -->
        <mysql.version>8.0.17</mysql.version>
    </properties>

主要用途:
1、在当前 pom.xml 文件中引用属性,比如依赖版本定义引用 properties 中的属性,达到一处修改,全部版本都替换的效果。
2、资源过滤功能:在非 Maven 配置文件中引用属性,由 Maven 在处理资源时将引用属性的表达式替换为属性值(下方 十、profile 标签 中有讲解)

查看属性值:根据输入表达式,输出表达式结果:

mvn help:evaluate

执行该命令后,会要求输入表达式,然后输出表达式的结果。
1、比如输出刚刚定义的 mysql.version 的表达式就是

${属性名}   如:${mysql.version}

2、访问系统属性:比如Java系统属性也就是通过 System.getProperties() 方法可以拿到的属性,表达式就是:

${属性名}   如:${java.runtime.name}

3、访问系统环境变量:比如JAVA_HOME,表达式就是:

${env.系统环境变量名}   如:${env.JAVA_HOME}

4、访问 project 属性,也就是访问当前POM中的元素值,表达式就是:

${project.元素名}
如:访问当前POM的一级标签表达式:${project.标签名},如:${project.artifactId} 输出当前POM的artifactId的值
访问当前POM的子级标签表达式:${project.标签名.子标签名},如:${project.parent.groupId} 输出当前POM的父POM的groupId的值
访问列表标签表达式:${project.标签名[下标]},如:${project.modules[0]} 输出当前POM的modules标签下的第一个值,${project.build.plugins[0]} 输出当前POM的build标签下的plugins标签下的第一个值

5、访问 settings 全局配置,比如maven settings.xml配置文件中的属性,表达式就是:

${settings.标签名}    如:${settings.localRepository} 输出settings.xml配置的本地仓库的路径

在这里插入图片描述

七、build 标签

build意为构建,意思就是在工程构建过程中的定制化操作,因为超级POM是每个POM的直接或间接父POM,所以每个工程的有效POM实际上其中的 build 都包含了超级POM中定义的 build 标签内容;当有效POM中的 build 标签内容(因为继承关系包含所有父POM的build内容)不能满足当前工程的构建的时候,那么我们就会在当前工程中定义 build 标签内容,所以 build 标签不是一定会存在当前工程中的。

build标签大致包含三类子标签:
1、定义约定的目录结构
如:

<build>
      <sourceDirectory>E:\test\my-test-product\my-test-product-biz\src\main\java</sourceDirectory>
      <scriptSourceDirectory>E:\test\my-test-product\my-test-product-biz\src\main\scripts</scriptSourceDirectory>
      <testSourceDirectory>E:\test\my-test-product\my-test-product-biz\src\test\java</testSourceDirectory>
      <outputDirectory>E:\test\my-test-product\my-test-product-biz\target\classes</outputDirectory>
      <testOutputDirectory>E:\test\my-test-product\my-test-product-biz\target\test-classes</testOutputDirectory>
</build>

2、备用插件管理
通过 pluginManagement 标签在父POM中来管理插件版本和 dependencyManagement 在父POM中定义依赖版本意思一样,子工程使用父工程中定义的插件时可以省略版本号,起到在父工程中统一管理插件版本的效果。
如:
父工程(在build标签中使用pluginManagement标签管理插件版本):

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>2.3.5.RELEASE</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

子工程(在build标签中使用plugins导入父工程管理的插件时,可以不写版本号,使用父工程定义的版本号,和继承一样,子工程可覆盖父工程,即采用的版本是距离当前工程最近的工程中定义的版本):

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

spring-boot-maven-plugin 插件作用:
比如spring boot项目使用maven本身install的插件只会将工程代码本身进行安装生成jar包,但我们想要java -jar可以直接运行的jar包,除了工程代码本身,还需要依赖的jar包、Servlet容器(如:Tomcat)、
通过 java -jar 方式可以直接启动jar包相关的配置等;spring-boot-maven-plugin插件就可以实现上述功能。
3、生命周期插件
如:

    <build>
      <plugins>
        <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
          <version>2.5.0</version>
          <executions>
            <execution>
              <id>repackage</id>
              <goals>
                <goal>repackage</goal>
              </goals>
              <configuration>
                <mainClass>${start-class}</mainClass>
              </configuration>
            </execution>
          </executions>
          <configuration>
            <mainClass>${start-class}</mainClass>
          </configuration>
        </plugin>
      <plugins>
    </build>

解释:
坐标:groupId、artifactId、version 标签定义了插件的坐标,如果是 Maven 的自带插件会省略 groupId
executions 标签内可以配置多个 execution 标签,execution 标签内含义:
id:指定唯一标识
phase:关联的生命周期阶段
goals/goal:关联指定生命周期的目标,goals 标签中可以配置多个 goal 标签,表示一个生命周期环节可以对应当前插件的多个目标。
configuration:对插件目标的执行过程进行配置,其中的标签需要是插件本身定义的,如上面的 spring-boot-maven-plugin 插件中的 repackage 目标,配置 mainClass 标签,这个 mainClass 就是 spring-boot-maven-plugin 插件中的类中的属性,意思是指定spring boot项目的启动类。
4、其他,如:

    <build>
        <!-- 当前工程在构建过程中使用的最终名称 -->
        <finalName>test-xxx</finalName>
    </build>

build 标签典型应用:
除了上面提到的使用 spring-boot-maven-plugin 插件,用于生成 java -jar可直接运行的 jar 包外,另外就是用于指定jdk版本也是常用的功能:
为什么需要指定jdk版本:不指定jdk版本的情况下项目一般默认识别的jdk版本是1.5,那么这个时候如果我们在项目中的代码,使用了jdk5以上版本的语法,比如jdk8的 Lambda 表达式 那么idea编辑器会提示这段代码报错,并且此时编译或打包也会报错。那么如果指定 jdk版本 的话一般可以在三个地方指定:
a、maven的 settings.xml 配置文件中的 profile 标签指定:

<!-- 配置Maven工程的默认JDK版本 -->
<profile>
  <!-- id:唯一标识 -->
  <id>jdk-1.8</id>
  <!-- activation:激活方式 -->
  <activation>
    <!-- activeByDefault:是否默认激活-->
	<activeByDefault>true</activeByDefault>
	<!-- jdk:标识当前 profile 可以根据 jdk 版本激活,比如这里如果环境是 jdk 1.8 的话就激活当前 profile 配置,不是则不激活,激活了当前 profile 之后,如果当前 profile 还是默认激活的话,则采用当前 profile -->
	<jdk>1.8</jdk>
  </activation>
  <!-- properties:当前 profile 激活并且使用后要应用的配置 -->
  <properties>
	<maven.compiler.source>1.8</maven.compiler.source>
	<maven.compiler.target>1.8</maven.compiler.target>
	<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
  </properties>
</profile>

b、当前工程的 pom.xml 中 build 标签指定:

<!-- build 标签:指定maven构建工程的定制化操作! -->
<build>
    <!-- plugins 标签:指定需要用到的插件! -->
    <plugins>
        <!-- plugin 标签:指定一个具体的插件 -->
        <plugin>
            <!-- 插件的坐标。此处引用的 maven-compiler-plugin 插件不是第三方的,是一个 Maven 自带的插件。 -->
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            
            <!-- configuration 标签:配置 maven-compiler-plugin 插件 -->
            <configuration>
                <!-- 具体配置信息会因为插件不同、需求不同而有所差异 -->
                <!-- source:要求编译器使用指定的jdk版本来兼容写的源代码 -->
                <source>1.8</source>
                <!-- target:源文件编译后,生成的 *.class 字节码文件要符合指定的 JVM 版本 -->
                <target>1.8</target>
                <!-- encoding:工程构建过程中读取源码时使用的字符集 -->
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

c、当前工程的 pom.xml 中 properties 标签指定:

<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

三种方式区别:
a方式settings.xml 中配置:仅在本地生效,如果脱离当前 settings.xml 能够覆盖的范围,则无法生效,这样就可能会导致我们本地的maven settings.xml文件中做了相关设置,但是同事的本地maven仓库却没有做该设置,那么我们本地可以编译、打包该项目,同事拉取代码之后却无法完成编译、打包操作;但是因为 settings.xml 是对整个maven的配置,所以对于本地的多个项目来说都能够生效。
b、c方式在当前 Maven 工程 pom.xml 中配置:无论在哪个环境执行编译等构建操作都有效,但仅仅对当前工程生效。

八、scope 依赖范围

在 dependencies 中导入依赖的时候,每个依赖可以定义 scope 即依赖范围。
默认值compile,总共有6种可选值:compile/test/provided/system/runtime/import

compile:默认值,compile范围的jar包,可以在开发时正常使用该jar包里面的类方法等(可以在main/test目录下的程序中正常使用),并且项目打包的时候会将该jar包打入到项目生成的jar或war包中,即该jar包会被main目录中的程序使用且不属于provided的情况如:spring-boot-starter

test:test范围的jar包,开发时可以在test目录下的程序正常使用该jar包里面的类方法等,但是main目录下的程序不能正常使用,并且项目打包的时候不会将该jar包打入到项目生成的jar或war包中,即仅作测试用的jar包定义该范围如:junit

provided:provided范围的jar包, 可以在开发时正常使用该jar包里面的类方法等(可以在main/test目录下的程序中正常使用),并且项目打包的时候不会将该jar包打入到项目生成的jar或war包中,一般对于服务器已提供的jar包,但是开发过程中又需要用到该jar包则定义为provided(因为服务器本身已有该jar包,所以不需要在打包时带入,以避免包臃肿和版本冲突),比如:servlet-api、jsp-api

system:比如 Windows 系统环境下开发,如果现在想要将 D:\tempare\test-aaa-1.0-SNAPSHOT.jar 引入到项目中,那么就可以将依赖配置为 system 范围(缺点:因为引入的依赖是本地路径完全不具有可移植性,基本不使用):
<dependency>
    <groupId>org.my.test</groupId>
    <artifactId>test-aaa</artifactId>
    <version>1.0-SNAPSHOT</version>
    <systemPath>D:\tempare\test-aaa-1.0-SNAPSHOT.jar</systemPath>
    <scope>system</scope>
</dependency>

runtime:用于编译时不需要,但是运行时需要的 jar 包。比如:编译时根据接口调用方法,但是实际运行时需要的是接口的实现类。如:
<!--热部署 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

import:一般通过继承父工程来管理依赖,但是 Maven 是单继承的;如果不同体系的依赖信息封装在不同 POM 中,此时想要将他们都导入进来就可以使用 import 依赖范围(要求:打包类型必须是 pom、必须放在 dependencyManagement 中)。如:
<dependencyManagement>
    <dependencies>
        <!-- SpringCloud 依赖导入 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR9</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

可选依赖:
在上面的 runtime 依赖范围的例子中导入 spring-boot-devtools 包的时候,有一个 optional 标签;该标签就是配置可选依赖,意为可有可无;官方解释为:比如Project X 依赖 Project A,A 中一部分 X 用不到的代码依赖了 B,那么对 X 来说 B 就是『可有可无』的。简单来说就是 标记了 optional 标签为 true 的依赖就是这个包里面的功能很少会用到,所以不要这个依赖不影响工程正常功能,如果使用到了这个包里面的功能就需要导入这个依赖。

九、版本仲裁

出现版本仲裁的情况一定是工程依赖了一个jar包的不同版本,如果本来就是不同的依赖包则是做叠加操作,也就是在最终生成的有效POM中这些依赖包都存在。
如果是通过继承方式得到的依赖包或者依赖版本管理是子工程覆盖父工程也就是继承关系中距离当前工程越近的声明生效。
如果是依赖传递的方式,则采用两种方式仲裁:
a、最短路径优先
比如A工程依赖B工程依赖C工程依赖了 x-1.0 jar包、A工程依赖D工程依赖了 x-2.0 jar包,则在A工程中生效的 x jar包是D工程中依赖的 x-2.0 jar包。
b、路径相同时先声明者优先
比如A工程依赖B工程依赖了 y-1.0 jar包、A工程依赖D工程依赖了 y-2.0 jar包,此时对于A工程来说,想要依赖 y jar包,两个版本的路径长度一样,那么对于依赖哪个 y jar包就是由A工程中的 pom.xml 中先声明的B工程依赖还是D工程依赖决定。先声明依赖B工程则是采用 y-1.0 jar包,先声明依赖D工程则是采用y-2.0 jar包。
maven版本仲裁只是maven的一个依赖冲突的自动解决方案(在没有人为干预的情况下按照以上规则进行仲裁),当通过maven本身的版本仲裁之后,项目功能没问题可以正常运行,不需要多做关心;但是有些情况下通过版本仲裁得到的依赖会不满足我们的使用要求,比如版本仲裁得到 1.0 版本,但是我们使用的该 jar包 中的某个方法,1.0 版本没有(或者1.0版本的逻辑不满足要求)这个方法在仲裁失败的 2.0 版本中并且满足要求,那么此时就只有人为干预让 maven 依赖 2.0 版本;比如在当前工程中明确指定依赖的版本信息,或者通过 exclusion 标签,排除 1.0 版本的依赖。

十、profile 标签

在 Maven 中,使用 profile 机制来管理不同环境下的配置信息。并且解决同类问题的类似机制在其他框架中也有;比如生产环境、测试环境、开发环境的数据库缓存连接等而且从模块划分的角度来说,持久化层的信息放在构建工具中配置也违反了『高内聚,低耦合』的原则。

默认 profile
在 pom.xml 中不在 profile 标签内的配置,就是配置的默认 profile 。因为根标签 project 下所有标签相当于都是在设定默认的 profile。project 标签下除了 modelVersion 和坐标标签之外,其它标签都可以配置到 profile 中。

使用顺序
a、为每个环境声明一个 profile
环境 A:profile A
环境 B:profile B
环境 C:profile C
b、激活某个环境的 profile

profile 配置地方
a、maven的 settings.xml 配置文件中的 profile 标签:对本地工程都生效。比如上面配置 JDK 1.8。
b、pom.xml:仅对当前工程生效。

profile 标签内容
a、profiles/profile 标签
profile 代表某个配置并且配置可以有多个,由 profiles 标签统一管理;所以 profile 配置在 profiles 标签中。
profile 标签中的配置会覆盖 pom.xml 中的同级配置,所以 profiles 标签通常是 pom.xml 中的最后一个标签。
b、id 标签
每个 profile 都必须有一个 id 标签,指定该 profile 的唯一标识。这个 id 标签的值会在命令行调用 profile 时被用到。命令格式(作为命令的参数被使用):-P<profile的id>,如:mvn compile -PjdkTest,其中 jdkTest 就是某个 profile 的 id 标签的值;意思是在编译的时候使用 profile id为 jdkTest 的配置进行编译。
c、其他标签
除了上面a b标签是 profile 固定的之外,其他标签是 project 跟标签下除了 modelVersion 和坐标标签,都可以配置到 profile 中;比如一个 profile 可以覆盖项目的最终名称、项目依赖、插件配置等各个方面以影响构建行为。

profile 激活
a、当 POM 中没有在 profile 标签里的配置就是默认的 profile,默认被激活。
b、基于环境信息激活,如:JDK 版本、操作系统参数、文件、属性等等。一个 profile 一旦被激活,那么它定义的所有配置都会覆盖原来 POM 中对应层次的元素。比如:

<profiles>
	<profile>
    	<id>JDK1.8</id>
    	<!-- activation:配置激活条件 -->
        <activation>
            <!-- 配置是否默认激活 -->
    	    <activeByDefault>false</activeByDefault>
            <!-- 指定激活条件为:JDK 1.8 -->
        	<jdk>1.8</jdk>
        	<!-- property:指定激活属性 -->
        	<property>
        	    <!-- name:激活属性名称 -->
        	    <name>mavenVersion</name>
        	    <!-- value:激活属性值 -->
                <value>3.5.4</value>
            </property>
        </activation>
    </profile>
</profiles>

表示当 JDK 版本为 1.8 且 maven 版本为 3.5.4 时被激活(Maven 3.2.2 及之后的版本)
上面的例子有两个激活条件则(多个激活条件也一样):
Maven 3.2.2 之前:遇到第一个满足的条件即可激活即或的关系。
Maven 3.2.2 开始:各条件均需满足即且的关系。
c、命令行激活
列出所有激活的 profile,以及它们在哪里定义

mvn help:active-profiles

执行maven指令时指定某个具体的 profile

mvn clean test -P<profile id> 如:mvn clean test -PjdkTest 指定清理测试时使用id为jdkTest的profile

资源属性过滤
Maven 能够通过 profile 实现各不同运行环境切换,因为 Maven 提供了『资源属性过滤』的机制。通过属性替换实现不同环境使用不同的参数。
我们可以在当前工程的 pom.xml 文件中,定义每个环境的 profile 配置,然后maven构建工程时,指定使用某个环境的profile即可,比如定义数据库连接信息,可以这样定义:
第一步:在 pom.xml 中通过 profile 定义数据库连接信息:

<profiles>
    <profile>
        <id>devJDBC</id>
        <properties>
            <dev.jdbc.user>root</dev.jdbc.user>
            <dev.jdbc.password>test123</dev.jdbc.password>
            <dev.jdbc.url>http://localhost:3306/test</dev.jdbc.url>
            <dev.jdbc.driver>com.mysql.jdbc.Driver</dev.jdbc.driver>
        </properties>
        <build>
            <resources>
                <resource>
                    <!-- 表示为这里指定的目录开启资源过滤功能 -->
                    <directory>src/main/resources</directory>
                    <!-- 开启资源过滤功能 -->
                    <filtering>true</filtering>
                    <!-- includes和excludes可以不配置 -->
                    <includes>
                        <include>*.properties</include>
                    </includes>
                    <excludes>
                        <exclude>test.properties</exclude>
                    </excludes>                  
                </resource>
            </resources>
        </build>               
    </profile>
</profiles>

第二步:创建待处理的资源文件;如:application.properties(在上一步中定义的开启资源过滤功能目录是src/main/resources,所以需要将application.properties文件,创建在该目录下)

dev.user=${dev.jdbc.user}
dev.password=${dev.jdbc.password}
dev.url=${dev.jdbc.url}
dev.driver=${dev.jdbc.driver}

第三步:执行 maven 构建工程的命令并指定使用对应环境的 profile

mvn clean install -PdevJDBC

执行清理安装命令时 会将 id 为 devJDBC 的profile配置,做资源过滤处理,效果就是会将 devJDBC 中配置的user、password、url、driver等信息,写入到 application.properties 文件中的对应属性上。application.properties 中 ${dev.jdbc.user} 对应 devJDBC 中的 <dev.jdbc.user>root</dev.jdbc.user> 也就是将 root 赋值给 dev.user属性。

includes、excludes
有时会在 resource 标签下配置 includes 和 excludes 标签;作用是:
includes:指定执行 resource 阶段时要包含到目标位置的资源
excludes:指定执行 resource 阶段时要排除的资源
比如上面的 devJDBC profile的例子,其中的 includes 指的是在 resource 阶段,需要将当前工程 resources 目录下的所有 properties 文件包含到目标位置,excludes 指的是这些 properties 文件中排除test.properties 文件。

Nexus下载安装及对接
maven jar包冲突解决方案、导入非maven工程jar包

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值