项目管理工具:Maven(中)

POM文件管理

pom.xml文件叫做项目对象模型(POM,Project Object Model)是Maven项目核心配置文件。

坐标

Maven 世界中存在着数十万甚至数百万构件,Maven 坐标包括 groupIdartifactIdversionpackaging 等元素,通过坐标在仓库中找到项目所需的Jar包。

  • groupId标签: 项目组 ID,定义当前 Maven 项目隶属的组织或公司,通常是唯一的。它的取值一般是项目所属公司或组织的网址或 URL 的倒写,例如 www.baidu.com,倒写后:com.baidu.www。
  • artifactId标签: 项目的唯一的标识符,通常是项目的名称。
  • version标签:版本号。
  • packaging标签:项目的打包方式,比如 jar包,war包、pom等,默认值为 jar

其中groupId标签、artifactId标签 和 version标签三个必须定义的元素,组成坐标唯一标识依赖。示例代码如下:

<?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.study.test</groupId>
    <artifactId>helloMaven</artifactId>
    <version>1.0-SNAPSHOT</version>
</project>

SNAPSHOT(快照)

团队项目开发中,可能会因为不同项目组之间版本不同导致错误,MavenSNAPSHOT(快照)是一种特殊的版本,它表示当前开发进度的副本。与常规版本不同,快照版本的构件在发布时,Maven 会自动为它打上一个时间戳,有了这个时间戳后,当依赖该构件的项目进行构建时,Maven 就能从仓库中找到最新的 SNAPSHOT 版本文件。

Maven 仓库分为两种,Snapshot 快照仓库和 Release 发行仓库。

Snapshot 快照仓库

<version>1.0-SNAPSHOT</version>

Release 发行仓库

<!--<version>1.0</version>-->
<version>1.0-RELEASE</version>

两者最大的区别是:每次重新构建项目的时候SNAPSHOT(快照)版本,自动从远程仓库上下载最新的快照版本;Release (发行)版本在不更改版本号的前提下,直接编译打包时,如果本地仓库已经存在该版本的模块,则 Maven 不会主动去远程仓库下载。

如果你不想让SNAPSHOT(快照)版本频繁的去远程仓库更新,可以在 Maven 的配置文件settings.xml中有一个 repository标签。频率共有四种,分别是 always、daily、interval、never,默认值是daily

  • always:每次都去远程仓库查看是否有更新。
  • daily:每天第一次的时候查看是否有更新。
  • intervalinterval:*设置一个分钟为单位的间隔时间,在这个间隔时间内只会去远程仓库中查找一次。
  • never:不会去远程仓库中查找。
    <profile>
      <repositories>
        <repository>
          <id>jdk14</id>
          <name>Repository for JDK 1.4 builds</name>
          <url>http://www.myhost.com/maven/jdk14</url>
          <layout>default</layout>
          <snapshotPolicy>always</snapshotPolicy>
        </repository>
      </repositories>
    </profile>

聚合和继承

在实际的开发过程中,我们所接触的项目一般都由多个模块组成。无论是聚合还是继承,它们都应属于一个项目中不同模块的关系。项目结构,如图
在这里插入图片描述

  • 聚合:将项目多个模块聚合在一起,这样能够更加方便项目的管理。

示例代码如下:

父模块 helloMaven,示例代码如下:

<?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.study.test</groupId>
    <artifactId>helloMaven</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>helloMaven-common</module>
        <module>helloMaven-server</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

使用<modules>标签进行项目聚合,不过需要注意父项目的<packaging>类型一定是pom,表明这是个聚合工程用于Jar包管理。

我们执行mvn install命令,可以看到对子项目的打包,如图:

在这里插入图片描述

  • 继承:定义了聚合以后,每个子项目就继承自父项目,继承的好处就是对依赖的统一管理,如果相同的依赖在每个模块中都写一遍,更换版本时,有项目版本未及时更改导致错误,这样操作非常繁琐。子项目也可通过版本覆盖的方式,定义指定版本,更加灵活。

子模块 helloMaven-server,示例代码如下:

<?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>helloMaven</artifactId>
        <groupId>com.study.test</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>helloMaven-server</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>

子模块 helloMaven-common,示例代码如下:

<?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>helloMaven</artifactId>
        <groupId>com.study.test</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>helloMaven-common</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>

在这里插入图片描述

我们可以看到子模块中,通过<parent>标签继承父模块,子模块可以访问父模块的依赖,可以不用定义<groupId>标签和 <version>标签,其中<relativePath>标签定义了父项目的pom文件位置,如果找不到再去本地仓库查找,如果子模块在父模块的文件夹中可以被省略。

除了<groupId>标签和 <version>标签可以被继承外,还有很多标签也可以被继承,简单列举一些,如下:

  • <groupId>:项目组ID,项目坐标的核心元素。
  • <version>:项目版本,项目坐标的核心元素。
  • <description>:项目的描述信息。
  • <organization>:项目的组织信息。
  • <properties>:自定义的Maven属性。
  • <dependencies>:项目的依赖配置。
  • <dependencyManagement>:项目的依赖管理配置。
  • <repositories>:项目的仓库配置。
  • <build>:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等。
  • <reporting>:包括项目的报告输出目录配置、报告插件配置等。

Maven 的继承和聚合的目的不同,继承的目的是为了消除 POM 中的重复配置,聚合的目的是为了方便快速的构建项目。

反应堆

听名字感觉很牛逼,其实就是指项目中所有模块构建的顺序。对于单模块的单体项目,反应堆就是它本身,而对于多模块的项目,反应堆就包含了各模块之间的继承与依赖关系,从而能够自动计算出合理的模块构建顺序。

示例代码如下:

<?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.study.test</groupId>
    <artifactId>helloMaven</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>helloMaven-common</module>
        <module>helloMaven-server</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

在这里插入图片描述
再没有模块依赖的情况下,是按照预期执行的,但是实际开发中,会有很多各种依赖。Maven会考虑模块之间的继承和依赖关系。如果某个模块中包含其它模块依赖,会去执行依赖模块的打包,直至依赖模块中没有依赖其它模块,再去执行原来模块的打包工作,周而复始(这很像套娃)。

子模块 helloMaven-common依赖helloMaven-server模块,示例代码如下:

<?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>helloMaven</artifactId>
        <groupId>com.study.test</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>helloMaven-common</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.study.test</groupId>
            <artifactId>helloMaven-server</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

子模块 helloMaven-server,示例代码如下:

<?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>helloMaven</artifactId>
        <groupId>com.study.test</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>helloMaven-server</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>

在这里插入图片描述
对比上一张图,你会发现构建顺序的明显区别。注意,如果两个模块之间相互依赖,这样会报错:

子模块 helloMaven-common,示例代码如下:

<?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>helloMaven</artifactId>
        <groupId>com.study.test</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>helloMaven-common</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.study.test</groupId>
            <artifactId>helloMaven-server</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

子模块 helloMaven-server,示例代码如下:

<?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>helloMaven</artifactId>
        <groupId>com.study.test</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>helloMaven-server</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.study.test</groupId>
            <artifactId>helloMaven-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

在这里插入图片描述
执行mvn install命令,错误信息如上图所示。

Maven可以通过命令去裁剪反应堆,构建单个模块,简单举例一些命令,有兴趣自行了解:

使用 -pl命令指定构建某几个模块

mvn clean install -pl moduleA,moduleB,…

使用 -rf命令在完整的反应堆构建顺序基础上指定从哪个模块开始构建

mvn clean install -rf moduleA

依赖(dependency)

Maven 坐标是依赖的前提, Maven 构建所产生的构件(例如 Jar 文件)被其他项目引用,那么该构件就是其他项目的依赖。

示例代码如下:

<?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.study.test</groupId>
    <artifactId>helloMaven</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <type>jar</type>
            <scope>compile</scope>
     	<optional>true</optional>
     	<!--主要用于排除传递性依赖-->
     	<exclusions>
         	<exclusion>
           	<groupId></groupId>
          	<artifactId></artifactId>
       	</exclusion>
     	</exclusions>
        </dependency>
    </dependencies>

</project>

dependencies元素可以包含一个或者多个 dependency 子元素,用以声明一个或者多个项目依赖。每个依赖可以包含的元素有:

  • grounpIdartifactIdversion:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到需要的依赖。
  • type:依赖的类型,对于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值为jar
  • scope:依赖的范围。
  • optional:标记依赖是否可选。
  • exclusions:用来排除传递性依赖。

绝大部分依赖的 Maven 坐标都能在 中央仓库 中获取,比如搜索spring的依赖,如图所示:
在这里插入图片描述

依赖范围

Maven 在对项目进行编译、测试和运行时,会分别使用三套不同的 classpath。通过pom.xml的依赖声明使用 <scope> 元素来控制依赖与三种 classpath(编译 classpath、测试 classpath、运行 classpath )之间的关系,这就是依赖范围。

Maven 具有以下 6 中常见的依赖范围:

  • compile:默认依赖范围。此依赖范围对于编译、测试、运行三种classpath都会被引入。以junit为例,示例代码如下:
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

在这里插入图片描述
可以发现除了测试以外也是可以正常使用的。

  • test:测试依赖范围。只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此依赖。比如:Junit,示例代码如下:
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

在这里插入图片描述
可以看到,此范围只对测试有效。

  • provided:已提供依赖范围。只对编译 classpath 和测试 classpath 有效。例如,servlet-api 依赖对于编译、测试阶段而言是需要的,但是运行阶段,由于tomcat 容器已经提供,故不需要 Maven 重复引入该依赖。由于Junit本身属于测试classpath,我们加入fastJson来进行测试,示例代码如下:
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.11</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

在这里插入图片描述
当设置为provided范围时,运行时使用fastJson会报异常,只有测试时候正常。

  • runtime:运行时依赖范围。只对测试 classpath、运行 classpath 有效。例如,JDBC 驱动实现依赖,其在编译时只需 JDK 提供的 JDBC 接口即可,只有测试、运行阶段才需要实现了 JDBC 接口的驱动。以Junit为例,实例代码如下:
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

在这里插入图片描述
可以看到编译时是过不去的,测试是可以的,可能会好奇编译都过不去,如何运行呢?采用反射的方式可以在程序运行时使用该类。比如jdbc的链接,示例代码如下:

    public static void main(String[] args) {
        Class clazz=Class.forName("com.mysql.jdbc.Driver");
        Driver driver=(Driver)clazz.newInstance();
        String url="jdbc:myslq://local:3306/database";
        Properties info=new Properties();
        info.setProperty("user","root");
        info.setProperty("password","****");
        Connection con=driver.connect(url,info);
    }

看到网上回答说是防止程序员直接使用依赖代码到项目,强制使用抽象接口。

  • system:系统依赖范围。其用于添加非 Maven 仓库的本地依赖,通过依赖元素 dependency 中的 systemPath 元素指定本地依赖的路径,不依赖Maven仓库,解析与本机系统绑定,可能造成构件的不可移植,因此谨慎使用。以fastJson为例,引入相对应依赖,示例代码如下:
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.11</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/fastjson-2.0.11.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.11</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/fastjson2-2.0.11.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson2-extension</artifactId>
            <version>2.0.11</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/fastjson2-extension-2.0.11.jar</systemPath>
        </dependency>
    </dependencies>

在这里插入图片描述
网上所说,与provided依赖范围一致,也只不过是不参与打包,仅此而已。但是编译、测试、运行其实都是可以的,以下是官网原话:

This scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository.
除了必须提供显式包含它的JAR之外,此作用域与提供的作用域类似。工件总是可用的,不需要在存储库中查找。

  • import:导入依赖范围。该依赖范围只能与 dependencyManagement 元素配合使用,其功能是将目标 pom.xml 文件中 dependencyManagement 的配置导入合并到当前 pom.xml 的 dependencyManagement 中。该依赖范围不会对三种classpath产生实际的影响,了解即可。

比如有两个项目,helloMaven项目,示例代码如下:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>2.0.11</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

helloMaven2项目,示例代码如下:

    <!--将父项目的dependencyManagement拿到本POM中,不再继承parent 依赖声明-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.study.test</groupId>
                <artifactId>helloMaven</artifactId>
                <version>1.0-SNAPSHOT</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <!-- 从继承的父项目中继承依赖版本 -->
        </dependency>
    </dependencies>

在这里插入图片描述

看过网上很多代码示例,感觉它们写都不用思考的,dependencyManagement里配置并不会实际引入,只是为了版本管理,实际引入需要直接在dependencies中添加,dependencies 元素中声明的依赖只配置了 groupIdartifactId,省略了 versionscope

依赖范围与三种 classpath关系表,如下:

依赖范围编译 classpath测试 classpath运行 classpath是否参与打包例子
compilelog4j
test--junit
provided-servlet-api
runtime-JDBC-driver
system-非 Maven 仓库的本地依赖
import---其它pom文件中的依赖管理

依赖传递

假设:项目 A 依赖于项目 B,B 又依赖于项目 C,此时 B 是 A 的直接依赖,C 是 A 的间接依赖,间接依赖 C 以传递性依赖的形式引入到项目 A 中。

在这里插入图片描述
在默认情况下项目编译时,Maven会把直接引用和间接引用的Jar包都下载到本地仓库中。

helloMaven项目,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

helloMaven2项目,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>com.study</groupId>
            <artifactId>helloMaven</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

下面介绍主要的四种依赖范围对传递依赖的影响

  • compile:当helloMaven项目的依赖范围为compile时,如图:

在这里插入图片描述

红框所示,因为compile的依赖范围都可以用,所以helloMaven2项目依赖传递对应的间接依赖也会包含且默认依赖范围也是compile

  • test:当helloMaven项目的依赖范围为test时,如图:

在这里插入图片描述
红框所示,因为test的依赖范围只有测试时可以用,所以helloMaven2项目依赖传递对应的间接依赖并没有。

  • provided:当helloMaven项目的依赖范围为provided时,如图:

在这里插入图片描述
红框所示,因为provided的依赖范围运行时被丢弃,所以helloMaven2项目对应的间接依赖并没有。

  • runtime:当helloMaven项目的依赖范围为runtime时,如图:

在这里插入图片描述
红框所示,因为runtime的依赖范围在测试或运行时使用,所以helloMaven2项目依赖传递对应的间接依赖也会包含且默认依赖范围也是runtime

假设,以helloMaven项目为直接依赖(纵向),helloMaven2项目为间接依赖(横向),直接依赖和间接依赖的依赖传递关系表,如下:

依赖范围complietestprovidedruntime
compliecomplietestprovidedruntime
test----
provided----
runtimeruntimetestprovidedruntime

通过关系表可以简单清晰的了解传递范围对传递依赖的关系,默认的依赖范围以直接依赖为主,否则参考关系表,比如:第一个项目的依赖范围定义为runtime,第二个项目的间接依赖的依赖范围定义为test,那么这个依赖范围在第二个项目的中属于test

可选依赖

当项目中不需要某个依赖时,通过配置可选依赖进行排除,可选依赖不会被传递。

前面讲解的依赖传递(optional)默认可选依赖为false,如果不想将依赖进行传递,可设为true

helloMaven项目,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>
    </dependencies>

helloMaven2项目,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>com.study.test</groupId>
            <artifactId>helloMaven</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

在这里插入图片描述
红框所示,helloMaven项目中的junit没有传递到helloMaven2项目中。

排除依赖

或许可选依赖并不能满足多个项目的灵活性,我们可以通过排除依赖的方式,对指定项目的某个具体的Jar包进行依赖排除。

helloMaven项目,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

helloMaven2项目,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>com.study.test</groupId>
            <artifactId>helloMaven</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

在这里插入图片描述
红框所示,helloMaven项目中的junit没有传递到helloMaven2项目中,这样给项目带来更多的灵活性。

我们可以清晰的知道,可选依赖和排除依赖的区别,可选依赖是统一配置,也就导致灵活性的缺失;排除依赖对应不同项目间的依赖就比较的灵活,可自由配置。不过需要注意可选依赖的优先级高于排除依赖,若对于同一个间接依赖同时使用排除依赖和可选依赖进行设置,那么可选依赖的取值必须为 false,否则排除依赖无法生效。

依赖调解

当一个间接依赖存在多条引入路径时,为了避免出现依赖重复的问题,Maven 通过依赖调节来确定间接依赖的引入路径。依赖调节遵循以下两条原则:

  1. 引入路径短者优先

还是用之前的案例,假设现在存在这样一个关系:

helloMaven2项目->junit
helloMaven2项目->helloMaven项目->junit

helloMaven项目,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

helloMaven2项目,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>com.study.test</groupId>
            <artifactId>helloMaven</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.2</version>
        </dependency>
    </dependencies>

在这里插入图片描述

helloMaven项目路径长度为2,helloMaven2项目路径长度为1,红框所示,helloMaven2项目中的junit最终引用3.8.2版本。

  1. 先声明者优先

如果路径都一样长的话,第一原则就无法起作用,假设现在存在这样一个关系:

helloMaven2项目->helloMaven项目->junit4.12
helloMaven2项目->helloMaven3项目->junit3.8.2

helloMaven项目,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

helloMaven3项目,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.2</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

helloMaven2项目,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>com.study.test</groupId>
            <artifactId>helloMaven</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.study.test</groupId>
            <artifactId>helloMaven3</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

在这里插入图片描述
helloMaven项目路径长度为1,helloMaven3项目路径长度为1,长度相同的前提下,顺序靠前的优先使用。红框所示,helloMaven2项目中的junit最终引用4.12版本。

以上两条原则,优先使用第一条原则解决,第一条原则无法解决,再使用第二条原则解决。

  1. 覆盖策略

同一个pom.xml文件下,出现不同版本的依赖,依赖调解两大原则都不起作用,假设存在这样一个关系:

helloMaven2项目->junit4.12
helloMaven2项目->junit3.8.2

示例代码如下:

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.2</version>
        </dependency>
    </dependencies>

在这里插入图片描述

红框所示,helloMaven2项目最终引入3.8.2的版本。

归类依赖

当依赖包很多时,会出现很多版本号相同的情况,我们可以将这些版本号统一管理,如果多个版本号一起变动的时候,可以方便修改。示例代码如下:

    <dependencies>
        <dependency>
            <groupId>com.study.test</groupId>
            <artifactId>helloMaven</artifactId>
            <version>${helloMaven.version}</version>
        </dependency>
        <dependency>
            <groupId>com.study.test</groupId>
            <artifactId>helloMaven3</artifactId>
            <version>${helloMaven.version}</version>
        </dependency>
    </dependencies>

通过<properties>标签,可以定义对应的版本号,通过占位符使用。

properties属性

归类依赖篇章中简单介绍了占位符的使用,使用${}当作占位符,通过再<properties>标签中定义变量。

Maven总共有5类属性:POM属性、自定义属性、Settings属性、Java系统属性和环境变量属性

  • POM属性

(1)${project.basedir}:这引用了module/project的根文件夹(当前pom.xml文件所在的位置),还可以简化的写法:${basedir}
(2)${project.build.sourceDirectory}:项目的主源码目录,默认为src/main/java/
(3)${project.build.testSourceDirectory}:项目的测试源码目录,默认为src/test/java/
(4)${project.build.directory} : 项目构建输出目录,默认为target/
(5)${project.outputDirectory} : 项目主代码编译输出目录,默认为target/classes/
(6)${project.testOutputDirectory}:项目测试主代码输出目录,默认为target/testclasses/
(7)${project.groupId}:项目的groupId
(8)${project.artifactId}:项目的artifactId
(9)${project.version}:项目的version,与${version} 等价。
(10)${project.build.finalName}:项目打包输出文件的名称,默认为${project.artifactId}-${project.version}

  • Settings属性

settings. 开头的属性引用settings.xml文件中的XML元素的值。

  • Java系统属性

所有Java系统属性都可以用Maven属性引用,如${user.home}指向了用户目录。

  • 环境变量属性

所有环境变量属性都可以使用以env. 开头的Maven属性引用,如${env.JAVA_HOME}指代了JAVA_HOME环境变量的值。

占位符除了可以再pom.xml文件中使用,还可以再.properties.yml等文件中使用。

  • 自定义属性

<properties>标签中定义。

子模块 helloMaven-server,示例代码如下:

<?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>helloMaven</artifactId>
        <groupId>com.study.test</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>helloMaven-server</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
</project>

.properties文件

maven.compiler.target=${maven.compiler.target}
maven.compiler.target1=${maven.compiler.target1}

执行mvn install命令,再target->classes目录下,可以看到编译后的.properties文件,如图
在这里插入图片描述
可以看到pom文件中定义的占位符,编译后自动替换成了对应的值。

插件(plugin)

Maven 实际上是一个依赖插件执行的框架,它执行的每个任务实际上都由插件完成的。Maven 的核心发布包中并不包含任何 Maven 插件,它们以独立构件的形式存在, 只有在 Maven 需要使用某个插件时,才会去仓库中下载。

在这里插入图片描述
执行mvn clean install 命令,Maven 默认为一些核心的生命周期阶段绑定了插件目标,当用户调用这些阶段时,对应的插件目标就会自动执行相应的任务。

生命周期

Maven的生命周期将项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建过程进行了抽象和统一。

Maven 生命周期是抽象的,其本身不能做任何实际工作,这些实际工作(如源代码编译)都通过调用 Maven 插件 中的插件目标(plugin goal)完成的。

Maven 拥有三套标准的生命周期:

  • clean: 在进行真正的构建之前进行一些清理工作。删除前一次构建在target文件夹下生成的各个Jar包等,它可以分为三个阶段:
    (1)pre-clean:清理前
    (2)clean:清理
    (3)post-clean:清理后

示例代码如下:

    <build>
        <plugins>
            <!-- 添加插件 maven-antrun-plugin -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.1</version>
                <executions>
                    <execution>
                        <!--自定义阶段 id -->
                        <id>pre-clean</id>
                        <!--阶段 -->
                        <phase>pre-clean</phase>
                        <!--目标 -->
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <!--配置 -->
                        <configuration>
                            <!-- 执行的任务 -->
                            <tasks>
                                <!-- 输出自定义文本信息 -->
                                <echo>预清理阶段</echo>
                            </tasks>
                        </configuration>
                    </execution>
                    <execution>
                        <!--自定义阶段 id -->
                        <id>clean</id>
                        <!--阶段 -->
                        <phase>clean</phase>
                        <!--目标 -->
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <!--配置 -->
                        <configuration>
                            <!-- 执行的任务 -->
                            <tasks>
                                <!--自定义文本信息 -->
                                <echo>清理阶段</echo>
                            </tasks>
                        </configuration>
                    </execution>
                    <execution>
                        <!--自定义阶段 id -->
                        <id>post-clean</id>
                        <!--阶段 -->
                        <phase>post-clean</phase>
                        <!-- 目标 -->
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <!--配置 -->
                        <configuration>
                            <!-- 执行的任务 -->
                            <tasks>
                                <!-- 执行的任务 -->
                                <echo>清理后阶段</echo>
                            </tasks>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

输入mvn post-claen命令,一般情况下不会执行post-clean阶段,如图
在这里插入图片描述

  • default:定义了构建项目时所需要的执行步骤,它是所有生命周期中最核心部分,比如:编译,测试,打包,部署等等。

简单讲解部分流程,示例代码如下:

    <build>
        <plugins>
            <!-- 添加插件 maven-antrun-plugin -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.1</version>
                <executions>
                    <execution>
                        <!--自定义阶段 id -->
                        <id>validate</id>
                        <!--阶段 -->
                        <phase>validate</phase>
                        <!--目标 -->
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <!--配置 -->
                        <configuration>
                            <!-- 执行的任务 -->
                            <tasks>
                                <!-- 输出自定义文本信息 -->
                                <echo>校验阶段</echo>
                            </tasks>
                        </configuration>
                    </execution>
                    <execution>
                        <!--自定义阶段 id -->
                        <id>compile</id>
                        <!--阶段 -->
                        <phase>compile</phase>
                        <!--目标 -->
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <!--配置 -->
                        <configuration>
                            <!-- 执行的任务 -->
                            <tasks>
                                <!--自定义文本信息 -->
                                <echo>编译阶段</echo>
                            </tasks>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

执行mvn package命令,如图

在这里插入图片描述

我们可以看到打包的完整流程,最开始从校验开始、然后编译、等,到最后完成打包。比较重要的一些步骤命令如下:

  1. validate:验证工程是否正确,所有需要的资源是否可用。
  2. initialize:做构建前的初始化操作,比如初始化参数、创建必要的目录等
  3. compile:编译项目的源代码。
  4. test:使用合适的单元测试框架来测试已编译的源代码。这些测试不需要已打包和布署。
  5. package:把已编译的代码打包成可发布的格式,比如jar。
  6. verify:运行所有检查,验证包是否有效且达到质量标准。
  7. install:把包安装到maven本地仓库,可以被其他工程作为依赖来使用。
  8. deploy:在集成或者发布环境下执行,将最终版本的包拷贝到远程的repository,使得其他的开发者或者工程可以共享。

比如:mvn test 就是在调用default生命周期的test阶段,实际执行了validatetest阶段之间的所有阶段,如图所示:

在这里插入图片描述

比如:mvn clean package 就是先调用clean生命周期的pre-cleanclean阶段,再调用default生命周期的validatepackage阶段,如图所示:

在这里插入图片描述

执行完后,可以看到项目中的target目录中生成了对应的Jar包,如图所示:
在这里插入图片描述

  • site:目的是建立和发布项目站点,生成一个站点,方便团队交流和发布项目信息。一共有四个阶段:
    (1)pre-site:执行生成站点前的准备工作,比如下载一些依赖包。
    (2)site:生成项目站点文档。
    (3)post-site:执行生成站点后需要收尾的工作。
    (4)site-deploy:将生成的站点发布到服务器上。

执行mvn site命令,示例代码如下:

    <build>
        <plugins>
            <!-- 添加如下两个插件 maven-site-plugin,不使用默认jar包,否则报找不到jar包 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-site-plugin</artifactId>
                <version>3.7.1</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>3.0.0</version>
            </plugin>
            <!-- 添加插件 maven-antrun-plugin -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.3</version>
                <executions>
                    <execution>
                        <!--自定义阶段 id -->
                        <id>pre-site</id>
                        <!--阶段 -->
                        <phase>pre-site</phase>
                        <!--目标 -->
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <!--配置 -->
                        <configuration>
                            <!-- 执行的任务 -->
                            <tasks>
                                <!--自定义文本信息 -->
                                <echo>站点前阶段</echo>
                            </tasks>
                        </configuration>
                    </execution>
                    <execution>
                        <!--自定义阶段 id -->
                        <id>site</id>
                        <!--阶段 -->
                        <phase>site</phase>
                        <!--目标 -->
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <!--配置 -->
                        <configuration>
                            <!-- 执行的任务 -->
                            <tasks>
                                <!--自定义文本信息 -->
                                <echo>生成站点阶段</echo>
                            </tasks>
                        </configuration>
                    </execution>
                    <execution>
                        <!--自定义阶段 id -->
                        <id>post-site</id>
                        <!--阶段 -->
                        <phase>post-site</phase>
                        <!--目标 -->
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <!--配置 -->
                        <configuration>
                            <!-- 执行的任务 -->
                            <tasks>
                                <!--自定义文本信息 -->
                                <echo>站点后阶段</echo>
                            </tasks>
                        </configuration>
                    </execution>
                    <execution>
                        <!--自定义阶段 id -->
                        <id>site-deploy</id>
                        <!--阶段 -->
                        <phase>site-deploy</phase>
                        <!--目标 -->
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <!--配置 -->
                        <configuration>
                            <!-- 执行的任务 -->
                            <tasks>
                                <!-- 输出自定义文本信息 -->
                                <echo>站点发布阶段</echo>
                            </tasks>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

执行结果如图:
在这里插入图片描述

我们找到target目录里的site文件,打开index.html站点页面,如图所示:
在这里插入图片描述

phase(阶段)和goal(目标)

Maven对一些生命周期的phase(阶段) 默认绑定了插件目标,每个phase(阶段)会挂接一到多个goal(目标)goal(目标)Maven里定义任务的最小单元。可以把插件理解为一个类,构建目标是类中的方法,构建阶段是是对这些方法的顺序调用。

有很多插件明确要求你要定义你的目标,下面讲解下如何查看插件目标:

  • 执行 mvn help:describe -Dplugin='org.apache.maven.plugins:maven-shade-plugin'

在这里插入图片描述
如图,红框所示,比如:shade:shade,右边的值就是对应的goal的值。

  • 使用编辑器查看,比如:idea。

在这里插入图片描述
如图,红框所示,阶段:目标

configuration

对当前插件进行配置,并不是所有插件的配置都是一样的,但是它们之间确实是有相同的配置,如果你引用了非当前插件的配置,可能会导致意外的错误,或许你可以使用idea等编辑器查看有哪些配置,如图

在这里插入图片描述

简单列举一些插件的配置,示例代码如下:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <!--声明源代码的jdk版本-->
                    <source>1.8</source>
                    <!--声明生成目标的jdk版本-->
                    <target>1.8</target>
                    <!--声明编译文件的编码-->
                    <encoding>UTF-8</encoding>
                    <!-- 指定打包的jar包输出路径 -->
                    <outputDirectory>${project.build.directory}/lib</outputDirectory>
                    <excludes>
                        <exclude>**/*.properties</exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <tasks>
                        <echo>打印:test</echo>
                    </tasks>
                </configuration>
            </plugin>
        </plugins>
    </build>

如果你对这些配置标签有所不理解,可以翻阅官方文档,它会告诉你每个插件的配置标签,和一些代码示例。

Archetype(原型/模板)

Maven 的所有功能都是通过插件实现的,Archetype 也不例外,由一个名为 maven-archetype-plugin 的插件实现的,该插件提供了 ArcheType 的所有功能。

ArchetypeMaven 项目的模板工具包,它定义了 Maven 项目的基本架构。Archetype 为开发人员提供了数千种创建 Maven 项目的模板,Maven 通过这些模板可以帮助用户快速的生成项目的目录结构以及 POM 文件。

以Idea创建为例,如图所示:
在这里插入图片描述
在红框处,选择模板(maven-archetype-quickstart),点击create按钮,等待生成项目。

在这里插入图片描述
生成好的项目,我们可以看到再main文件和test文件下生成了默认的初始类,不仅如此,pom文件中也加入很多插件。除此之外还有很多的模板,参考网上其它文章,如图所示:在这里插入图片描述

占位符替换

spring-boot为了保护application.ymlapplication.properties,修改了默认的占位符${*}@*@,我们也可以修改默认占位符格式,示例代码如下:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <!--是否使用默认占位符-->
                    <useDefaultDelimiters>false</useDefaultDelimiters>
                    <delimiters>
                        <!--占位符格式,*号代表内容-->
                        <delimiter>$[*]</delimiter>
                    </delimiters>
                    <!--占位符中value的编码方式-->
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

在这里插入图片描述
当格式修改为$[],执行mvn install命令,可以看到能够正常替换值,说明格式替换成功。

打包

默认Maven打包的结果只包含项目本身的代码,不会包含依赖,如图:

在这里插入图片描述

在真正的开发中,会包含很多依赖,如果不对这些依赖打包,项目运行过程中就会出错。

在这里插入图片描述

Maven中,主要有3个插件(当然还有很多框架打包插件)可以用来打包:

  • maven-jar-plugin插件:默认的打包插件,用来打普通的项目Jar包。

示例代码如下:

    <build>
        <plugins>
            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.4</version>
            </plugin>
        </plugins>
    </build>

执行mvn install命令,结果如图
在这里插入图片描述
执行结果如上图所示。

  • maven-assembly-plugin插件:定制化打包方式,对项目目录的重新组装。assembly配置分为两部分,一部分是assembly的配置文件,一部分是在pom.xml中的配置。

示例代码如下:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.2-beta-5</version>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <appendAssemblyId>false</appendAssemblyId>
                    <descriptors>
                        <!--   这个是assembly 所在位置  -->
                        <descriptor>src/main/resources/assembly.xml</descriptor>
                    </descriptors>
                </configuration>
            </plugin>
        </plugins>
    </build>

assembly.xml,示例代码如下:

<assembly>
    <formats>
        <!--支持 zip,tar,tar.gz,tar.bz2,jar,dir,war 等 -->
        <format>tar.gz</format>
        <format>zip</format>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <!--文件集,可用于不同文件存放路径  -->
    <fileSets>
        <fileSet>
            <!--资源路径-->
            <directory>src/main/resources</directory>
            <!--输出目录-->
            <outputDirectory>conf</outputDirectory>
            <!--文件的权限-->
            <fileMode>0644</fileMode>
        </fileSet>
    </fileSets>
    <!--依赖jar包以及项目打包文件存储文件-->
    <dependencySets>
        <dependencySet>
            <outputDirectory>lib</outputDirectory>
        </dependencySet>
    </dependencySets>
</assembly>

执行 mvn assembly:assembly -Dmaven.test.skip=true命令或者mvn package命令,执行结果如图:

在这里插入图片描述
assembly插件把本身项目也当作依赖进行了打包,里面存放着对应的代码。

  • maven-shade-plugin插件:简单的依赖打包。

示例代码如下:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <id>shade-plugin</id>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <filters>
                        <filter>
                            <artifact>*:*</artifact>
                            <!--排除以下文件,防止程序启动启动时,校验错误-->
                            <excludes>
                                <exclude>META-INF/*.SF</exclude>
                                <exclude>META-INF/*.DSA</exclude>
                                <exclude>META-INF/*.RSA</exclude>
                            </excludes>
                        </filter>
                    </filters>
                    <!--资源转换器:定义程序入口,除此之外还可以合并相同文件名资源-->
                    <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <mainClass>com.study.test.HelloMaven</mainClass>
                        </transformer>
                    </transformers>
                </configuration>
            </plugin>
        </plugins>
    </build>

执行mvn package命令,结果如图
在这里插入图片描述
图中我们看到生成文件目录下有两个文件,一个是原Jar包文件,里面不包含依赖,一个是shade插件生成的文件,与assembly插件不同,依赖并不是以Jar包的形式,而是.class文件进行引入。

profile

一个项目有不同的运行环境,比如,开发环境,测试环境、生产环境。不同的环境有不同的配置。每次将项目部署到不同的环境时,都需要修改相应的配置,这样重复的工作,不仅浪费劳动力,还容易出错。为了解决这一问题,Maven 引入了 Profile 的概念,通过它可以为不同的环境定制不同的构建过程。

在这里插入图片描述

示例代码如下:

	<!--定义资源位置-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources/</directory>
                <filtering>true</filtering>
                <includes>
                    <include>${profile.active}.properties</include>
                </includes>
            </resource>
        </resources>
    </build>
	<!--定义配置环境-->
    <profiles>
        <profile>
            <id>dev</id>
            <properties>
                <profile.active>dev</profile.active>
            </properties>
        </profile>
        <profile>
            <id>test</id>
            <!--设置默认环境-->
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <profile.active>test</profile.active>
            </properties>
        </profile>
        <profile>
            <id>prod</id>
            <properties>
                <profile.active>prod</profile.active>
            </properties>
        </profile>
    </profiles>

其原理很简单通过<profiles>标签配置不同环境标识和命令,然后通过<resources>标签定义资源位置,在<includes>标签中使用占位符,灵活打包配置文件。

简单介绍下<resources>标签结构含义:

<build>
	<resources>
		  <resource>
		 		<directory></directory>
		 		<filtering></filtering>
			  <includes>
		  			<include></include>
		 		</includes>
		  	<excludes>
		   			<exclude></exclude>
		  	</excludes>
		 </resource>
	</resources>
</build>
  • <directory>:资源文件目录。
  • <filtering>:布尔值,打包时的配置文件中是否进行变量替换,与<filters>标签使用。
  • <includes>:指定打包文件。
  • <excludes>:指定不打包文件。

我们执行mvn clean test -Ptest命令,激活配置,如图所示,可以看到打包了test的配置文件:

在这里插入图片描述
激活的方式有很多种,比如:

  • 命令行激活:id为自定义的值,执行多个profile用,分开。
mvn clean install -Pid,id
  • settings.xml 文件显示激活
<activeProfiles>
    <activeProfile>test</activeProfile>
</activeProfiles>
  • 操作系统环境激活
<activation>
    <os>
        <name>Windows 10</name>
        <family>Windows</family>
        <arch>amd64</arch>
        <version>10.0</version>
    </os>
</activation>
  • 文件存在与否激活
<activation>
        <file>
            <exists>./src/main/resources/env.prod.properties</exists>
            <missing>./src/main/resources/env.test.properties</missing>
        </file>
</activation>
  • 默认激活
<activation>
    <activeByDefault>true</activeByDefault>
</activation>

除了使用Maven方式调用不同配置环境,还有很多方法,简单列举常见的:

  • 使用java命令指定配置文件
java -jar 生成的jar包 --spring.profiles.active=prod
  • idea设置启动环境

在这里插入图片描述

  • springboot-application.yml多环境配置

在这里插入图片描述
修改application.yml文件中的active值,比如active: test就是将读取application-test.yml文件,示例代码如下:

spring:
  profiles:
    active: test

依赖管理和插件管理

  • dependencyManagement依赖管理

在前面依赖范围import,篇章中有介绍到依赖管理的使用,在该元素下声明的依赖不会实际引入到模块中,只有在 dependencies 元素下同样声明了该依赖,才会引入到模块中。若未指定版本,则使用 dependencyManagement 中指定的版本,否则将覆盖 dependencyManagement 中的版本。

以继承项目为例,父项目helloMaven,示例代码如下:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

子模块helloMaven-server,示例代码如下:

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>

项目结构如下,可以看到红框所示部分,引入了junit的包,且代码也能正常使用。
在这里插入图片描述

  • pluginManagement插件管理

pluginManagement 元素与 dependencyManagement 元素的原理十分相似,主要用于Maven插件的管理。如图,我们可以看到helloMaven-server项目编译时,Maven默认的插件版本

在这里插入图片描述
以继承项目为例,父项目helloMaven,示例代码如下:

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

子模块helloMaven-server,示例代码如下:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

执行 mvn install命令后,如图所示,插件的版本改变
在这里插入图片描述

仓库管理

仓库顾名思义是用来存储东西的地方, 在 Maven 中任何一个依赖、插件或者项目构建的输出,都可以称之为 “构件” 。Maven 仓库能帮助我们管理构件(主要是JAR)。

Maven 根据坐标寻找构件时,它会首先查看本地仓库,若本地仓库存在此构件,则直接使用;若本地仓库不存在此构件,Maven 就会去远程仓库查找,若发现所需的构件后,则下载到本地仓库使用。如果本地仓库远程仓库都没有所需的构件,则 Maven 就会报错。本地仓库就像一个 “缓存”,从远程仓库下载 Jar 包到本地仓库中以备将来使用,本地仓库会随着项目的积累越变越大。

Maven 仓库可以分为 2 个大类:
在这里插入图片描述

  • 本地仓库

默认情况下,不管在Window还是Linux下,每个用户在自己用户目录下都有一个路径名为.m2/repository/的仓库目录。在%M2_HOME%\conf 目录下的settings.xml中,可以配置本地仓库位置,示例代码如下:

  <localRepository>C:\Users\.m2</localRepository>

在这里插入图片描述

  • 远程仓库

远程仓库还可以分为 3 个小类:中央仓库、私服、其他公共仓库

(1)中央仓库

中央仓库是由 Maven 社区提供的一种特殊的远程仓库,它包含了绝大多数流行的开源构件。在默认情况下,当本地仓库没有 Maven 所需的构件时,会首先尝试从中央仓库下载。可通过 http://search.maven.org/ 或者http://mvnrepository.com来访问。

(2)私服

私服一般是指公司内部使用的仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,它是开发人员自己定制仓库,运行 Maven 时,Maven 所需要的任何构件都是从本地仓库获取的,但是如果本地仓库没有,它就会尝试从远程仓库中下载构件到本地仓库。如果 Maven 无法连接到远程仓库,将无法正常构建项目。

配置示例代码如下:

<!-- 配置远程仓库 -->
    <repositories>
        <repository>
            <id>maven-repo</id>
            <name>Repository</name>
            <url>http://192.168.16.10/maven2/</url>
            <releases>
                <enabled>true</enabled>
                <updatePolicy>daily</updatePolicy>
            </releases>
            <snapshots>
                <enabled>false</enabled>
                <checksumPolicy>warn</checksumPolicy>
            </snapshots>
            <layout>default</layout>
        </repository>
    </repositories>

(3)其他公共仓库

除了中央仓库、私服外,还有公共仓库,可能在中央仓库中没有,示例代码如下:

  <!-- 设定远程主仓库,按设定顺序进行查找。 -->
    <repositories>    
        <repository>
            <id>oschina-repos</id>
            <name>Oschina Releases</name>
            <url>http://maven.oschina.net/content/groups/public</url>
        </repository>
        
        <repository>
            <id>aliyun-repos</id>
            <name>aliyun Releases</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        </repository>
    </repositories>

章节目录

上一章:项目管理工具:Maven(上)
下一章:项目管理工具:Maven-Nexus(下)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值