一. 概念

          Maven是一个用于项目构建和管理Jar包依赖的工具。

二. Maven的优势

          1. 项目构建。Maven整合了从清理、编译、测试到生成报告,再到打包和部署的全套自动化构建过程。

          2. Jar管理。通过Maven的坐标系统,在仓库中能轻松的定位到任何一个java类库,我们可以借助它来有序的管理复杂的jar依赖。

三. Maven与Gradle的区别

          1. 多模块构建

              在现在的项目中,我们往往会将项目拆分成多个模块,在Maven中需要定义一个parent的pom.xml作为一组module的通用                配置,子模块通过<module>进行定义。在gradle中也支持多模块构建,build.gradle中可以通过subproject或allproject来对                子模块进行一些依赖上的定义,针对子模块的声明则在settings.gradle中通过include语句来实现。

          2. 依赖管理系统

              在Maven中通过GroupId,ArtifactId以及Version组成一个唯一的标识,而Gradle支持动态的版本依赖,在版本号后使用+号                  就可以实现动态的版本管理。

          3. 一致的构建模型

              Maven设置了标准的项目周期,包含验证、初始化、生成原始数据、处理原始数据、生成资源、处理资源、编译、处理类                等等。而Gradle更加的灵活,可以创建一个task,随时通过depends建立与已有的task的依赖关系。

          4. 一致的项目结构

              Maven中指定了一套标准的项目结构作为java项目结构(src/main/java,src/main/resource,src/test/java,src/test/resource),                Gradle也使用了这套标准。在Gradle中只需要在文件中包含applyplugin:'java',系统即可自动识别。

          5. 插件机制

              两者都采用了插件机制。

四. Maven基本命令

          1. mvn clean: 清理。将根目录下的target目录清理掉。

          2. mvn compile: 编译。将项目中的.java文件编译成.class文件。

          3. mvn test: 测试。执行项目src/test/java路径下的单元测试类。

          4. mvn package: 打包。将项目打包并放置在项目根目录的.target目录下。

          5. mvn install: 把项目安装至本地仓库。

          6. mvn deploy: 将项目部署到私服环境中。

          7. mvn package -DskipTests: 打包并跳过测试。

mvn denpendency:tree: 查看依赖的树形结构,用于排除依赖冲突。

          9. mvn install:install-file -Dfile=Jar包的完整路径 -DgroupId=自定义groupId -DartifactId=自定义artifactId -Dversion=自定义                    version -Dpackaging=jar 将现有的jar包打包(上传)至本地仓库

         10.mvn deploy:deploy-file -Dfile=Jar包的完成路径 -DgroupId=自定义groupId -DartifactId=自定义artifactId -Dversion=自定义                version -Dpackaging=jar -Durl=http://ip:port/nexus/content/repositories/thirdparty/ -DrepositoryId=thirdparty                                      将本地已有的jar打包(上传)至私服

五. Maven的特性

     5.1 依赖管理

         1. 坐标

             以下是坐标相关的属性: 

             1. groupId: 项目所属组织的唯一标识,一般由域和公司名称组成

             2. artifactId: 项目名称

             3. version: 项目当前所处的版本号

             4. packaging: 项目的打包方式,默认为jar

             5. classifier: 用于指明同一个pom的不同构件。比如针对相同groupId,artifactId,version,可以通过指定不同的JDK版本或者                                  组成部分(如源代码source,文档doc,类文件等),以此来获取不同的构件。

          2. 依赖范围scope

             Maven在编译主代码时,需要使用一套classpath,测试代码时需要使用另一套classpath,运行代码时也会使用一套独立的               classpath。依赖范围就是用来控制依赖与这三种classpath。maven有以下几种依赖范围:

             1. compile: 编译依赖范围,默认使用该依赖范围。对编译、测试和运行时使用的三类classpath都有效。

             2. test: 测试依赖范围。只对测试的classpath有效,只在编译测试代码和运行测试单元时有效。

             3. provided: 已提供依赖的范围。在编译和测试的classpath中都有效,但在运行时无效。

             4. runtime: 运行时依赖。在测试和运行时有效,但在编译时无效。

             5. system: 系统依赖范围。与provided的依赖范围完全一致,必须通过systemPath元素显示的指定依赖文件的路径。

             6. import: 导入依赖范围。不对上述三种classpath产生任何影响。

          3. 依赖的传递性

              比如A依赖了B,B又依赖了C,则把B称作A的第一依赖,C称作A的第二依赖,C之所以能够被A依赖,是因为借助了依赖                的传递性。如下图所示,最左边一列表示第一直接依赖范围,最上方一行表示第二直接依赖范围。 

              注意: 当第一依赖是compile,第二依赖是runtime时,根据依赖的传递性,传递依赖的范围是runtime。因为C在B中时就没                         参与项目的编译,况且A使用的是B,所以C没有必要支持A项目的编译。

 

compile

test

provided

runtime

compile

compile

--

--

runtime

test

test

--

--

test

provided

provided

--

provided

provided

runtime

runtime

--

--

runtime

           4.  依赖调解

                项目A有这样的依赖关系: A->B->C->X(1.0),A->D->X(2.0),那么在项目A中,哪个版本的X会被Maven解析使用呢?

Maven依赖调解的第一原则: 路径最近者优先。因此上述问题中,X(2.0)将会被解析。

                若依赖路径的长度相同,比如A->C->X(1.0),A->D->X(2.0),依赖的长度相同,那么Maven会如何解析呢?

Maven依赖调解的第二原则: 第一声明者优先。上述问题中,如果C的依赖声明在D之前,则X(1.0)将会被解析。

           5. 可选依赖

               被 <optional>true</optional> 所修饰的依赖就是可选依赖。它们只对当前项目产生影响,不会被传递。

           6. 排除依赖

               依赖的传递性会为项目隐式的引入了许多依赖,虽然极大的简化了依赖管理,但有时候并非所有的依赖在当前项目中都被                 需要,此时我们就可以对传递而来的依赖进行排除。做法如下:

              <exclusions><exclusion>...</exclusion></exclusions>

     5.2 生命周期

         1. validate: 验证工程(项目)是否正确,所有的资源是否可用。

         2. compile: 编译项目的源代码。

         3. test: 使用合适的单元测试框架来已编译的源代码。测试时无需对项目进行打包和部署。

         4. package: 把已编译的代码打包成可以发布的格式,具体格式参考pom.xml文件中的packaging元素的值,默认打包成Jar。

         5. verify: 运行所有检查,验证包是否有效且达到了质量标准。

         6. install: 把包安装到Maven的本地仓库中,可以被本地其他工程作为依赖来使用。

         7.deploy: 在集成或发布环境下执行。将最终版本的包上传至远程私服的仓库中,供其他开发者的其他工程使用。

     5.3 仓库

         Maven仓库分为本地仓库和远程仓库,而远程仓库又可以被分成中央仓库、私服以及其他公共库。

         Maven中任何一个依赖、插件或者项目构建的输出,都可以被称为构件。任何一个构件都会有唯一的坐标,根据坐标可以定            义其在仓库中的唯一存储路径。Maven仓库是基于简单文件系统存储的。

         1. 本地仓库

             可以在Maven的配置文件settings.xml中进行如下配置:

<localRepository>本地仓库的绝对路径地址</localRepository>

         2. 远程仓库

             当在本地仓库内搜索不到依赖时,就需要访问远程仓库。远程仓库可以配置在某一个需要项目的pom.xml文件中,也可以                 配置在全局的settings.xml中。

            1. 在项目的pom.xml内配置远程仓库地址

<repositories>  
        <repository>  
            <id>jboss</id>  
            <name>JBoss Repository</name>  
            <url>http://repository.jboss.com/maven2/</url>  
            <releases>  
                <!-- Maven从远程仓库检查更新的频率(比较本地pom和远程pom的时间戳) 
                     daily:每天都检查一次(默认) never:从不检查 
                     always: 每次构件都检查更新 interval n:每隔n分钟检查一次-->
                <updatePolicy>daily</updatePolicy><!-- never,always,interval n -->  
                <enabled>true</enabled>  
                <!-- maven检查和检验文件的策略,warn为默认值,还可以选择ignore和fail,
                表示当maven检查检验和文件失败之后应该采取的行动 -->
                <checksumPolicy>warn</checksumPolicy><!-- fail,ignore -->  
            </releases>  
            <snapshots>  
                <!-- 是否从该仓库中获取不稳定的SNAPSHOT版本的构件 -->
                <enabled>false</enabled>  
            </snapshots>  
            <layout>default</layout>  
        </repository>  
</repositories>

                出于安全考虑,访问某些仓库时需要验证身份,而鉴权相关的代码只能在settings.xml中编写。我们可以打开                                    settings.xml,找到<servers></servers>标签,用以下方式书写鉴权代码:

<servers>
<server>
      <id>company-snapshots</id>
      <username>admin</username>
      <password>123456</password>
<server>
</servers>

            2. 在Maven的配置文件settings.xml中配置全局的远程仓库地址(所有项目共享)

                打开settings.xml,找到<profiles></profiles>标签,接着就可以书写<repositories>了,方法与在项目的pom.xml中一样。

         3. 私服

                 私服是一种特殊的远程仓库,为了节省网络带宽和构件的下载时间,有条件的情况下,建议在局域网内架设一个,通过                   私服来代理所有的远程仓库。

         4. 中央仓库

             Maven本身已经为我们配置好了中央仓库的地址,具体位置在$MAVEN_HOME/lib/maven-model-builder-3.6.3.jar,解压或直接反编译jar包,打开org.apache.maven.model包下的pom-4.0.0.xml,可以看到如下语句:

<repositories>
    <repository>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

             其中,https://repo.maven.apache.org/maven2就是中央仓库的远程访问地址。 

settings.xml文件的配置说明

             1. 声明规范 

                 xmlns用于声明命名空间,这就意味着,在<settings>标签内的元素都必须遵守命名空间给出的规范。

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

             2. localRepository

                 定义在settings标签内部。指明了本地仓库的物理路径。默认值为${user.home}/ .m2/repository

<localRepository>/Users/jack/repo</localRepository>

             3. interactiveMode

                定义在settings标签内部。maven是否需要和用户交互(一串提示),以便获得输入。默认值为false。

<interactiveMode>true</interactiveMode>

             4. usePluginRegistry

                 maven是否需要使用plugin-registry.xml来管理插件版本。若需要让maven使用${user.home}/ .m2/plugin-registry.xml来管                   理版本插件,则设置为true,默认值为false。

<usePluginRegistry>false</usePluginRegistry>

             5. offline

                 maven是否需要在离线模式下运行。如果构件系统需要在离线模式下运行,则设置为true,否则设置为false。比如由于                     网络原因或者安全因素,构件服务器无法连接远程仓库时,就可以使用这个属性。默认为false。

<offline>false</offline>

             6.  pluginGroups

                  

     5.4 pom.xml 配置

     5.5 plugins打包插件

         1. maven-shade-plugin

         2. maven-dependency-plugin

         3. maven-assembly-plugin

     5.6 常见内置变量

         ${basedir} 项目根目录

         ${project.build.directory} 构建目录,缺省为target

         ${project.build.outputDirectory} 构建过程输出目录,缺省为target/classes

         ${project.build.finalName} 产出物名称,缺省为${project.artifactId}-${project.version}

         ${project.packaging} 打包类型,缺省为jar

         ${project.xxx} 当前pom文件的任意节点的内容

六. 值得注意的问题

     1. 继承父项目的方式有两种,分别是通过<parent>和在<denpendencyManagement>中使用<scope>为import,<type>为<pom>实现多继承。值得注意的是,前者继承而来的依赖的版本号可以通过在父项目中声明<properties>标签内进行修改和覆盖,而后者则不允许这么做。比如当前项目是A,打算导入pom.xml文件的依赖是B,B中包含了C(版本1.0),而A中在<properties>标签内声明了C的版本号为2.0,且依赖了C(没有显示的写出版本号),那么运行的结果是,A项目依赖的是1.0版本的C,<properties>不会起作用!原因在于maven在扫描A的<denpendencyManagement>时,C会直接因为B而被构建完成(版本1.0),因为构建的顺序导致此时properties中的版本号声明不会起到任何作用。

         想要覆盖import而来的版本号的问题有两个解决办法:1. 在<denpendencyManagement>中重新声明依赖C并写明版本号。2. 在启项目启动时使用-DXXX.version=XXXX来指定版本号

   2 直接引用本地第三方jar包可以使用<systemPath>,但这样做不能将jar打包到最后的包中,因此还需要使用<includeSystemScope>true</includeSystemScope>

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