Maven


一、Maven介绍

1、Maven简介

maven 是一个项目管理工具,主要作用是在项目开发阶段对Java项目进行依赖管理和项目构建。

依赖管理:就是对jar包的管理。通过导入maven坐标,就相当于将仓库中的jar包导入了当前项目中。

项目构建:通过maven的一个命令就可以完成项目从清理、编译、测试、报告、打包,部署整个过程。

在这里插入图片描述

2、Maven的仓库类型

  1. 本地仓库
  2. 远程仓库:

依赖搜索地址:

https://mvnrepository.com/

3、配置文件

核心程序压缩包:apache-maven-3.8.4-bin.zip,解压到非中文、没有空格的目录。

在解压目录中,我们需要着重关注 Maven 的核心配置文件:conf/settings.xml

(1)默认仓库路径

本地仓库默认值:用户/.m2/repository。配置方式如下:

<localRepository>D:\maven-repository</localRepository>

注意:本地仓库本身也需要使用一个非中文、没有空格的目录。

(2)配置阿里云镜像仓库

将原有的例子配置注释掉

<!-- <mirror>
  <id>maven-default-http-blocker</id>
  <mirrorOf>external:http:*</mirrorOf>
  <name>Pseudo repository to mirror external repositories initially using HTTP.</name>
  <url>http://0.0.0.0/</url>
  <blocked>true</blocked>
</mirror> -->

<mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>central</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

(3)配置jdk版本

如果按照默认配置运行,Java 工程使用的默认 JDK 版本是 1.5,而我们熟悉和常用的是 JDK 1.8 版本。修改配置的方式是:将 profile 标签整个复制到 settings.xml 文件的 profiles 标签内。

<profile>
    <id>jdk-1.8</id>
    <activation>
        <activeByDefault>true</activeByDefault>
        <jdk>1.8</jdk>
    </activation>
    <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>

二、Maven的使用

1、核心概念

(1)坐标

  • groupId:公司或组织域名的倒序,通常也会加上项目名称
    • 例如:com.atguigu.maven
  • artifactId:模块的名称,将来作为 Maven 工程的工程名
  • version:模块的版本号,根据自己的需要设定
    • 例如:SNAPSHOT 表示快照版本,正在迭代过程中,不稳定的版本
    • 例如:RELEASE 表示正式版本
<dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
</dependency>

上面坐标对应的 jar 包在 Maven 本地仓库中的位置:

Maven本地仓库根目录\javax\servlet\servlet-api\2.5\servlet-api-2.5.jar

(2)Maven的依赖范围

依赖范围 对于编译classpath有效 对于测试classpath有效 对于运行时classpath有效 例子
compile Y Y Y spring-core
test - Y - Junit
provided Y Y - servlet-api
runtime - Y Y JDBC驱动
system Y Y - 本地的,maven仓库之外的类库

compile:通常使用的第三方框架的 jar 包这样在项目实际运行时真正要用到的 jar 包都是以 compile 范围进行依赖的。比如 SSM 框架所需jar包。

test:测试过程中使用的 jar 包,以 test 范围依赖进来。比如 junit。

provided:在开发过程中需要用到的“服务器上的 jar 包”通常以 provided 范围依赖进来。比如 servlet-api、jsp-api。而这个范围的 jar 包之所以不参与部署、不放进 war 包,就是避免和服务器上已有的同类 jar 包产生冲突,同时减轻服务器的负担。

依赖范围的配置通过<scope>标签实现

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

(3)依赖传递

在 A 依赖 B,B 依赖 C 的前提下,C 是否能够传递到 A,取决于 B 依赖 C 时使用的依赖范围。

  • B 依赖 C 时使用 compile 范围:可以传递
  • B 依赖 C 时使用 test 或 provided 范围:不能传递,所以需要这样的 jar 包时,就必须在需要的地方明确配置依赖才可以。

(4)依赖的排除

当 A 依赖 B,B 依赖 C 而且 C 可以传递到 A 的时候,A 不想要 C,需要在 A 里面把 C 排除掉。而往往这种情况都是为了避免 jar 包之间的冲突。

所以配置依赖的排除其实就是阻止某些 jar 包的传递。因为这样的 jar 包传递过来会和其他 jar 包冲突。

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

2、生成Maven工程

运行 mvn archetype:generate 命令

下面根据提示操作

TIP
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 7:【直接回车,使用默认值】
Define value for property 'groupId': com.atguigu.maven
Define value for property 'artifactId': pro01-maven-java
Define value for property 'version' 1.0-SNAPSHOT: :【直接回车,使用默认值】
Define value for property 'package' com.atguigu.maven: :【直接回车,使用默认值】
Confirm properties configuration: groupId: com.atguigu.maven artifactId: pro01-maven-java version: 1.0-SNAPSHOT package: com.atguigu.maven Y: :【直接回车,表示确认。如果前面有输入错误,想要重新输入,则输入 N 再回车。】

Maven 默认生成的工程,对 junit 依赖的是较低的 3.8.1 版本,我们可以改成较适合的 4.12 版本。

 <!-- 当前Maven工程的坐标 -->
  <groupId>com.atguigu.maven</groupId>
  <artifactId>pro01-maven-java</artifactId>
  <version>1.0-SNAPSHOT</version>

  <!-- 当前Maven工程的打包方式,可选值有下面三种: -->
  <!-- jar:表示这个工程是一个Java工程  -->
  <!-- war:表示这个工程是一个Web工程 -->
  <!-- pom:表示这个工程是“管理其他工程”的工程 -->
  <packaging>jar</packaging>

  <name>pro01-maven-java</name>
  <url>http://maven.apache.org</url>

  <properties>
    <!-- 工程构建过程中读取源码时使用的字符集 -->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <!-- 当前工程所依赖的jar包 -->
  <dependencies>
    <!-- 使用dependency配置一个具体的依赖 -->
    <dependency>

      <!-- 在dependency标签内使用具体的坐标依赖我们需要的一个jar包 -->
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>

      <!-- scope标签配置依赖的范围 -->
      <scope>test</scope>
    </dependency>
  </dependencies>

3、构建命令

运行 Maven 中和构建操作相关的命令时,必须进入到 pom.xml 所在的目录。

清理操作

mvn clean

效果:删除 target 目录

编译操作

主程序编译:编译结果存放的目录:target/classes

mvn compile

测试程序编译:编译结果存放的目录:target/test-classes

mvn test-compile

测试操作

mvn test

测试的报告存放的目录:target/surefire-reports

打包操作

mvn package

打包的结果——jar 包,存放的目录:target

安装操作

mvn install

安装的效果是将本地构建过程中生成的 jar 包存入 Maven 本地仓库。这个 jar 包在 Maven 仓库中的路径是根据它的坐标生成的。

另外,安装操作还会将 pom.xml 文件转换为 XXX.pom 文件一起存入本地仓库。所以我们在 Maven 的本地仓库中想看一个 jar 包原始的 pom.xml 文件时,查看对应 XXX.pom 文件即可,它们是名字发生了改变,本质上是同一个文件。

跳过测试

# -D 表示后面要附加命令的参数,字母 D 和后面的参数是紧挨着的,中间没有任何其它字符
# maven.test.skip=true 表示在执行命令的过程中跳过测试
mvn clean install -Dmaven.test.skip=true

4、继承

Maven工程之间,A 工程继承 B 工程

  • B 工程:父工程
  • A 工程:子工程

本质上是 A 工程的 pom.xml 中的配置继承了 B 工程中 pom.xml 的配置。

作用是统一管理依赖,统一版本。

(1)创建父工程

工程创建好之后,要修改它的打包方式:

  <groupId>com.atguigu.maven</groupId>
  <artifactId>pro03-maven-parent</artifactId>
  <version>1.0-SNAPSHOT</version>

  <!-- 当前工程作为父工程,它要去管理子工程,所以打包方式必须是 pom -->
  <packaging>pom</packaging>

只有打包方式为 pom 的 Maven 工程能够管理其他 Maven 工程。打包方式为 pom 的 Maven 工程中不写业务代码,它是专门管理其他 Maven 工程的工程。

(2)创建模块工程

模块工程类似于 IDEA 中的 module,所以需要进入 pro03-maven-parent 工程的根目录,然后运行 mvn archetype:generate 命令来创建模块工程。

<!-- 使用parent标签指定当前工程的父工程 -->
<parent>
    <!-- 父工程的坐标 -->
    <groupId>com.atguigu.maven</groupId>
    <artifactId>pro03-maven-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

<!-- 子工程的坐标 -->
<!-- 如果子工程坐标中的groupId和version与父工程一致,那么可以省略 -->
<!-- <groupId>com.atguigu.maven</groupId> -->
<artifactId>pro04-maven-module</artifactId>
<!-- <version>1.0-SNAPSHOT</version> -->

(3)父工程依赖配置

<?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.myorg.myapp</groupId>  
    <artifactId>app-parent</artifactId>  
    <packaging>pom</packaging>  
    <version>1.0-SNAPSHOT</version>  

    <!-- 聚合子模块 -->
    <modules>  
        <module>pro04-maven-module</module>
        <module>pro05-maven-module</module>
        <module>pro06-maven-module</module>
    </modules>

    <!-- 通过自定义属性,统一指定Spring的版本 -->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!-- 自定义标签,维护Spring版本数据 -->
        <spring.version>4.3.6.RELEASE</spring.version>
    </properties>

    <!-- 使用dependencyManagement标签配置对依赖的管理 -->
    <!-- 被管理的依赖并没有真正被引入到工程 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <!-- 使用${}的形式来引用自定义的属性名 -->
                <version>${spring.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>  

(4)子工程引用父依赖

关键点:省略版本号

<!-- 子工程引用父工程中的依赖信息时,可以把版本号去掉。    -->
<!-- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定。 -->
<!-- 具体来说是由父工程的dependencyManagement来决定。 -->
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
    </dependency>
</dependencies>

5、设置打包文件名

build目标文件的名称,默认情况为${artifactId}-${version}

<project> 
    <build>
        <finalName>rewind</finalName>
    </build>
</project> 

三、微服务打包插件

我们可以将此JAR文件用作另一个项目中的依赖项。但是在Spring Boot应用程序中,我们不能直接使用java -jar JAR_FILE执行上述JAR文件。这是因为没有捆绑运行时依赖项

要使用简单的java -jar命令启动Spring Boot应用程序,我们需要spring-boot-maven-plugin插件

可以以 SpringBoot 微服务形式直接运行的 jar 包包括:

  • 当前微服务本身代码
  • 当前微服务所依赖的 jar 包
  • 内置 Tomcat(Servlet 容器)
  • 与 jar 包可以通过 java -jar 方式直接启动相关的配置

要加入额外的资源、相关配置等等,仅靠 Maven 自身的构建能力是不够的,所以要通过 build 标签引入下面的插件。

<!-- build 标签:用来配置对构建过程的定制 -->
<build>
    <finalName>${project.artifactId}</finalName>
    <!-- plugins 标签:定制化构建过程中所使用到的插件 -->
    <plugins>
        <!-- plugin 标签:一个具体插件 -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
               <excludes>
                   <exclude>
                       <groupId>org.projectlombok</groupId>
                       <artifactId>lombok</artifactId>
                   </exclude>
               </excludes>
           </configuration>
        </plugin>
    </plugins>
</build>

使用命令

mvn clean package spring-boot:repackage -Dmaven.test.skip=true

spring-boot:repackage 这个命令需要以现有JAR或WAR存档为源,将项目类重新打包,包括所有项目运行时依赖都包括在一起。这样才可以使用命令行 java -jar JAR_FILE.jar 来执行重新打包好的JAR文件。

在重新打包的JAR文件中,不仅具有来自项目的已编译Java类,而且还具有启动Spring Boot应用程序所需的所有运行时库。

repackage 命令生成的包,默认会包含项目引入的所有依赖,包括scope为provied的依赖,若除去provided依赖,可参考上面排除lombok案例

四、help插件

1、help 插件的各个目标

官网说明地址:https://maven.apache.org/plugins/maven-help-plugin

目标 说明
help:active-profiles 列出当前已激活的 profile
help:all-profiles 列出当前工程所有可用 profile
help:describe 描述一个插件和/或 Mojo 的属性
help:effective-pom 以 XML 格式展示有效 POM
help:effective-settings 为当前工程以 XML 格式展示计算得到的 settings 配置
help:evaluate 计算用户在交互模式下给出的 Maven 表达式
help:system 显示平台详细信息列表,如系统属性和环境变量

2、查看属性值

定义属性值

<properties>
    <com.rewind.hello>good morning maven</com.rewind.hello>
</properties>

通过以下命令查看

mvn help:evaluate

然后在命令行输入

${com.rewind.hello}

输出

good morning maven

3、访问java系统属性

Java 系统属性一览

Properties properties = System.getProperties();
Set<Object> propNameSet = properties.keySet();
for (Object propName : propNameSet) {
    String propValue = properties.getProperty((String) propName);
    System.out.println(propName + " = " + propValue);
}
java.runtime.name = Java(TM) SE Runtime Environment

....

使用 Maven 访问系统属性

通过以下命令查看

mvn help:evaluate

然后在命令行输入

${java.runtime.name}

输出

Java(TM) SE Runtime Environment

4、访问系统环境变量

通过以下命令查看

mvn help:evaluate

然后在命令行输入

${env.JAVA_HOME}

5、访问project标签属性

使用表达式 ${project.xxx} 可以访问当前 POM 中的元素值。

访问一级标签

${project.标签名}

访问子标签

${project.标签名.子标签名}

访问列表标签

${project.标签名[下标]}

6、访问settings 全局配置

${settings.标签名} 可以访问 settings.xml 中配置的元素值。

7、用途

  • 在当前 pom.xml 文件中引用属性
  • 资源过滤功能:在非 Maven 配置文件中引用属性,由 Maven 在处理资源时将引用属性的表达式替换为属性值

五、build标签

1、build 标签的完整示例

在实际使用 Maven 的过程中,我们会发现 build 标签有时候有,有时候没,这是怎么回事呢?其实通过有效 POM 我们能够看到,build 标签的相关配置其实一直都在,只是在我们需要定制构建过程的时候才会通过配置 build 标签覆盖默认值或补充配置。这一点我们可以通过打印有效 POM 来看到。

所以本质上来说:我们配置的 build 标签都是对超级 POM 配置叠加。那我们又为什么要在默认配置的基础上叠加呢?很简单,在默认配置无法满足需求的时候定制构建过程

<build>
    <!-- 主体源程序存放目录 -->
    <sourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\java</sourceDirectory>
    <!-- 脚本源程序存放目录 -->
    <scriptSourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\scripts</scriptSourceDirectory>
    <!-- 测试源程序存放目录 -->
    <testSourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\test\java</testSourceDirectory>
    <!-- 主体源程序编译结果输出目录 -->
    <outputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\classes</outputDirectory>
    <!-- 测试源程序编译结果输出目录 -->
    <testOutputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\test-classes</testOutputDirectory>
    <!-- 主体资源文件存放目录 -->
    <resources>
        <resource>
            <directory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\resources</directory>
        </resource>
    </resources>
    <!-- 测试资源文件存放目录 -->
    <testResources>
        <testResource>
            <directory>D:\idea2019workspace\atguigu-maven-test-prepare\src\test\resources</directory>
        </testResource>
    </testResources>
    <!-- 构建结果输出目录 -->
    <directory>D:\idea2019workspace\atguigu-maven-test-prepare\target</directory>
    <finalName>atguigu-maven-test-prepare-1.0-SNAPSHOT</finalName>

    <!-- pluginManagement 标签存放着几个极少用到的插件: -->
    <pluginManagement>
        <plugins>
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.3</version>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.2-beta-5</version>
            </plugin>
            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.8</version>
            </plugin>
            <plugin>
                <artifactId>maven-release-plugin</artifactId>
                <version>2.5.3</version>
            </plugin>
        </plugins>
    </pluginManagement>

    <!--存放的是默认生命周期中实际会用到的插件-->
    <plugins>
        <plugin>
            <artifactId>maven-clean-plugin</artifactId>
            <version>2.5</version>
            <executions>
                <execution>
                    <id>default-clean</id>
                    <phase>clean</phase>
                    <goals>
                        <goal>clean</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-resources-plugin</artifactId>
            <version>2.6</version>
            <executions>
                <execution>
                    <id>default-testResources</id>
                    <phase>process-test-resources</phase>
                    <goals>
                        <goal>testResources</goal>
                    </goals>
                </execution>
                <execution>
                    <id>default-resources</id>
                    <phase>process-resources</phase>
                    <goals>
                        <goal>resources</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.4</version>
            <executions>
                <execution>
                    <id>default-jar</id>
                    <phase>package</phase>
                    <goals>
                        <goal>jar</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <executions>
                <execution>
                    <id>default-compile</id>
                    <phase>compile</phase>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                </execution>
                <execution>
                    <id>default-testCompile</id>
                    <phase>test-compile</phase>
                    <goals>
                        <goal>testCompile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.12.4</version>
            <executions>
                <execution>
                    <id>default-test</id>
                    <phase>test</phase>
                    <goals>
                        <goal>test</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-install-plugin</artifactId>
            <version>2.4</version>
            <executions>
                <execution>
                    <id>default-install</id>
                    <phase>install</phase>
                    <goals>
                        <goal>install</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-deploy-plugin</artifactId>
            <version>2.7</version>
            <executions>
                <execution>
                    <id>default-deploy</id>
                    <phase>deploy</phase>
                    <goals>
                        <goal>deploy</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-site-plugin</artifactId>
            <version>3.3</version>
            <executions>
                <execution>
                    <id>default-site</id>
                    <phase>site</phase>
                    <goals>
                        <goal>site</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\site</outputDirectory>
                        <reportPlugins>
                            <reportPlugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-project-info-reports-plugin</artifactId>
                            </reportPlugin>
                        </reportPlugins>
                    </configuration>
                </execution>
                <execution>
                    <id>default-deploy</id>
                    <phase>site-deploy</phase>
                    <goals>
                        <goal>deploy</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\site</outputDirectory>
                        <reportPlugins>
                            <reportPlugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-project-info-reports-plugin</artifactId>
                            </reportPlugin>
                        </reportPlugins>
                    </configuration>
                </execution>
            </executions>
            <configuration>
                <outputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\site</outputDirectory>
                <reportPlugins>
                    <reportPlugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-project-info-reports-plugin</artifactId>
                    </reportPlugin>
                </reportPlugins>
            </configuration>
        </plugin>
    </plugins>
</build>

plugins 标签存放的是默认生命周期中实际会用到的插件,这些插件想必大家都不陌生,所以抛开插件本身不谈,我们来看看 plugin 标签的结构:

坐标部分

artifactId 和 version 标签定义了插件的坐标,作为 Maven 的自带插件这里省略了 groupId。

执行部分

executions 标签内可以配置多个 execution 标签,execution 标签内:

  • id:指定唯一标识
  • phase:关联的生命周期阶段
  • goals/goal:关联指定生命周期的目标
    • goals 标签中可以配置多个 goal 标签,表示一个生命周期环节可以对应当前插件的多个目标。

另外,插件目标的执行过程可以进行配置,例如 maven-site-plugin 插件的 site 目标:

六、依赖配置

1、依赖范围

(1)import

管理依赖最基本的办法是继承父工程,但是和 Java 类一样,Maven 也是单继承的。如果不同体系的依赖信息封装在不同 POM 中了,没办法继承多个父工程怎么办?这时就可以使用 import 依赖范围。

典型案例当然是在项目中引入 SpringBoot、SpringCloud 依赖:

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

        <!-- SpringCloud Alibaba 依赖导入 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.2.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <!-- SpringBoot 依赖导入 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.3.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

import 依赖范围使用要求:

  • 打包类型必须是 pom
  • 必须放在 dependencyManagement 中

(2)system

以 Win2dows 系统环境下开发为例,假设现在 D:\tempare\atguigu-maven-test-aaa-1.0-SNAPSHOT.jar 想要引入到我们的项目中,此时我们就可以将依赖配置为 system 范围:

<dependency>
    <groupId>com.atguigu.maven</groupId>
    <artifactId>atguigu-maven-test-aaa</artifactId>
    <version>1.0-SNAPSHOT</version>
    <systemPath>D:\tempare\atguigu-maven-test-aaa-1.0-SNAPSHOT.jar</systemPath>
    <scope>system</scope>
</dependency>

但是很明显:这样引入依赖完全不具有可移植性,所以不要使用。如果需要引入体系外 jar 包我们后面会讲专门的办法。

(3)runtime

专门用于编译时不需要,但是运行时需要的 jar 包。比如:编译时我们根据接口调用方法,但是实际运行时需要的是接口的实现类。典型案例是:

<!--热部署 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

2、可选依赖

可选其实就是『可有可无』。其核心含义是:Project X 依赖 Project A,A 中一部分 X 用不到的代码依赖了 B,那么对 X 来说 B 就是『可有可无』的。

<!--热部署 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <!--optional为true时为可选依赖 -->
    <optional>true</optional>
</dependency>

3、版本仲裁

image-20220915205459422

其实 Maven 的版本仲裁机制只是在没有人为干预的情况下,自主决定 jar 包版本的一个办法。而实际上我们要使用具体的哪一个版本,还要取决于项目中的实际情况。所以在项目正常运行的情况下,jar 包版本可以由 Maven 仲裁,不必我们操心;而发生冲突时 Maven 仲裁决定的版本无法满足要求,此时就应该由程序员明确指定 jar 包版本。

七、profile 详解

1、说明

用于环境隔离

其实即使我们在 pom.xml 中不配置 profile 标签,也已经用到 profile了。为什么呢?因为根标签 project 下所有标签相当于都是在设定默认的 profile。这样一来我们也就很容易理解下面这句话:project 标签下除了 modelVersion 和坐标标签之外,其它标签都可以配置到 profile 中。

2、profile配置

(1)外部视角:配置文件

从外部视角来看,profile 可以在下面两种配置文件中配置:

  • settings.xml:全局生效。其中我们最熟悉的就是配置 JDK 1.8。
  • pom.xml:当前 POM 生效

(2)内部实现:具体标签

从内部视角来看,配置 profile 有如下语法要求:

profiles/profile 标签

  • 由于 profile 天然代表众多可选配置中的一个所以由复数形式的 profiles 标签统一管理。
  • 由于 profile 标签覆盖了 pom.xml 中的默认配置,所以 profiles 标签通常是 pom.xml 中的最后一个标签。

id 标签

每个 profile 都必须有一个 id 标签,指定该 profile 的唯一标识。这个 id 标签的值会在命令行调用 profile 时被用到。这个命令格式是:-D

其它允许出现的标签

一个 profile 可以覆盖项目的最终名称、项目依赖、插件配置等各个方面以影响构建行为。

  • build
    • defaultGoal
    • finalName
    • resources
    • testResources
    • plugins
  • reporting
  • modules
  • dependencies
  • dependencyManagement
  • repositories
  • pluginRepositories
  • properties

3、激活 profile

(1)激活条件

前面提到了,POM 中没有在 profile 标签里的就是默认的 profile,当然默认被激活。

环境信息包含:JDK 版本、操作系统参数、文件、属性等各个方面。一个 profile 一旦被激活,那么它定义的所有配置都会覆盖原来 POM 中对应层次的元素。大家可以参考下面的标签结构:

<profile>
    <id>dev</id>
    <!-- 激活条件 -->
    <activation>
        <!-- 配置是否默认激活 -->
        <activeByDefault>false</activeByDefault>
        <!-- 指定激活条件为:JDK 1.8 -->
        <jdk>1.8</jdk>
        <os>
            <name>Windows XP</name>
            <family>Windows</family>
            <arch>x86</arch>
            <version>5.1.2600</version>
        </os>
        <property>
            <name>mavenVersion</name>
            <value>2.0.5</value>
        </property>
        <file>
            <exists>file2.properties</exists>
            <missing>file1.properties</missing>
        </file>
    </activation>
</profile>

这里有个问题是:多个激活条件之间是什么关系呢?

  • Maven 3.2.2 之前:遇到第一个满足的条件即可激活——的关系。
  • Maven 3.2.2 开始:各条件均需满足——的关系。

(2)命令行激活

列出活动的 profile

# 列出所有激活的 profile,以及它们在哪里定义
mvn help:active-profiles

指定某个具体 profile

mvn compile -P<profile id>

4、资源属性过滤

Maven 为了能够通过 profile 实现各不同运行环境切换,提供了一种『资源属性过滤』的机制。通过属性替换实现不同环境使用不同的参数。

(1)配置 profile

<profiles>
    <profile>
        <id>devJDBCProfile</id>
        <properties>
            <dev.jdbc.user>root</dev.jdbc.user>
            <dev.jdbc.password>atguigu</dev.jdbc.password>
            <dev.jdbc.url>http://localhost:3306/db_good</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>
                </resource>
            </resources>
        </build>
    </profile>
</profiles>

(2)创建待处理的资源文件

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

(3)执行处理资源命令

mvn clean resources:resources -PdevJDBCProfile

(4)找到处理得到的资源文件

images

5、延伸

我们时不时会在 resource 标签下看到 includes 和 excludes 标签。它们的作用是:

  • includes:指定执行 resource 阶段时要包含到目标位置的资源
  • excludes:指定执行 resource 阶段时要排除的资源

情看下面的例子:

<build>
    <resources>
        <resource>
            <!-- 表示为这里指定的目录开启资源过滤功能 -->
            <directory>src/main/resources</directory>

            <!-- 将资源过滤功能打开 -->
            <filtering>true</filtering>

            <includes>
                <include>*.properties</include>
            </includes>

            <excludes>
                <exclude>happy.properties</exclude>
            </excludes>
        </resource>
    </resources>
</build>

执行处理资源命令:

mvn clean resources:resources -PdevJDBCProfile

处理结果

image-20220915210927476

当然我们这里只是以 properties 文件为例,并不是只能处理 properties 文件。

6、环境资源隔离

(1)方法一

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <env>dev</env>
        </properties>
        <activation>
            <!--默认生效的配置组 打包需要改为false-->
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <env>prod</env>
        </properties>
        <activation>
            <!--默认生效的配置组 打包需要改为false-->
            <activeByDefault>false</activeByDefault>
        </activation>
    </profile>

</profiles>

application.properties 读取 pom.xml 文件的变量应该使用 ${变量名}

application.yml 读取 pom.xml 文件的变量应该使用 @变量名@

${变量名} 在yml中表示读取yml文件内部的变量值

spring:
  # 环境 dev|test|prod
  profiles:
    active: @env@

image-20221210233752206

(2)方法二

image-20221210233421210

<build>
    <resources>
        <resource>
            <directory>src/main/resources/</directory>
            <filtering>true</filtering>
            <!--打包时先排除掉所有环境的配置文件-->
            <excludes>
                <exclude>config/</exclude>
            </excludes>
        </resource>
        <resource>
            <!-- 在将指定环境的配置文件,加载到classes目录下 -->
            <directory>src/main/resources/config/${env}/</directory>
            <filtering>true</filtering>
            <targetPath>${project.build.directory}/classes</targetPath>
        </resource>
    </resources>
</build>

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <env>dev</env>
        </properties>
        <activation>
            <!--默认生效的配置组 打包需要改为false-->
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <env>prod</env>
        </properties>
        <activation>
            <!--默认生效的配置组 打包需要改为false-->
            <activeByDefault>false</activeByDefault>
        </activation>
    </profile>

</profiles>

(3)maven编译

mvn clean package -Pdev

八、jar包冲突问题

1、表现形式

抛异常:找不到类或方法

此时抛出的常见的异常类型:

  • java.lang.ClassNotFoundException:编译过程中找不到类

类在较低版本中没有,但在较高版本存在。

没报错但结果不对

发生这种情况比较典型的原因是:两个 jar 包中的类分别实现了同一个接口,这本来是很正常的。但是问题在于:由于没有注意命名规范,两个不同实现类恰巧是同一个名字。

4、解决办法

(1)手动解决

很多情况下常用框架之间的整合容易出现的冲突问题都有人总结过了,拿抛出的异常搜索一下基本上就可以直接找到对应的 jar 包。我们接下来要说的是通用方法。

不管具体使用的是什么工具,基本思路无非是这么两步:

  • 第一步:把彼此冲突的 jar 包找到
  • 第二步:在冲突的 jar 包中选定一个。具体做法无非是通过 exclusions 排除依赖,或是明确声明依赖。

(2)IDEA 的 Maven Helper 插件

这个插件是 IDEA 中安装的插件,不是 Maven 插件。它能够给我们罗列出来同一个 jar 包的不同版本,以及它们的来源。但是对不同 jar 包中同名的类没有办法。

(3)Maven 的 enforcer 插件

使用 Maven 的 enforcer 插件既可以检测同一个 jar 包的不同版本,又可以检测不同 jar 包中同名的类。

引入 netty 依赖

这里我们引入两个对 netty 的依赖,展示不同 jar 包中有同名类的情况。

<dependencies>
    <dependency>
        <groupId>org.jboss.netty</groupId>
        <artifactId>netty</artifactId>
        <version>3.2.10.Final</version>
    </dependency>

    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty</artifactId>
        <version>3.9.2.Final</version>
    </dependency>
</dependencies>

配置 enforcer 插件

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-enforcer-plugin</artifactId>
                <version>1.4.1</version>
                <executions>
                    <execution>
                        <id>enforce-dependencies</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>display-info</goal>
                            <goal>enforce</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.codehaus.mojo</groupId>
                        <artifactId>extra-enforcer-rules</artifactId>
                        <version>1.0-beta-4</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <rules>
                        <banDuplicateClasses>
                            <findAllDuplicates>true</findAllDuplicates>
                        </banDuplicateClasses>
                    </rules>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

测试

执行如下 Maven 命令:

mvn clean package enforcer:enforce

九、体系外jar包引入

目前来说我们在 Maven 工程中用到的 jar 包都是通过 Maven 本身的机制导入进来的。

而实际开发中确实有可能用到一些 jar 包并非是用 Maven 的方式发布,那自然也没法通过 Maven 导入。

此时如果我们能够拿到该 jar 包的源码那还可以自己建一个 Maven 工程,自己打包。可是如果连源码都没有呢?

1、将包安装到本地仓库

这里我们使用 install 插件的 install-file 目标:

mvn install:install-file -Dfile=[体系外 jar 包路径] \
-DgroupId=[给体系外 jar 包强行设定坐标] \
-DartifactId=[给体系外 jar 包强行设定坐标] \
-Dversion=1 \
-Dpackage=jar

例如(Windows 系统下使用 ^ 符号换行;Linux 系统用 \):

mvn install:install-file -Dfile=D:\idea2019workspace\atguigu-maven-outer\out\artifacts\atguigu_maven_outer\atguigu-maven-outer.jar ^
-DgroupId=com.atguigu.maven ^
-DartifactId=atguigu-maven-outer ^
-Dversion=1 ^
-Dpackaging=jar

再看本地仓库中确实有

在其它地方依赖这个 jar 包:

<dependency>
    <groupId>com.atguigu.maven</groupId>
    <artifactId>atguigu-maven-outer</artifactId>
    <version>1</version>
</dependency>

2、引用时使用本地路径

首先把jar包放到项目中:
在这里插入图片描述

然后在pom文件中引入:

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>sdk.core</artifactId>
    <version>3.3.1</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/aliyun-java-sdk-core-3.3.1.jar</systemPath>
</dependency>
1234567

注意:
1.添加 system
2.systemPath这个路径是jar包的路径。${project.basedir}只是一个系统自己的常量。
3.使用这种方式,在将项目用Maven打包是需要在 < plugin > 标签中加入:

<configuration>
    <includeSystemScope>true</includeSystemScope>
</configuration>

也就是:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <includeSystemScope>true</includeSystemScope>
    </configuration>
</plugin>

十、私服搭建:Nexus

1、安装

下载地址:https://download.sonatype.com/nexus/3/latest-unix.tar.gz

上传到 Linux 系统,解压后即可使用,不需要安装。但是需要注意:必须提前安装 JDK。

启动 Nexus

进入解压后的目录

./bin/nexus start

查看状态

./bin/nexus status

查看端口占用情况

netstat -anp | grep java

默认 8081

访问 Nexus 首页

首页地址:http://[Linux 服务器地址]:8081/

  • 用户名:admin
  • 密码:查看 /opt/sonatype-work/nexus3/admin.password 文件

初始化完成后需要选择登录方式

匿名登录,启用还是禁用?由于启用匿名登录后,后续操作比较简单,这里我们演示禁用匿名登录的操作方式:

image-20220915211632197

2、Nexus 上的各种仓库

image-20220915211816362

仓库类型 说明
proxy 某个远程仓库的代理
group 存放:通过 Nexus 获取的第三方 jar 包
hosted 存放:本团队其他开发人员部署到 Nexus 的 jar 包
仓库名称 说明
maven-central Nexus 对 Maven 中央仓库的代理
maven-public Nexus 默认创建,供开发人员下载使用的组仓库
maven-releasse Nexus 默认创建,供开发人员部署自己 jar 包的宿主仓库 要求 releasse 版本
maven-snapshots Nexus 默认创建,供开发人员部署自己 jar 包的宿主仓库 要求 snapshots 版本

初始状态下,这几个仓库都没有内容

3、通过 Nexus 下载 jar 包

先将本地仓库设置为空,修改maven配置文件

 <!-- 配置一个新的 Maven 本地仓库 -->
  <localRepository>D:/maven-repository-new</localRepository>

把我们原来配置阿里云仓库地址的 mirror 标签改成私服地址:

<mirror>
    <id>nexus-mine</id>
    <mirrorOf>central</mirrorOf>
    <name>Nexus mine</name>
    <url>http://ip:8081/repository/maven-public/</url>
</mirror>

这里的 url 标签是网页端点击仓库后面的 copy 来的

把上图中看到的地址复制出来即可。如果我们在前面允许了匿名访问,到这里就够了。但如果我们禁用了匿名访问,那么接下来我们还要继续配置 settings.xml:

<server>
  <id>nexus-mine</id>
  <username>admin</username>
  <password>atguigu</password>
</server>

这里需要格外注意:server 标签内的 id 标签值必须和 mirror 标签中的 id 值一样。

效果

找一个用到框架的 Maven 工程,执行命令:

mvn clean compile

下载后,Nexus 服务器上就会同步 jar 包了

4、将 jar 包部署到 Nexus

配置 Maven 工程

<distributionManagement>
    <snapshotRepository>
        <id>nexus-mine</id>
        <name>Nexus Snapshot</name>
        <url>http://192.168.198.100:8081/repository/maven-snapshots/</url>
    </snapshotRepository>
</distributionManagement>

这里 snapshotRepository 的 id 标签也必须和 settings.xml 中指定的 mirror 标签的 id 属性一致。

执行部署命令即可

mvn deploy

5、引用别人部署的 jar 包

  • 默认访问的 Nexus 仓库:maven-public
  • 存放别人部署 jar 包的仓库:maven-snapshots

配置 Maven 工程

<repositories>
    <repository>
        <id>nexus-mine</id>
        <name>nexus-mine</name>
        <url>http://ip:8081/repository/maven-snapshots/</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
        <releases>
            <enabled>true</enabled>
        </releases>
    </repository>
</repositories>

十一、maven-assembly-plugin

  1. 在spring boot项目中使用maven profiles和maven assembly插件根据不同环境打包成tar.gz或者zip
  2. 将spring boot项目中的配置文件提取到外部config目录中
  3. 将spring boot项目中的启动jar包移动到boot目录中
  4. 将spring boot项目中的第三方依赖jar包移动到外部lib目录中
  5. bin目录中是启动,停止,重启服务命令
  6. 打包后的目录结构类似于tomcat/maven目录结构

使用到的插件

  1. maven-assembly-plugin
  2. maven-jar-plugin
  3. spring-boot-maven-plugin
  4. maven-dependency-plugin
  5. maven-resources-plugin

1、maven-assembly-plugin

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.1.0</version>
    <configuration>
        <descriptors>
            <descriptor>src/main/assembly/assembly.xml</descriptor>
        </descriptors>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>

2、assembly.xml

<?xml version="1.0" encoding="UTF-8"?>
<assembly>
    <!-- 可自定义,这里指定的是项目环境 -->
    <!-- spring-boot-assembly-local-1.0.RELEASE.tar.gz  -->
    <!-- <id>${profileActive}-${project.version}</id>  -->
    <id>assembly</id>

    <!--是否添加根目录-->
    <includeBaseDirectory>true</includeBaseDirectory>
    <!-- 根目录的名称 -->
    <baseDirectory>${name}-${version}</baseDirectory>

    <!-- 打包的类型,如果有N个,将会打N个类型的包 -->
    <!-- 其他可选格式 gzip/zip/tar.gz/ -->
    <formats>
        <format>tar.gz</format>  
        <!--<format>zip</format>-->
    </formats>
    <dependencySets>
        <dependencySet>
            <outputDirectory>lib</outputDirectory>
        </dependencySet>
    </dependencySets>

    <fileSets>
        <!--
            0755->即用户具有读/写/执行权限,组用户和其它用户具有读写权限;
            0644->即用户具有读写权限,组用户和其它用户具有只读权限;
        -->
        <!-- 将src/bin目录下的所有文件输出到打包后的bin目录中 -->
        <fileSet>
            <directory>${basedir}/src/bin</directory>
            <outputDirectory>bin</outputDirectory>
            <fileMode>0755</fileMode>
            <includes>
                <include>**.sh</include>
                <include>**.bat</include>
            </includes>
        </fileSet>

        <!-- 指定输出target/classes中的配置文件到config目录中 -->
        <fileSet>
            <directory>${basedir}/target/classes</directory>
            <outputDirectory>config</outputDirectory>
            <fileMode>0644</fileMode>
            <includes>
                <include>application.yml</include>
                <include>application-${profileActive}.yml</include>
                <include>mapper/**/*.xml</include>
                <include>static/**</include>
                <include>templates/**</include>
                <include>*.xml</include>
                <include>*.properties</include>
            </includes>
        </fileSet>

        <!-- 将第三方依赖打包到lib目录中 -->
        <fileSet>
            <directory>${basedir}/target/lib</directory>
            <outputDirectory>lib</outputDirectory>
            <fileMode>0755</fileMode>
        </fileSet>

        <!-- 将项目启动jar打包到boot目录中 -->
        <fileSet>
            <directory>${basedir}/target</directory>
            <outputDirectory>boot</outputDirectory>
            <fileMode>0755</fileMode>
            <includes>
                <include>${project.build.finalName}.jar</include>
            </includes>
        </fileSet>

        <!-- 包含根目录下的文件 -->
        <fileSet>
            <directory>${basedir}</directory>
            <includes>
                <include>NOTICE</include>
                <include>LICENSE</include>
                <include>*.md</include>
            </includes>
        </fileSet>
    </fileSets>

</assembly>

3、

https://gitee.com/geekidea/spring-boot-assembly/blob/master/pom.xml


  目录