Maven

Maven

一.Maven简介

1.1 何为maven

Maven可翻译为"知识的积累" or"专家",是一款成功的开源跨平台的项目管理工具,无论小型的开源类库项目,还是大型的企业级应用;无论传统的瀑布式开发,还是流行的敏捷模式,Maven都能大显身手.

1.1.1 构建工具

​ 我们一直在不停的寻找避免重复的方法,设计的重复,编码的重复,文档的重复,当然还有构建的重复.Maven最大化的消除了构建的重复,抽象了构建生命周期,并且为绝大部分的构建任务提供了已实现的插件,我们不需要再定义过程,甚至不需要去实现这些过程中的一些任务,只需要遵循Maven中的约定.

​ 同时Maven帮助我们标准化构建过程,以前十个项目可能有十种构建方式,有了Maven后,所有项目的构建命令都是一直且简单的.因此Maven作为一个构建工具:

  1. 可以帮我们自动化构建,
  2. 可以帮我们抽象构建过程
  3. 提供构建任务是实现
  4. 跨平台
  5. 对外提供一直的操作接口

1.1.2 不仅仅是构建工具

​ Maven不仅是构建工具,还是一个依赖管理工具和项目信息管理工具,提供中央仓库来帮忙我们自动下载构建,通过引入一套经纬机制来系统准确的定位每一个构建(artifact).

1.1.3 Maven

​ 在Maven之前,有过程式的Make和Ant,开发者需要显示的指定每一个目标,以及完成该目标所需要执行的任务.针对每一个项目,开发者都需要重新编写这一过程,而其中就隐含着大量重复.

​ 而Maven是声明式的,项目构建过程和过程各个阶段所需的工作都由插件实现,并且大部分插件都是现成的,开发者只需要声明项目的基本元素,Maven就执行内置的,完整的构建过程.

二.Maven的使用

2.1 pom文件

​ Maven项目的核心是pom.xml,POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖等.

4.0.0:modelVersion指定了当前Pom模型的版本,固定为4.0.0

com.lsy:groupId定义了项目属于哪个组,这个组往往和项目所在的组织和公司相关

hello-world:artifactId定义了当前Maven项目在组中唯一的ID

1.0-SNAPSHOT:version指定了Hello World项目当前的版本,其中SNAPSHOT意为快照,说明该项目还处于开发中

Maven Hello World Project:name元素声明了一个对于用户更为友好的项目名称.非必须

​ 当运行mvn clean compile命令:clean告诉Maven清理输出目录target/,compile告诉Maven编译项目主代码,从输出中看到Maven首先执行clean:clean任务,删除target/目录(默认情况下,Maven构建的所有输出都在target目录中,接着执行resources:resources任务(未定义项目资源),最后执行compiler:compile任务,将项目主代码编译至target/classes目录

​ 其中clean:clean,resources:resources和compiler:compile对应了Maven插件及插件目标,比如clean:clean是clean插件的clean目标,compiler:compile是compiler插件的compile目标

2.2 测试

​ 首先添加Junit依赖

junit

junit

4.7

test

​ 其中scope元素为依赖范围,若依赖范围为test则表示该依赖只对测试有效,如果不声明依赖范围,则默认是compile,表示该依赖对主代码和测试代码均有效

当运行mvn clean test命令,Maven实际执行的不止test任务,而是clean:clean,resources:resources,compiler:compile,resources:testResources以及compiler:testCompile,即在执行测试之前,会先自动执行项目资源处理,主代码编译,测试资源处理,测试代码编译等工作,这是Maven生命周期的一个特性.

​ 由于Maven核心插件之一compiler插件默认支持Java1.3,因此需要配置插件支持Java1.8

org.apache.maven.plugins

maven-compiler-plugin

1.8

1.8

2.3 打包和运行

​ 将项目进行编译,测试之后,下一个重要的步骤就是打包(package),当没有指定打包类型时,默认打包类型是jar,当执行mvn clean package命令,Maven会在打包之前进行编译,测试等操作,之后通过jar:jar任务负责打包,实际上就是jar插件的jar目标将项目主代码打包成一个hello-world-1.0-SNAPSHOT.jar的文件

​ 当其他项目需要直接引用这个jar时,接下来需要一个安装的步骤,执行mvn clean install,此命令执行了安装任务install:install,将项目输出的jar安装到了Maven本地仓库中,而构件只有被下载到本地仓库后,才能由Maven项目使用.

2.4 使用Archetype生成项目骨架

​ mvn archetype:generate,当运行插件时,格式为:groupId:artifactId:version:goal,默认使用最新的稳定版

三. 坐标和依赖

3.1 坐标

  • Maven的一大功能还是管理项目依赖,为了能自动化解析任何一个Java构件,Maven就必须将它们唯一标识,这就依赖管理的底层基础---坐标
  • 在实际生活中,我们可以将地址看作是一种坐标,省,市,区,街道等一系列信息可以唯一标识城市中的任意居住地址,对应在Maven的世界中拥有非常巨大的构件,也就是平常使用的一些jar,war等文件
  • Maven定义了这样一规则:世界上任意一个构建都可以使用Maven坐标唯一标识,Maven坐标的元素包括

    • groupId:定义当前Maven项目隶属公司的实际项目
    • artifactId:该元素定义实际项目中的一个Maven模块,推荐做法使用实际项目名称为artifact的前缀,便于寻找实际构建
    • version:该元素定义Maven项目当前所处的版本
    • package:该元素定义Maven项目的打包方式,默认为jar包
    • classifier:该元素用来帮助定义构建输出一些附属构建,附属构件与主构件对应
org.sonatype.nexus  
nexus-indexer  
2.0.0  
jar   
1\. 以上5个元素,groupId,artifactId,version是必须定义的,packaging是可选的,而classifier是不能直接定义的  
2\. 项目构件的文件名格式:artifactId-version\[-classifier\].packaging

3.2 依赖

3.2.1 依赖的配置

  • 项目要引用Maven中的构建,就需要在pom文件中,通过坐标来使用依赖,在根元素project下的dependencies可以包含一个或多个dependency元素,用以声明一个或多个项目依赖

    • groupId,artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的
    • type:依赖的类型,对应项目坐标定义的packaging,一般不必声明,默认为jar
    • scope:依赖的范围
    • optional:标记依赖是否可选
    • exclusions:用来排除传递性依赖

3.2.2 依赖的范围

  • Maven项目在编译项目主代码时需要使用一套classpath,如编译项目主代码时需要使用spring-core,该文件以依赖的方式被引入到classpath中.其次,Maven在编译和执行测试的时候会使用另外一套classpath,依赖同样会引入到相应的classpath中,最后在运行Maven项目时,又会使用一套classpath
  • 依赖范围就是用来控制依赖与三种classpath(编译classpath,测试classpath,运行classpath)的关系

    • compile:编译依赖范围,没有指定时,为默认依赖范围.使用此依赖范围的Maven依赖,对于编译,测试运行三种classpath都有效,典型的例子为:spring-core,在编译,测试,运行阶段都需要使用该依赖
    • test:测试依赖范围,使用此依赖范围的Maven依赖,只对于测试的classpath有效,在编译主代码或者运行项目时将无法使用此类依赖,典型的例子就是JUnit,它只有在编译器测试代码及运行测试的时候才需要
    • provided:已提供依赖范围,使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效,典型的例子就是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复引入了
    • runtime:运行时依赖范围,使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效,典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或运行项目的时候才需要实现上述接口的具体JDBC驱动
    • system:系统依赖范围,该依赖与三种classpath的关系,和provided依赖范围完全一致,但是,使用system范围的依赖时必须通过systemPath元素显示地指定依赖文件的路径,由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植性


      javax.sql
      jdbc-stdext
      2.0
      system
      ${java.home}/lib/rt.jar

    • import:导入依赖范围,该依赖范围不会对三种classpath产生实际的影响,主要用于导入其他pom文件中的dependencyManagement元素对于依赖版本约束的内容

3.2.3 传递性依赖

  • 在使用Maven依赖时,如Spring Framework,此依赖又会依赖其他的开源库,因此实际中往往会下载一个很大的如spring-framework-2.5.6-with-dependencies.zip包,这样往往就引入了很多不必要的依赖,而Maven的传递性依赖机制就可以很好的解决这一问题

    • 当A项目引入一个compile范围的B依赖,而B依赖中有一个compile范围的C依赖,那么C依赖同样会成为A的compile范围依赖

      image.png

  • 传递性依赖和依赖范围

    • 假设A依赖于B,B依赖于C,那么A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖
    • 当第二直接依赖是compile的时候,传递性依赖与第一直接依赖范围一致
    • 当第二直接依赖是test的时候,依赖不会得以传递
    • 当第二直接依赖是provided的时候,只传递第一直接依赖范围为provided的依赖,且传递性依赖范围为provided
    • 当第二直接依赖是runtime的时候,传递性地依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime

Maven_第1张图片

3.2.4 可选依赖

  • 假如有这样一个依赖关系,A依赖于B,B依赖于X和Y,B对于X和Y的依赖都是可选依赖,根据传递性依赖的定义,如果这三个依赖的范围都是compile,那么X,Y就是A的compile范围传递性依赖,但是由于X,Y都是可选依赖,所以依赖不会得以传递,因此X,Y不会对A有任何影响

    Maven_第2张图片

    • 为什么会有可选依赖这一特性呢?

      • 当B实现了两个特性,特性一依赖于X,特性二依赖于Y,并且这两个特性是互斥的,用户不可能同时使用两个特性,比如B是一个持久层隔离工具包,支持多种数据库,在使用这个工具包的时候,只会依赖一种数据库
  
  
   
 mysql  
 mysql-connector-java  
 5.6.0  
 true  
   
   
 postgresql  
   
 8.4-701.jdbc3  
 true  
   


*   以上使用元素表示两个依赖为可选依赖,他们只会对当前项目B产生影响,当其他项目依赖于B时,这两个依赖不会被传递,所以当A依赖于B项目时,如果要使用mysql数据库,那么需要显式的声明mysql-connector-java这一依赖
    
*   关于可选依赖,在理想的情况下,是不应该使用可选依赖的,使用可选依赖的原因是某一个项目中实现了多个特性,而根据单一职责原则,应该针对不同的特性分别创建一个Maven项目,用户根据需要选择使用其中某一个依赖
    

3.2.5 排除依赖

  • 传递性依赖会给项目隐式的引入很多依赖,这极大的简化了项目依赖的管理,但是有时候这种特性䧥带来问题

    • 例如,当前项目有一个第三方依赖,而这个依赖由于某些原因依赖了另一个类库的SNAPSHOT,那么整个SNAPSHOT就会成为当前项目的传递性依赖,而SNAPSHOT的不稳定性会直接影响到当前的项目,这时候就需要排除掉该SNAPSHOT,并且在当前项目中声明该类库的某个正式发布版



      com.lsy.myproject
      myproject-a
      1.0.0


      com.lsy.myproject
      myproject-b




      com.lsy.myproject
      myproject-b
      1.1.0

3.2.6 归类依赖

  • 当一些依赖来自同一个项目的不同模块,这些依赖的版本都应该是相同的,将来升级也是一起升级,如Spring Framework,这时可以使用properties元素定义Maven属性


    4.2.1




    org.springframework
    spring-core
    ${springframework.version}


    org.springframework
    spring-beans
    ${springframework.version}


    org.springframework
    spring-context
    ${springframework.version}


    org.springframework
    spring-context-support
    ${springframework.version}

3.2.7 优化依赖

  • 在软件开发过程中,通常会通过重构等方式不断优化自己代码,同样,对于Maven项目的依赖也需要对其进行优化

    • 去除多余的依赖
    • 显示声明某些必要的依赖
  • Maven会自动解析所有项目的直接依赖和间接依赖,并且根据规则判断每个依赖范围,对于一些依赖冲突,也能进行调整,以确保任何一个构建只有唯一的版本在依赖中存在,此称为解析依赖

    • mvn dependency:list 查看当前项目的已解析依赖
    • mvn dependency:tree 以树结构查看已解析依赖
    • mvn dependency:analyze 解析依赖

      • Used undeclared dependencies:指项目中使用到的,但是没有显示声明的依赖,这种依赖意味着潜在的风险,当前项目直接在使用它们,所以需要显示声明任何项目中直接用到的依赖
      • Unused declared dependencies:指项目中未使使用的.但显示声明的依赖

四. 仓库

  • 之前已经介绍了Maven的坐标和依赖,坐标和依赖是任何一个构件在Maven世界中的逻辑表示方式,而构建的物理表示方式是文件,Maven通过仓库来同一管理这些文件
  • 在Maven世界中,任何一个以依赖,插件或项目构建的输出,都可以成为构件

    • 例如:log4j-1.2.15.jar,maven-compiler-plugin-2.0.2.jar或项目打包后的myproject-1.0.0-SNAPSHOT.jar
  • 在一台工作站上,可能会有几十个项目,所有项目在/lib目录下都会有自己所需的依赖包,而这些依赖中都有大量的重复,每个项目各自存储自己所需的依赖包不仅造成磁盘空间的浪费,而且也难于同一管理,文件的复制等操作
  • 得益于坐标机制,任何Maven项目使用任何一个构建的方式都是相同的,因此,Maven可以在某个位置统一存储所有Maven项目共享的构件,这个统一的位置就是仓库,为了实现重用,项目构建完毕后生成的构建也可以安装或部署到仓库中

4.1 仓库的布局

  • 任何一个构建都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径,这就是Maven的仓库布局方式,如log4j:log4j:1.2.15这一依赖,其对应的仓库路径为log4j/log4j/1.2.15/log4j-1.2.15.jar

    • 路径与坐标的关系为:groupId/artifactId/version/artifactId-verision.packaging

4.2 仓库的分类

  • 对于Maven来说,仓库分为两类

    • 本地仓库
    • 远程仓库

      • 中央仓库:Maven核心自带的仓库服务器,它包含了绝大部分开源的构件
      • 私服:另一种特殊的远程仓库,为了节省宽带和时间,应该在局域网内部构建一个私有仓库服务器,用其代理所有外部的远程仓库,内部项目部署到私服上供其它项目使用
      • 其他公共库
  • 当Maven根据坐标寻找构件时,它首先会查看本地仓库,如果本地仓库存在此构件,则直接使用,如果本地仓库不存在此构件,或者需要查看是否有更新的构件版本,Maven就会去远程仓库查找,发现需要的构件后下载到本地仓库再使用,若本地和远程都没有,则报错

4.3 本地仓库

  • 一般来说,在Maven项目目录下,没有注入lib/这样用来存放依赖文件的目录,当Maven执行编译或测试时,如果需要使用依赖文件,它总是基于坐标使用本地仓库的依赖文件

    • 默认情况,在用户目录下路径名为.m2/repository/的仓库目录,当想自定义本地仓库目录地址时,可以编辑~/.m2/setting.xml,设置localRepository元素来指定仓库地址


      D:/java/repository/

    • 默认情况下,~/.m2/settings.xml文件是不存在的,用户需要从Maven安装目录复制$M2_HOME/conf/settings.xml文件再编辑,建议不要直接修改全局目录的settings.xml,而是在用户目录下进行修改
  • 一个构建只有在本地仓库中,才能由其它Maven项目使用

    1. 依赖Maven从远程仓库下载到本地仓库中
    2. 将本地项目的构件安装到Maven本地仓库中 mvn clean install

4.4 远程仓库

  • 安装好Maven后,如果不执行任何Maven命令,本地仓库目录是不存在的,只有输入第一条Maven命令后,Maven才会创建本地仓库,并根据配置和需要,从远程仓库下载至本地仓库

    • 这就好比藏书,本地仓库好比书房,远程仓库好比书店,我需要读书时先去书房找,当书房没有时,就去书店买回放到书房,并且一般对每个人来说,书房只有一个,而外面的书店可以有多个

4.4.1 中央仓库

  • 最原始的本地仓库是空的,所以Maven必须知道至少一个可用的远程仓库,才能在执行Maven命令时下载到需要的构建,中央仓库就是这样一个默认的远程仓库,可以通过解压工具打开$M2_HOME/lib/maven-model-builder-3.0.jar中的org/apache/maven/model/pom-4.0.0.xml文件



    central
    Maven Repository Switchboard
    http://repo1.maven/org/maven2<;/url>
    default

    false


    • 这段配置是所有Maven项目都会继承的超级Pom文件,这段配置使用id central对中央仓库进行唯一标识,其名称为Maven Repository Switchboard,它使用default仓库布局,也就是在之前介绍的仓库布局,最后snapshots元素表示不从该仓库下载快照版

4.4.2 私服

  • 私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,供局域网内的Maven用户使用,当Maven需要下载构建的时候,它从私服请求,如果私服上不存在该构件,则从外部的远程仓库下载,缓存在私服上之后,再为Maven的下载提供服务

    • 此外,一些无法从外部下载的构件也可以从本地上传到私服上供大家使用
  • 私服的好处

    • 节省外网带宽:建立私服同样可以减少组织自己的开支,大量的对于外部仓库的重复请求会消耗很大的带宽,利用私服代理外部仓库之后,对外的重复构件下载便可以消除,即降低外网带宽的压力
    • 加速Maven构件:不停的连接请求外部仓库是十分耗时的,但Maven的一些内部机制(如快照更新检查井)要求Maven在执行构建时不停的检查远程仓库数据,因此,当项目配置很多外部仓库时,构建速度就会降低
    • 部署第三方构件:当某个构件无法从任何一个外部远程仓库获取,建立私服之后,便可以将这些构件部署到这个内部的仓库中,供内部的Maven项目使用
    • 提高稳定性,增强控制:Maven构建高度依赖远程仓库,因此,大哥Internet不稳定的时候,Maven构建也会变得不稳定,甚至无法构建,使用私服后,即是短暂时没有Internet连接,由于私服中有大量缓存,Maven依然可以正常运行,并且私服中有很多额外的权限功能控制
    • 降低中央仓库的负荷:每天中央仓库都需要面对大量的下载请求,使用私库可以降低对于中央仓库的负荷
  • 远程仓库的配置

    • 很多情况下,默认的中央仓库无法满足项目的需求,可能项目需要的构件存在于另一个远程仓库中,可以通过Pom/settings文件中来配置该仓库

      POM文件

      .....


      jboss
      JBoss Repository
      http://repository.jboss.com/m...;/url>

      true


      false

      default




      settings文件


      dev

      true



      jboss
      JBoss Repository
      http://repository.jboss.com/m...;/url>

      true


      false

      default



      • 在repositories元素下,可以使用repository子元素声明一个或多个远程仓库,并且声明一个id,任何一个仓库声明的id必须是唯一id
      • Maven自带的中央仓库使用的id为central,如果其他仓库使用该id,则会覆盖中央仓库的配置,该配置中的url指向了仓库的地址
      • 该配置中的release和snapshots元素用来控制Maven对于发布版构件和快照版构件的下载,对于release和snapshots元素来说除了enabled子元素,还有updatePolicy和checksumPolicy元素

        • updatePolicy:用来配置Maven从远程仓库检查更新的频率,默认为daily

          • daily:每天
          • never:从不
          • always:每次
          • interval :X :每隔X分钟一次
        • checksumPolicy:配置Maven检查文件失败时的策略,默认为warn

          • fail:Maven遇到验证和错误就让构建失败
          • warn:Maven遇到验证和错误就发出警告信息
          • ignore:Maven遇到验证和错误时完全忽略
          
          
         true  
         daily  
         ignore  
        
        
    
*   远程仓库的验证
    
    *   大部分远程仓库无需认证就可以访问,但出于安全考虑,我们需要提供一些认证信息才能访问一些远程仓库
        
    *   配置认证信息和配置仓库信息不同,仓库信息可以直接配置在POM文件中,但是认证信息必须配置在settings.xml文件中
        
    *   其中server元素的id必须与POM文件中需要认证的repository元素的id完全一致
        
          
          
         ......  
           
           
         my-project  
         user  
         password  
           
           
         .......  
        
        
    
*   部署至远程仓库
    
    *   私服一大作用就是部署第三方构件,包括组织内部生成的构件以及无法从外部仓库直接获取的构件,无论是日常开发中生成的构件,还是正式版本发布的构件,都需要部署到仓库中
        
        *   distributionManagement包含repository和snapshotRepository子元素,前者表示发布版构件的仓库,后者表示快照版的仓库
            
        *   往远程仓库部署构件时,往往需要认证,认证配置需在settings.xml中创建一个server元素,其id与仓库的id匹配,无论从远程仓库下载构件,还是部署构件至远程仓库,当需要认证时,配置方式都是一样的
            
        
          
         在POM文件中配置distributionManagement   
          
         .....  
           
           
         jboss-release  
         JBoss Release Repository  
         http://192.168.1.99/content/repository/jboss-release  
           
           
         jboss-snapshots  
         JBoss Snapshots Repository  
         http://192.168.1.99/content/repository/jboss-snapshots  
           
           
         .....  
        
        

4.5 快照

  • 在Maven的世界中,任何一个项目或者构件都必须有自己的版本,版本的值可能是1.0.0,1.3-alpha-4,2.0,2.1-SNAPSHOT或者2.1-20191214.221414-13,其中1.0.0,1.3-alpha-4和2.0是稳定的发布版本,而2.1-SNAPSHOT和2.1-20091214.221414-13是不稳定的快照版本
  • 对于一个稳定的版本,如果仓库中已经包含,那么Maven就不会再去对照远程仓库进行更新,除非每次执行Maven命令前,清除本地仓库中等待稳定版本,而对于一个正在迭代的项目,如果要实时更新版本的内容就需要频繁的修改新的版本名称,这样是对版本号的滥用
  • 针对这种情况,使用快照版时,Maven会自动为构件打上时间戳,因此,Maven就能随时找到仓库中该构建最新版本的文件,一旦有新的更新,就会去同步到本地仓库.当项目经过完善的测试后需要发布的时候,再将快照版本更改为发布版本
  • 快照版本只应该在组织内部的项目或模块之间依赖使用,因为这时,组织对这些快照版本的依赖具有完全的理解和控制权,项目不应该依赖任何组织外部的快照版本依赖,由于快照版本的不稳定性,随时可能发生变化,这样的依赖会有潜在的危险

4.6 从仓库解析依赖的机制

  • 当本地仓库没有依赖构件的时候,Maven会自动从远程仓库下载,当依赖版本为快照版本时,Maven会自动找到最新的快照,这背后的依赖机制可以概括如下

    1. 当依赖的范围时system的时候,Maven直接从本地文件系统解析构件
    2. 根据依赖坐标计算仓库路径后,尝试从本地仓库寻找构件,如果发现相应的构件,则解析成功
    3. 在本地仓库不存在相应构件的情况下,如果依赖的版本时显式的发布版本构件,如1.2,2.1-beta-1等,则遍历所有的远程仓库,发现后,下载并解析使用
    4. 如果依赖的版本时RELEASE或LATEST,则基于更新策略读取所有远程仓库的元数据groupId/artifact/maven-metadata.xml,将其与本地仓库的对应元数据合并后,计算出RELEASE或LATEST真实的值,然后基于这个真实的值检查本地仓库和远程仓库
    5. 如果依赖的版本是SNAPSHOT,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/maven-metadata.xml,将其与本地仓库的对应数据合并后,得到最新的快照版本的值,然后基于该值检查本地仓库,或者从远程仓库下载
    6. 如果最后解析得到的构件版本是时间戳格式,如1.4.1-20191104.121455-8,则复制其时间戳格式的文件至非时间戳格式,如SNAPSHOT,并使用该非时间戳格式的构件

      基于groupId和artifactId的maven-metadata.xml


      org.sonatype.nexus
      nexus

      1.4.2-SNAPSHOT
      1.3.7

      1.3.5
      1.3.6
      1.3.7
      1.4.0-SNAPSHOT
      1.4.1-SNAPSHOT
      1.4.2-SNAPSHOT

      20191214221133

  • 该XML文件列出了仓库中存在的构件所有可用的版本,同时latest元素指向了这些版本中最新的那个版本1.4.2-SNAPSHOT,而release元素指向了这些版本中最新的发布版本1.3.7,Maven通过合并多个远程仓库及本地仓库的元数据,就能计算出基于所有仓库的latest和release

    • 需要注意的是,在依赖声明使用LATEST和RELEASE是不推荐的做法,因为Maven随时可能解析到不同的构件,且Maven不会明确告诉用户这样的变化

4.7 镜像

  • 如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像,由于地理位置的因素,中央仓库的下载速度会比较慢,这时我们可以配置Maven使用镜像来代替中央仓库,编辑settings.xml


    ......


    maven.net.cn
    one of the central mirror in china
    http://maven.net.cn/content/g...;/url>
    central


    ......

    • 的值为central,表示该配置为中央仓库的镜像,任何对于中央仓库的请求都会转至该镜像,用户也可以使用同样的方法配置其他仓库的镜像,另外三个元素id,name,url与一般仓库配置无异,表示该镜像仓库的唯一标识,名称以及地址

      • 若该镜像要进行验证,即基于该id配置仓库认证
  
  
 .....  
   
   
 internal-repository  
 Internal Repository Mananger  
 http://192.168.1.100/maven2/  
 \*  
   
   
 .....  

  • 以上mirrorOf元素的值为*,表示该配置是所有Maven仓库的镜像,对于任何远程仓库的请求都会被转至该指定的仓库,如果镜像仓库需要验证,则配置一个id为internal-repository的即可

    • *:匹配所有远程仓库
    • external:*:匹配所有远程仓库,使用localhost的除外,使用file://协议的除外,也就是说,匹配所有不在本机上的远程仓库
    • repo1,repo2:匹配仓库repo1和repo2,用逗号分隔多个仓库
    • *,! repo1:匹配所有的远程仓库,除repo1外,使用感叹号将仓库从匹配中排除
  • 需要注意的是,由于镜像仓库完全屏蔽了被镜像仓库,当镜像仓库不稳定或停止服务时,Maven仍将无法访问被镜像仓库,因此无法下载构件

五. 生命周期和插件

5.1 Maven的生命周期

  • 在Maven出现之前,项目构建的生命周期就已经存在,但是不同的公司和开发人员虽然同样在做构件工作,其对于不同的项目却不能够重用,只能重新定制开发,而Maven的生命周期就是为了对所有的构件过程进行抽象和统一

    • 这个生命周期包含了项目的清理,初始化,编译,测试,打包,集成测试,验证,部署和站点生成等几乎所有的构件步骤,也就是说几乎所有项目的构件,都能映射到这样一个生命周期上
  • Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,在Maven的设计中,实际的任务(如编译主代码)都交由插件完成,这种思想和设计模式的模方法类似,在父类中定义算法的整体结构,子类可以通过实现或重写父类的方法来控制实际的行为

    public abstract class Template{
    public void build(){
    initialize();
    compile();
    test();
    packagee();
    integrationTest();
    deploy();
    }

    protect abstract void initialize();
    protect abstract void compile();
    protect abstract void test();
    protect abstract void packagee();
    protect abstract void integrationTest();
    protect abstract void deploy();
    }

    • 在Maven的生命周期中抽象了各个步骤,定义了他们的次序,但是没有提供具体的实现,而通过插件机制为每个构件步骤绑定一个或多个插件行为,而且Maven为大多数构件步骤都绑定了默认的插件,例如,针对编译的maven-compiler-plguin,针对测试的maven-surefire-plugin等
    • Maven定义的生命周期和插件机制一方面保证了所有Maven项目有一致的构件标准,另一方面又通过默认的插件简化和稳定实际项目的构件,此外,该机制还提供了足够的扩展,用户可以通过配置现有的插件或自定义插件来自定义构件行为
  • 三套声明周期

    • Maven拥有三套相互独立的生命周期,他们分别为clean,default和site,每个生命周期包含一些阶段,这些阶段是有序的,并且后面的阶段依赖于前面的阶段,但是三套声明周期本身是互相独立的

      1. clean生命周期:清理项目

        • pre-clean:执行一些清理前需要完成的工作
        • clean:清理上次构件生成的文件
        • post-clean:执行一些清理后需要完成的工作
      2. default声明周期:定义了真正的构件所需执行的所有步骤

        • validate
        • initialize
        • generate-sources
        • process-sources:处理项目主资源文件,一般来说针对/src/main/resources目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中
        • compile:编译项目的主源码,一般来说针对/src/main/java目录下的Java文件至目录输出的主classpath目录中
        • process-classes
        • generate-test-sources
        • process-test-sources:处理项目测试资源文件,一般来说针对/src/test/resources目录的内容进行变量替换工作后,复制到项目输出的测试classpath目录
        • test-compile:编译项目的测试代码,一般来说针对/src/test/java目录下的java文件至输出的测试classpath目录中
        • test:使用单元测试框架运行测试,测试代码不会被打包或部署
        • prepare-package
        • package:接收编译好的代码,打包成可发布的格式,如jar
        • pre-integration-test
        • integration-test
        • post-integration-test
        • verify
        • install:将包安装到Maven本地仓库,供本地其他Maven项目使用
        • deploy:将最终的包复制到远程仓库,供其他开发人员和Maven项目使用
      3. site声明周期:建立和发布项目站点,Maven能够基于POM所包含的信息,自动生成一个友好的站点供交流和发布项目信息

        • pre-site:执行一些在生成项目站点之前需要完成的工作
        • site:生成项目站点文档
        • post-site:执行一些在生成项目站点后需要完成的工作
        • site-deploy:将生成的项目站点发布到服务器上
    • 命令行与生命周期

      • 从命令行执行Maven任务最主要方式就是调用Maven的生命周期,各个生命周期是相互独立的,而生命周期的阶段是有前后依赖关系的

        • mvn clean:该命令调用clean生命周期的clean阶段,实际执行阶段为pre-clean和clean
        • mvn test:该命令调用default生命周期的test阶段,实际执行的阶段为default生命周期的validate,initialize直到test的所有阶段,这也解释为什么在执行测试的时候,项目代码能够自动编译
        • mvn clean install:该命令调用clean生命周期的clean阶段和default生命周期的install阶段
        • mvn clean deploy site-deploy:该命令调用clean生命周期的clean阶段,default生命周期的deploy阶段和site生命周期的site-deploy阶段

5.2 插件

5.2.1 插件目标和绑定

  • Maven的核心仅仅定义了抽象的生命周期,具体的任务是交由插件完成的,插件以独立的构件形式存在

    • 对于插件本身而言,为了能够复用代码,它往往能够完成多个任务,为每个功能编写一个插件显然不合理,因为这些任务背后有大量可复用的代码,因此,这些功能聚集到一个插件里面,每个功能就是一个插件目标
  • Maven的生命周期和插件相互绑定,用以完成实际的构件任务,具体而言,是生命周期的阶段与插件的目标相互绑定,以完成某个具体的构件任务

    • 例如:编译这一任务对应了default生命周期的compile这一阶段,而maven-compiler-plugin这一插件的compile目标能完成此任务

Maven_第3张图片

Maven_第4张图片

  • 自定义绑定

    • 除了内置绑定以外,用户还能够自己选择将某个插件目标绑定到生命周期的某个阶段上,这种自定义绑定方式能让Maven项目在构件过程中执行更多更丰富的任务

      • 当需要创建项目的源码jar包,maven-source-plugin可以帮我们完成任务,但是内置的插件绑定关系中并没有涉及这一任务,因此需要自行配置,它的jar-no-fork目标能将项目主代码打包成jar文件,可以将其绑定到default生命周期的verify阶段,在执行完集成测试和安装构件之前创建源码jar包




        org.apache.maven.plugins
        maven-source-plugin
        2.1.1


        attach-sources
        verify

        jar-no-fork





      • 在POM的build元素下的plugins子元素中声明插件的使用,上例用到了是maven-source-plugin,其groupId为org.apache.maven.plugins,用户总是应该声明一个非快照版本,这样可以避免由于插件版本变化造成的构件不稳定性

        • 除了基本的插件坐标声明外,还有插件执行配置,executions下每个execution子元素可以用来配置执行一个任务,该例配置了一个id为attach-sources的任务,通过phase配置将其绑定到verify生命周期阶段上,再通过goals配置指定要执行的插件目标,在运行mvn verify时就会执行该任务
        • 有时候.即是不通过phase元素配置生命周期阶段,插件目标也能绑定到生命周期中去,原因是:有很多插件的目标在编写时就已经定义了默认绑定阶段,可以使用maven-help-plugin查看详细信息

          • mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:2.1 -Ddetail

            • Bound to phase : package 默认绑定到package生命周期
      • 当插件目标被绑定到不同生命周期阶段的时候,其执行顺序会由生命周期阶段的先后顺序决定,如果多个目标被绑定到同一个阶段,他们的顺序就由插件声明的先后顺序决定

5.2.2 插件配置

  • 用户可以通过配置插件目标的参数,进一步调整插件目标所执行的任务,几乎所有Maven插件的目标都可以配置参数,用户可以通过命令行和POM配置等方式来配置这些参数

    • 命令行插件配置

      • 在日常的Maven使用中,我们通常从命令行输入并执行Maven命令,很多插件目标的参数都支持从命令行配置,用户可以在Maven命令中使用-D参数,并伴随一个参数键=参数值的形式,来配置插件目标的参数

        • mvn install -Dmaven.test.skip = ture:给maven.surefire-plugin提供一个maventest.skip参数,当参数为true时,就会跳过执行测试

          • 参数-D是Java自带的,其功能是通过命令行设置一个Java系统属性,Maven简单地重用了该参数,在准备插件的时候检查系统属性来实现插件参数的配置
    • POM中插件全局配置

      • 并不是所有的插件参数都适合用命令行配置,有些参数的值从项目创建到项目发布都不会改变,或者说很少改变,这种情况下在POM文件中一次性配置比重复在命令行输入更合理

        • 用户可以在声明插件的时候,对插件进行一个全局的配置,所有基于该插件的目标任务,都会使用这些配置,如:maven-compiler-plugin来编译1.8版本的源文件,生成与JVM1.8兼容的字节码文件




          org.apache.maven.plugins
          maven-compiler-plugin
          2.1

          1.8
          1.8



5.2.3 获取插件信息

  • 当遇到一个构建任务时,不仅需要知道去哪里找到合适的插件,还需要详细的了解插件的配置点,由于Maven的插件非常多,其中大部分没有完善的文档,因此,通过帮助命令来了解插件显得非常重要

    1. 使用maven-help-plugin描述插件

      • 除了访问在线的插件文档外,可以借助maven-help-plugin来获取插件的详细信息

        • mvn help:decribe -Dplugin = org.apache.maven.plugins:maven-compiler-plugin:2.1

          • 这里执行的是maven-help-plugin的decribe目标,在参数plugin中输入需要描述插件的groupId,artifactId和version,Maven在命令行的输出maven-compiler-plugin的简要信息,包括插件的坐标,目标前缀和目标等信息
      • 在描述插件时,可以省去版本信息,Maven会自动获取最新版本来进行表述,并可以用目标前缀来替换坐标

        • mvn help:describe -Dplugin=compiler
      • 如果仅仅描述某个插件目标的信息,则加上goal参数,更详细的信息,则加上detail参数

        • mvn help:describe -Dplugin=compiler -Dgoal=compile -Ddetail

5.2.4 插件解析机制

  • 为了方便用户使用和配置插件,Maven不需要用户提供完整的插件坐标信息,就可以解析得到正确的插件

    • 插件仓库

      • 与依赖构件一样,插件构件同样基于坐标存储在Maven仓库中,在需要的时候Maven先从本地仓库寻找,如果不存在,则从远程仓库查找,找到插件之后,再下载到本地仓库使用
      • 不同于repositories以及repository子元素,插件的远程仓库使用pluginRepositories和pluginRepository配置



        central
        Maven Plugin Resository
        http://repo1.maven.org/maven2<;/url>
        default

        false


        true
        never


  • 插件默认的groupId

    • 在POM中配置插件时,如果该插件时Maven的官方插件(即groupId为org.apache.maven.plugins),就可以省略groupId,Maven在解析该插件的时候,会自动使用默认groupId不起




      maven-compiler-plugin
      2.1

      1.8
      1.8



  • 解析插件版本

    • 同样为了简化插件的配置和使用,在用户没有提供插件版本的情况下,Maven会自动解析插件版本

      • 首先Maven在超级POM中为所有核心插件设定了版本,超级POM是所有Maven项目的父POM,所有项目都继承这个超级POM配置,因此即使用户不加任何配置,Maven在使用核心插件时,它的版本就已经确定了
      • 如果用户使用的某个插件没有设定版本,并且这个插件也不属于核心插件,Maven机会去检查所有仓库中可用的版本,通过仓库元数据groupId/artifactId/maven-metadata.xml文件,如maven-compiler-plugin插件为例,它在org/apache/maven/plugins/maven-compiler-plugin/maven-metadata.xml



        org.apache.maven.plugins
        maven-compiler-plugin

        2.1
        2.1

        2.0-beta-1
        2.1
        2.2
        2.3

        20190102092331

      • Maven遍历本地仓库和所有远程仓库,将仓库元数据合并后,就能计算出latest和release的值,maven3将版本解析到最新的非快照版,如2.1

5.2.5 解析插件前缀

  • Maven命令支持使用插件前缀来简化插件的使用,插件前缀和groupId:artifact是一一对应的,这种匹配关系存储在仓库元数据中,与之前提到的groupId/artifactId/maven-metadata.xml不同,这里仓库元数据为groupId/maven-metadata.xml,一般插件都位于/org/apache/maven/plugins/和org/code-haus/mojo/,可以通过settings.xml配置让Maven检查其他groupId上的插件仓库元数据



    com.my.plugins

  • 在仓库的元数据文件中可以看到插件的前缀定义




    Maven Clean Plugin
    clean
    maven-clean-plugin


    Maven Compiler Plugin
    compile
    maven-compile-plugin


    Maven Dependency Plugin
    dependency
    maven-dependency-plugin


六.聚合与继承

  • Maven的集合特性能够把项目各个模块聚合在一起构建,而Maven的继承特性则能帮助抽泣各模块相同的依赖和插件等配置,在简化POM的同时,还能促进各个模块配置的一致性

6.1 集合

  • aggregator

    4.0.0
    com.lsy.project
    project-aggregator
    1.0.0-SNAPSHOT
    pom
    Aggregator


    project-email
    project-register

    • 对于聚合模块,其打包方式packaging的值必须为pom
    • 通过modules元素来实现聚合,用户可以通过在一个打包方式为pom的Maven项目中声明任意数量的module元素来实现模块的聚合,这里每个module的值都是一个当前POM的相对目录

      • 如aggregator的POM路径为.../project-aggregator/pom.xml,那么project-email对应 的目录为.../project-aggregator/project-email/,并且目录中包含了pom.xml,src/main/java,src/test/java等内容,离开了project-aggregator也能独立创建=
    • 聚合模块和其他模块的目录结构并非一定要父子关系,也可以是平行关系


      ../prject-email
      ../project-register

    • 当在聚合模块中执行mvn clean install时,Maven首先解析聚合模块的POM,分析要构件的模块,并计算出一个反应堆构件顺序(Reactor Build Order),然后根据这个顺序构件各个模块
    • 聚合模块的构件顺序

      • Maven按序读取POM文件,如果POM没有依赖模块,那么就构件模块,否则就先构件其依赖模块

6.2 继承

  • 从以上的聚合模块和其他模块中可以看到,多个被管理模块的POM文件中会有大量重复的相同配置,他们有相同的groupId和version,相同的依赖,相同的插件配置,而通过POM文案的继承可以消除重复

    • project-parent父模块

      4.0.0
      com.lsy.project
      project-parent
      1.0.0-SNAPSHOT
      pom
      Parent

      • 父模块的POM文件使用与其他模块一直的groupId和version,它的packaging方式为pom,与聚合模块一致
      • 父模块主要是为了消除配置的重复,因此它本身不包含除POM文件之外的项目文件,也就不需要src/main/java之类的文件夹了
    • project-email子模块


      com.lsy.project
      project-parent
      1.0.0-SNAPSHOT
      ../project-parent/pom.xml


      project-email
      Email


      .....

      • 使用parent元素声明父模块,parent下子元素groupId,artifactId,version指定父模块的坐标,元素relativePath表示父模块POM的相对路径,表示Email模块和其父模块是在平行的目录下

        • 当构件项目时,Maven会首先根据relativePath检查父POM文件,如果找不到再从本地仓库找,relativePath的默认值为../pom.xml,也就是说,Maven默认父POM在上一层目录
      • 子模块没有groupId和version,是因为子模块隐式的从父模块继承了这两个元素,从而消除了不必要的配置
  • 可继承的POM元素

    • groupId:项目组ID,项目坐标的核心元素
    • version:项目版本,项目坐标的核心元素
    • description:项目的描述信息
    • organization:项目的组织信息
    • inceptionYear:项目的创始年份
    • url:项目的URL地址
    • develops:项目的开发者信息
    • contributors:项目贡献者信息
    • distributionManagement:项目的部署配置
    • issueManagement:项目的缺陷跟踪系统信息
    • ciManagement:项目的持续集成系统信息
    • scm:项目的版本控制系统信息
    • mailingList:项目的邮件列表信息
    • properties:自定义属性
    • dependencies:项目的依赖配置
    • dependencyManagement:项目的依赖管理配置
    • repositories:项目的仓库配置
    • pluginRepositories:项目的插件仓库配置
    • build:包括项目的源码目录配置,输出目录配置,插件配置,插件管理配置等
    • reporting:项目的输出目录配置,报告插件配置等
  • 依赖管理

    • dependencies元素是可以被继承的,说明依赖是会被继承的,所以我们可以将子模块共有的依赖配置到父模块中,子模块就可以移除这些依赖,简化配置
    • 上述方法是可行的,但是可能将来会有新的模块并不需要父模块中的一些依赖,这就会产生不合理的现象,从而Maven提供了dependencyManagement元素,既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性

      • 在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用
    4.0.0  
    com.lsy.project  
    project-parent  
    1.0.0-SNAPSHOT  
    pom  
    Parent  
     4.3.1  
      
       
       
     org.springframework  
     spring-core  
     ${springframwork.version}  
       
       
     org.springframework  
     spring-beans  
     ${springframwork.version}  
       
       
     org.springframework  
     spring-context  
     ${springframwork.version}  
       
       
    
    
    *   这里使用dependencyManagement声明的依赖既不会给parent模块引入依赖,也不会给子模块引入依赖,不过这段配置是会被继承的
        
    *   子模块POM
        
          
          
         com.lsy.project  
         project-parent  
         1.0.0-SNAPSHOT  
         ../project-parent/pom.xml  
        project-email  
        Email  
           
         org.springframework  
         spring-core  
           
           
         org.springframework  
         spring-beans  
           
           
         org.springframework  
         spring-context  
           
         
        
        *   使用这种依赖管理机制,可以在父POM中使用dependencyManagement声明依赖能够统一项目规范中的依赖版本,当版本在父POM中声明之后,子模块使用依赖时就不需要声明了.也不会发生多个子模块使用依赖版本不一致的情况
            
            *   scoper元素的import依赖范围只在dependencyManagement元素下才有效果,使用该范围的依赖通常指向一个POM文件,作用是将POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中
                
            *   所以除了复制配置和继承父模块这两种方式外,还可以通过import范围依赖导入这一配置
                
            
              
              
               
             com.lsy.project  
             project-parent  
             1.0-SNAPSHOT  
             pom  
             import  
               
            
            
  • 插件管理

    • Maven提供了dependencyManagement元素帮助管理依赖,类似的,Maven也提供了pluginManagement元素管理插件,该元素中配置的依赖同样不会造成实际的插件调用行为,而POM文件中配置了真正的plugin元素,并且groupId和artifact一致时,才会产生实际的插件行为
    • 父模块





      org.apache.maven.plugins
      maven-source-plugin
      3.1.1


      attach-source
      verify

      jar-no-fork






    • 子模块




      org.apache.maven.plugins
      maven-source-plugin


      • 子模块使用了maven-source-plugin插件,同时又继承了父模块的pluginManagement配置
  • 集合与继承的关系

    • 多模块的聚合与继承其实是两个概念,其目的是完全不同的,前者为了方便快速的构件项目,后者主要为了消除重复配置

      • 对于聚合模块来说:它知道有哪些模块被聚合,但是那些被聚合的模块并不知道这个聚合模块的存在
      • 对于继承关系的父POM来说:它不知道有哪些子模块继承于它,但是那些子模块都必须知道自己的父模块是什么
    • 在实际项目中,一个POM往往即是聚合模块,又是父模块
  • 约定优于配置

    • Maven提倡"约定优于配置",Maven只需要一个简单的POM文件,就可以完成清除,构件等任务

      • 源码目录:src/main/java/
      • 编译输出目录为:target/classes/
      • 打包方式为:jar
      • 包输出目录为:target/
    • Maven此机制的来源就是超级POM文件,此文件在$MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml路径下



      central
      Maven Repository Swithboard
      http://repo1.maven.org/maven2<;/url>
      default

      false







      central
      Maven Plugin Repository
      http://repo1.maven.org/maven2<;/url>
      default

      false


      never






      定义了项目的主输出目录
      ${project.basedir}/target
      主代码输出目录
      ${project.build.directory}/classes
      最终构件的名称格式
      ${project.artifactId}-${project.version}
      测试代码输出目录
      ${project.build.directory}/test-
      classes

      主源码目录
      ${project.basedir}/src/main/java
      脚本源码目录
      src/main/scripts
      测试源码目录
      ${project.basedir}/src/test/java
      主资源目录


      ${project.basedir}/src/main/resources





      ${project.basedir}/src/test/resources

      核心插件版本



      maven-antrun-plugin
      1.3


      maven-assembly-plugin
      2.3


      maven-clean-plugin
      2.3


      maven-compiler-plugin
      2.0.3

      ......


七. Nexus私服

7.1 Nexus内置的仓库

  • Nexus仓库有四种类型

    1. group:仓库组
    2. hosted:宿主仓库
    3. proxy:代理仓库
    4. virtual:虚拟仓库

Maven_第5张图片


*   Maven可以从宿主仓库下载构件,Maven也可以从代理仓库下载构件,而代理仓库会简介的从远程仓库下载并缓存构件
    
*   而一般为了方便,我们会从仓库组下载构件,而仓库组没有实际的内容,它只是管理一组实际的仓库,当它接收到请求时,它会转向其包含的宿主仓库或代理仓库获得实际构件的内容
    
  • 仓库的Policy属性

    • Release:发布版
    • Snapshot:快照版

7.2 配置Maven从Nexus下载构件

  • 在POM文件中配置仓库和插件仓库------只对当前项目有效


    .......


    nexus
    Nexus
    http://localhost:8081/nexus/content/groupId/public/

    true





    nexus
    Nexus
    http://localhost:8081/nexus/content/groupId/public/

    true


    true



    .......

  • 在settings.xml文件中配置---全局有效

    • settings.xml文件并不支持直接配置repositories和pluginRepositories,但是可以通过Maven提供的Profile机制,让用户将仓库配置到settings.xml中的Profile中
  
 ....  
   
   
 nexus  
   
   
 nexus  
 Nexus  
 http://localhost:8081/nexus/content/groupId/public/  
   
 true  
   
   
 ture  
   
   
   
   
   
   
 nexus  
 Nexus  
 http://localhost:8081/nexus/content/groupId/public/  
   
 true  
   
   
 true  
   
   
   
   
   
   
  激活指定id的profile   
   
 nexus  
   

  • 以上配置已经能让Maven项目从Nexus私服下载构件了,但是Maven仍然会去访问中央仓库,现在我们想要将所有请求都仅仅通过私服,这就需要借助于Maven镜像了


    .....


    nexus
    *
    http://localhost:8081/nexus/content/groupId/public/



    nexus


    central
    central
    http://central<;/url>

    true


    ture




    central
    Nexus
    http://central<;/url>

    true


    true




    激活指定id的profile

    nexus

    • 这里仓库和插件仓库配置的id都为central,它们将会覆盖POM中央仓库的配置,并且这里的url已经无关紧要,因为所有的请求都会通过镜像访问私服地址

7.3 部署构件至Nexus

  • 如果只是为了代理外部公共仓库,那么Nexus的代理仓库就已经完全足够了,对于另一类Nexus仓库---宿主仓库来说,他们主要作用是存储组织内部的,或一些无法从公共仓库获得的第三方构件,用户可以通过配置Maven自动部署构件至Nexus的宿主仓库

    • 使用Maven部署构件至Nexus

      • 日常开发生成的快照版本构件可以直接部署到Nexus中策略为Snapshot的宿主仓库中,项目正式发布的构件则应该部署到Nexus中策略为Release的宿主仓库中
      • POM文件配置


        .....


        nexus-releases
        Nexus Release Repository
        http://localhost:8081/nexus/content/repositories/
        release/



        nexus-snapshots
        Nexus Snapshots Repository
        http://localhost:8081/nexus/content/repositories/
        snapshots



        .....

      • Nexus的仓库对于匿名用户只是可读的,为了能够部署构件,还需要在settings.xml中配置认证信息


        ....


        nexus-releases
        admin
        admin123


        nexus-snapshots
        admin
        admin123


        ....

八. Profile

  • 一个优秀的构件系统必须足够灵活,它应该能够让项目在不同的环境下都能够成功的构件,例如:开发环境,测试环境和产品环境,这些环境的数据库配置不尽相同,那么项目构建时就需要能够识别其所在的环境并正确使用配置

8.1 属性


4.3.1





org.springframework
spring-core
${springframwork.version}


org.springframework
spring-beans
${springframwork.version}


  • 这是最常见的Maven属性使用的方式,通过properties元素使得用户自定义一个或多个Maven属性,然后在POM的其他地方使用${属性名称}的方式来引用该属性,这种做法的最大意义是消除重复
  • Maven的属性有六类

    1. 内置属性

      1. ${basedir}:表示项目根目录,即包含pom.xml文件的目录
      2. ${version}:表示项目的版本
    2. POM属性:用户可以使用该类属性引用POM文件中对应元素的值

      • ${project.artifactId}:对应元素的值

        • ${project.build.sourceDirectory}:项目的主源码目录.默认为/src/main/java/
        • ${project.build.testSourceDirectory}:项目的测试代码源码目录.默认为/src/test/java/
        • ${project.build.directory}:项目构件输出目录.默认为/target
        • ${project.build.outputDirectory}:项目主代码编译输出目录.默认为/target/classes
        • ${project.build.testOutputDirectory}:项目测试代码编译输出目录.默认为/target/test-classes
        • ${project.build.groupId}:项目的groupId
        • ${project.build.artifactId}:项目的artifactId
        • ${project.build.build.finalName}:项目打包输出文件的名称,默认为${project.artifactId}

          -${project.version}

    3. 自定义属性:元素下自定义的Maven属性
    4. settings属性:与POM属性同理,用户使用settings.来引用,如:${settings.localRepository}指向用户本地仓库的地址
    5. Java系统属性:所有Java系统属性都可以使用Maven属性引用,如${user.home}指向用户目录

      • 可以通过mvn help:system查看所有的java系统属性
    6. 环境变量属性:所有环境变量都可以用env.来引用,例如:${env.JAVA_HOME}

      • 可以通过 mvn help:system查看所有的环境变量

8.2 资源过滤

  • 在不同的开发环境中,项目的源码应该使用不同的方式进行构件,最常见的就是数据库的配置了,在开发中,有些项目会在src/main/resources/目录下放置不同环境下的数据库配置

    database.jdbc.driverClass = com.mysql.jdbc.Driver
    database.jdbc.connectionURL = jdbc:mysql://localhost:3306/dev
    database.jdbc.username = dev
    database.jdbc.password = dev-passwd

    database.jdbc.driverClass = com.mysql.jdbc.Driver
    database.jdbc.connectionURL = jdbc:mysql://localhost:3306/test
    database.jdbc.username = test
    database.jdbc.password = test-passwd

  • 为了应对环境的变化,我们可以使用Maven属性将这些会发生变化的部分提取出来

    database.jdbc.driverClass = ${db.driver}
    database.jdbc.connectionURL = ${db.url}
    database.jdbc.username = ${db.username}
    database.jdbc.password = ${db.password}

  • 在settings.xml中通过profile定义不同环境下的配置数据



    dev

    com.mysql.jdbc.Driver
    jdbc:mysql://localhost:3306/dev
    dev
    dev-passwd


    test

    com.mysql.jdbc.Driver
    jdbc:mysql://localhost:3306/test
    test
    test-passwd


    • 需要注意的是:

      • Maven属性默认只会在POM文件中才会被解析,也就是说${db.username}放到POM文件中会变成dev或test,但是在src/main/resources/目录下仍然还是${db.username},因此,需要让Maven解析资源文件中的Maven属性
      • 资源文件处理的是maven-reosurces-plugin做事,它默认的行为只是将项目主资源文件复制到主代码编译输出目录
  • 开启资源过滤

    • Maven默认的主资源目录和测试目录的定义是在超级POM文件中
  
   
 ${project.basedir}/src/main/resources  
 true  
   
  
  
   
 ${project.basedir}/src/test/resources  
 true  
   

  • 通过mvn clean install -Pdev : 通过mvn 的 -P参数表示在命令行激活一个profile=dev的profile

8.3 Profile

  • 激活Pofile

    1. 命令行激活

      • 用户可以使用mvn命令行参数-P 加上profile的id来激活profile,多个id之间逗号分隔

        • mvn clean install -Pdev,-Ptest
    2. settings文件显示激活

      • 如果用户希望某个profile默认一直处于激活状态,就可以配置settings.xml文件的activeProfile元素,表示其配置的profile对所有项目都处于激活状态


        .....

        dev

        .....

    3. 系统属性激活

      • 用户可以配置当前某系统属性存在时,自动激活profile





        test


        .....

      • 用户可以配置当前某系统属性存在且等于a时,自动激活profile





        test
        a


        .....

      • 用户可以在命令行声明系统属性

        • mvn clean install -Dtest=x
    4. 操作系统环境变量激活

      • Profile还可以根据操作系统环境激活





        Windows10


        .....

    5. 文件存在与否激活

      • Maven可以根据项目中某个文件是否存在来决定是否激活profile





        a.properties
        b.properties


        .....

    6. 默认激活

      • 用户可以在定义profile的时候指定其默认激活



        dev

        true

        .....

  • 以上激活优先级从上之下依次减小

    • 可以通过mvn help:active-profiles来查看当前激活的profile
    • 可以通过mvn help:all-profiles来查看所有的profile
  • profile的种类

    • 根据具体的需要,可以在一下位置声明profile

      Maven

一.Maven简介

1.1 何为maven

Maven可翻译为"知识的积累" or"专家",是一款成功的开源跨平台的项目管理工具,无论小型的开源类库项目,还是大型的企业级应用;无论传统的瀑布式开发,还是流行的敏捷模式,Maven都能大显身手.

1.1.1 构建工具

​ 我们一直在不停的寻找避免重复的方法,设计的重复,编码的重复,文档的重复,当然还有构建的重复.Maven最大化的消除了构建的重复,抽象了构建生命周期,并且为绝大部分的构建任务提供了已实现的插件,我们不需要再定义过程,甚至不需要去实现这些过程中的一些任务,只需要遵循Maven中的约定.

​ 同时Maven帮助我们标准化构建过程,以前十个项目可能有十种构建方式,有了Maven后,所有项目的构建命令都是一直且简单的.因此Maven作为一个构建工具:

  1. 可以帮我们自动化构建,
  2. 可以帮我们抽象构建过程
  3. 提供构建任务是实现
  4. 跨平台
  5. 对外提供一直的操作接口

1.1.2 不仅仅是构建工具

​ Maven不仅是构建工具,还是一个依赖管理工具和项目信息管理工具,提供中央仓库来帮忙我们自动下载构建,通过引入一套经纬机制来系统准确的定位每一个构建(artifact).

1.1.3 Maven

​ 在Maven之前,有过程式的Make和Ant,开发者需要显示的指定每一个目标,以及完成该目标所需要执行的任务.针对每一个项目,开发者都需要重新编写这一过程,而其中就隐含着大量重复.

​ 而Maven是声明式的,项目构建过程和过程各个阶段所需的工作都由插件实现,并且大部分插件都是现成的,开发者只需要声明项目的基本元素,Maven就执行内置的,完整的构建过程.

二.Maven的使用

2.1 pom文件

​ Maven项目的核心是pom.xml,POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖等.

4.0.0:modelVersion指定了当前Pom模型的版本,固定为4.0.0

com.lsy:groupId定义了项目属于哪个组,这个组往往和项目所在的组织和公司相关

hello-world:artifactId定义了当前Maven项目在组中唯一的ID

1.0-SNAPSHOT:version指定了Hello World项目当前的版本,其中SNAPSHOT意为快照,说明该项目还处于开发中

Maven Hello World Project:name元素声明了一个对于用户更为友好的项目名称.非必须

​ 当运行mvn clean compile命令:clean告诉Maven清理输出目录target/,compile告诉Maven编译项目主代码,从输出中看到Maven首先执行clean:clean任务,删除target/目录(默认情况下,Maven构建的所有输出都在target目录中,接着执行resources:resources任务(未定义项目资源),最后执行compiler:compile任务,将项目主代码编译至target/classes目录

​ 其中clean:clean,resources:resources和compiler:compile对应了Maven插件及插件目标,比如clean:clean是clean插件的clean目标,compiler:compile是compiler插件的compile目标

2.2 测试

​ 首先添加Junit依赖

junit

junit

4.7

test

​ 其中scope元素为依赖范围,若依赖范围为test则表示该依赖只对测试有效,如果不声明依赖范围,则默认是compile,表示该依赖对主代码和测试代码均有效

当运行mvn clean test命令,Maven实际执行的不止test任务,而是clean:clean,resources:resources,compiler:compile,resources:testResources以及compiler:testCompile,即在执行测试之前,会先自动执行项目资源处理,主代码编译,测试资源处理,测试代码编译等工作,这是Maven生命周期的一个特性.

​ 由于Maven核心插件之一compiler插件默认支持Java1.3,因此需要配置插件支持Java1.8

org.apache.maven.plugins

maven-compiler-plugin

1.8

1.8

2.3 打包和运行

​ 将项目进行编译,测试之后,下一个重要的步骤就是打包(package),当没有指定打包类型时,默认打包类型是jar,当执行mvn clean package命令,Maven会在打包之前进行编译,测试等操作,之后通过jar:jar任务负责打包,实际上就是jar插件的jar目标将项目主代码打包成一个hello-world-1.0-SNAPSHOT.jar的文件

​ 当其他项目需要直接引用这个jar时,接下来需要一个安装的步骤,执行mvn clean install,此命令执行了安装任务install:install,将项目输出的jar安装到了Maven本地仓库中,而构件只有被下载到本地仓库后,才能由Maven项目使用.

2.4 使用Archetype生成项目骨架

​ mvn archetype:generate,当运行插件时,格式为:groupId:artifactId:version:goal,默认使用最新的稳定版

三. 坐标和依赖

3.1 坐标

  • Maven的一大功能还是管理项目依赖,为了能自动化解析任何一个Java构件,Maven就必须将它们唯一标识,这就依赖管理的底层基础---坐标
  • 在实际生活中,我们可以将地址看作是一种坐标,省,市,区,街道等一系列信息可以唯一标识城市中的任意居住地址,对应在Maven的世界中拥有非常巨大的构件,也就是平常使用的一些jar,war等文件
  • Maven定义了这样一规则:世界上任意一个构建都可以使用Maven坐标唯一标识,Maven坐标的元素包括

    • groupId:定义当前Maven项目隶属公司的实际项目
    • artifactId:该元素定义实际项目中的一个Maven模块,推荐做法使用实际项目名称为artifact的前缀,便于寻找实际构建
    • version:该元素定义Maven项目当前所处的版本
    • package:该元素定义Maven项目的打包方式,默认为jar包
    • classifier:该元素用来帮助定义构建输出一些附属构建,附属构件与主构件对应
    org.sonatype.nexus
    nexus-indexer
    2.0.0
    jar
    
     
    1. 以上5个元素,groupId,artifactId,version是必须定义的,packaging是可选的,而classifier是不能直接定义的
    2. 项目构件的文件名格式:artifactId-version[-classifier].packaging

3.2 依赖

3.2.1 依赖的配置

  • 项目要引用Maven中的构建,就需要在pom文件中,通过坐标来使用依赖,在根元素project下的dependencies可以包含一个或多个dependency元素,用以声明一个或多个项目依赖

    • groupId,artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的
    • type:依赖的类型,对应项目坐标定义的packaging,一般不必声明,默认为jar
    • scope:依赖的范围
    • optional:标记依赖是否可选
    • exclusions:用来排除传递性依赖

3.2.2 依赖的范围

  • Maven项目在编译项目主代码时需要使用一套classpath,如编译项目主代码时需要使用spring-core,该文件以依赖的方式被引入到classpath中.其次,Maven在编译和执行测试的时候会使用另外一套classpath,依赖同样会引入到相应的classpath中,最后在运行Maven项目时,又会使用一套classpath
  • 依赖范围就是用来控制依赖与三种classpath(编译classpath,测试classpath,运行classpath)的关系

    • compile:编译依赖范围,没有指定时,为默认依赖范围.使用此依赖范围的Maven依赖,对于编译,测试运行三种classpath都有效,典型的例子为:spring-core,在编译,测试,运行阶段都需要使用该依赖
    • test:测试依赖范围,使用此依赖范围的Maven依赖,只对于测试的classpath有效,在编译主代码或者运行项目时将无法使用此类依赖,典型的例子就是JUnit,它只有在编译器测试代码及运行测试的时候才需要
    • provided:已提供依赖范围,使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效,典型的例子就是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复引入了
    • runtime:运行时依赖范围,使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效,典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或运行项目的时候才需要实现上述接口的具体JDBC驱动
    • system:系统依赖范围,该依赖与三种classpath的关系,和provided依赖范围完全一致,但是,使用system范围的依赖时必须通过systemPath元素显示地指定依赖文件的路径,由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植性

      
          javax.sql
          jdbc-stdext
          2.0
          system
          ${java.home}/lib/rt.jar
      
    • import:导入依赖范围,该依赖范围不会对三种classpath产生实际的影响,主要用于导入其他pom文件中的dependencyManagement元素对于依赖版本约束的内容

3.2.3 传递性依赖

  • 在使用Maven依赖时,如Spring Framework,此依赖又会依赖其他的开源库,因此实际中往往会下载一个很大的如spring-framework-2.5.6-with-dependencies.zip包,这样往往就引入了很多不必要的依赖,而Maven的传递性依赖机制就可以很好的解决这一问题

    • 当A项目引入一个compile范围的B依赖,而B依赖中有一个compile范围的C依赖,那么C依赖同样会成为A的compile范围依赖

      1590662084736

  • 传递性依赖和依赖范围

    • 假设A依赖于B,B依赖于C,那么A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖
    • 当第二直接依赖是compile的时候,传递性依赖与第一直接依赖范围一致
    • 当第二直接依赖是test的时候,依赖不会得以传递
    • 当第二直接依赖是provided的时候,只传递第一直接依赖范围为provided的依赖,且传递性依赖范围为provided
    • 当第二直接依赖是runtime的时候,传递性地依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime

1590662084736

3.2.4 可选依赖

  • 假如有这样一个依赖关系,A依赖于B,B依赖于X和Y,B对于X和Y的依赖都是可选依赖,根据传递性依赖的定义,如果这三个依赖的范围都是compile,那么X,Y就是A的compile范围传递性依赖,但是由于X,Y都是可选依赖,所以依赖不会得以传递,因此X,Y不会对A有任何影响

    1590662084736

    • 为什么会有可选依赖这一特性呢?

      • 当B实现了两个特性,特性一依赖于X,特性二依赖于Y,并且这两个特性是互斥的,用户不可能同时使用两个特性,比如B是一个持久层隔离工具包,支持多种数据库,在使用这个工具包的时候,只会依赖一种数据库

    
        mysql
        mysql-connector-java
        5.6.0
        true
    
    
        postgresql
        
        8.4-701.jdbc3
        true
    
  • 以上使用元素表示两个依赖为可选依赖,他们只会对当前项目B产生影响,当其他项目依赖于B时,这两个依赖不会被传递,所以当A依赖于B项目时,如果要使用mysql数据库,那么需要显式的声明mysql-connector-java这一依赖
  • 关于可选依赖,在理想的情况下,是不应该使用可选依赖的,使用可选依赖的原因是某一个项目中实现了多个特性,而根据单一职责原则,应该针对不同的特性分别创建一个Maven项目,用户根据需要选择使用其中某一个依赖

3.2.5 排除依赖

  • 传递性依赖会给项目隐式的引入很多依赖,这极大的简化了项目依赖的管理,但是有时候这种特性䧥带来问题

    • 例如,当前项目有一个第三方依赖,而这个依赖由于某些原因依赖了另一个类库的SNAPSHOT,那么整个SNAPSHOT就会成为当前项目的传递性依赖,而SNAPSHOT的不稳定性会直接影响到当前的项目,这时候就需要排除掉该SNAPSHOT,并且在当前项目中声明该类库的某个正式发布版

      
          
              com.lsy.myproject
              myproject-a
              1.0.0
              
                  
                      com.lsy.myproject
                      myproject-b
                  
              
          
          
              com.lsy.myproject
              myproject-b
              1.1.0
          
      

3.2.6 归类依赖

  • 当一些依赖来自同一个项目的不同模块,这些依赖的版本都应该是相同的,将来升级也是一起升级,如Spring Framework,这时可以使用properties元素定义Maven属性

    
        4.2.1
    
    
    
        
            org.springframework
            spring-core
            ${springframework.version}
        
        
            org.springframework
            spring-beans
            ${springframework.version}
        
        
            org.springframework
            spring-context
            ${springframework.version}
        
        
            org.springframework
            spring-context-support
            ${springframework.version}
        
    

3.2.7 优化依赖

  • 在软件开发过程中,通常会通过重构等方式不断优化自己代码,同样,对于Maven项目的依赖也需要对其进行优化

    • 去除多余的依赖
    • 显示声明某些必要的依赖
  • Maven会自动解析所有项目的直接依赖和间接依赖,并且根据规则判断每个依赖范围,对于一些依赖冲突,也能进行调整,以确保任何一个构建只有唯一的版本在依赖中存在,此称为解析依赖

    • mvn dependency:list 查看当前项目的已解析依赖
    • mvn dependency:tree 以树结构查看已解析依赖
    • mvn dependency:analyze 解析依赖

      • Used undeclared dependencies:指项目中使用到的,但是没有显示声明的依赖,这种依赖意味着潜在的风险,当前项目直接在使用它们,所以需要显示声明任何项目中直接用到的依赖
      • Unused declared dependencies:指项目中未使使用的.但显示声明的依赖

四. 仓库

  • 之前已经介绍了Maven的坐标和依赖,坐标和依赖是任何一个构件在Maven世界中的逻辑表示方式,而构建的物理表示方式是文件,Maven通过仓库来同一管理这些文件
  • 在Maven世界中,任何一个以依赖,插件或项目构建的输出,都可以成为构件

    • 例如:log4j-1.2.15.jar,maven-compiler-plugin-2.0.2.jar或项目打包后的myproject-1.0.0-SNAPSHOT.jar
  • 在一台工作站上,可能会有几十个项目,所有项目在/lib目录下都会有自己所需的依赖包,而这些依赖中都有大量的重复,每个项目各自存储自己所需的依赖包不仅造成磁盘空间的浪费,而且也难于同一管理,文件的复制等操作
  • 得益于坐标机制,任何Maven项目使用任何一个构建的方式都是相同的,因此,Maven可以在某个位置统一存储所有Maven项目共享的构件,这个统一的位置就是仓库,为了实现重用,项目构建完毕后生成的构建也可以安装或部署到仓库中

4.1 仓库的布局

  • 任何一个构建都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径,这就是Maven的仓库布局方式,如log4j:log4j:1.2.15这一依赖,其对应的仓库路径为log4j/log4j/1.2.15/log4j-1.2.15.jar

    • 路径与坐标的关系为:groupId/artifactId/version/artifactId-verision.packaging

4.2 仓库的分类

  • 对于Maven来说,仓库分为两类

    • 本地仓库
    • 远程仓库

      • 中央仓库:Maven核心自带的仓库服务器,它包含了绝大部分开源的构件
      • 私服:另一种特殊的远程仓库,为了节省宽带和时间,应该在局域网内部构建一个私有仓库服务器,用其代理所有外部的远程仓库,内部项目部署到私服上供其它项目使用
      • 其他公共库
  • 当Maven根据坐标寻找构件时,它首先会查看本地仓库,如果本地仓库存在此构件,则直接使用,如果本地仓库不存在此构件,或者需要查看是否有更新的构件版本,Maven就会去远程仓库查找,发现需要的构件后下载到本地仓库再使用,若本地和远程都没有,则报错

4.3 本地仓库

  • 一般来说,在Maven项目目录下,没有注入lib/这样用来存放依赖文件的目录,当Maven执行编译或测试时,如果需要使用依赖文件,它总是基于坐标使用本地仓库的依赖文件

    • 默认情况,在用户目录下路径名为.m2/repository/的仓库目录,当想自定义本地仓库目录地址时,可以编辑~/.m2/setting.xml,设置localRepository元素来指定仓库地址

      
          D:/java/repository/
      
    • 默认情况下,~/.m2/settings.xml文件是不存在的,用户需要从Maven安装目录复制$M2_HOME/conf/settings.xml文件再编辑,建议不要直接修改全局目录的settings.xml,而是在用户目录下进行修改
  • 一个构建只有在本地仓库中,才能由其它Maven项目使用

    1. 依赖Maven从远程仓库下载到本地仓库中
    2. 将本地项目的构件安装到Maven本地仓库中 mvn clean install

4.4 远程仓库

  • 安装好Maven后,如果不执行任何Maven命令,本地仓库目录是不存在的,只有输入第一条Maven命令后,Maven才会创建本地仓库,并根据配置和需要,从远程仓库下载至本地仓库

    • 这就好比藏书,本地仓库好比书房,远程仓库好比书店,我需要读书时先去书房找,当书房没有时,就去书店买回放到书房,并且一般对每个人来说,书房只有一个,而外面的书店可以有多个

4.4.1 中央仓库

  • 最原始的本地仓库是空的,所以Maven必须知道至少一个可用的远程仓库,才能在执行Maven命令时下载到需要的构建,中央仓库就是这样一个默认的远程仓库,可以通过解压工具打开$M2_HOME/lib/maven-model-builder-3.0.jar中的org/apache/maven/model/pom-4.0.0.xml文件

    
        
            central
            Maven Repository Switchboard
            http://repo1.maven/org/maven2
            default
            
                false
            
        
    
    • 这段配置是所有Maven项目都会继承的超级Pom文件,这段配置使用id central对中央仓库进行唯一标识,其名称为Maven Repository Switchboard,它使用default仓库布局,也就是在之前介绍的仓库布局,最后snapshots元素表示不从该仓库下载快照版

4.4.2 私服

  • 私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,供局域网内的Maven用户使用,当Maven需要下载构建的时候,它从私服请求,如果私服上不存在该构件,则从外部的远程仓库下载,缓存在私服上之后,再为Maven的下载提供服务

    • 此外,一些无法从外部下载的构件也可以从本地上传到私服上供大家使用
  • 私服的好处

    • 节省外网带宽:建立私服同样可以减少组织自己的开支,大量的对于外部仓库的重复请求会消耗很大的带宽,利用私服代理外部仓库之后,对外的重复构件下载便可以消除,即降低外网带宽的压力
    • 加速Maven构件:不停的连接请求外部仓库是十分耗时的,但Maven的一些内部机制(如快照更新检查井)要求Maven在执行构建时不停的检查远程仓库数据,因此,当项目配置很多外部仓库时,构建速度就会降低
    • 部署第三方构件:当某个构件无法从任何一个外部远程仓库获取,建立私服之后,便可以将这些构件部署到这个内部的仓库中,供内部的Maven项目使用
    • 提高稳定性,增强控制:Maven构建高度依赖远程仓库,因此,大哥Internet不稳定的时候,Maven构建也会变得不稳定,甚至无法构建,使用私服后,即是短暂时没有Internet连接,由于私服中有大量缓存,Maven依然可以正常运行,并且私服中有很多额外的权限功能控制
    • 降低中央仓库的负荷:每天中央仓库都需要面对大量的下载请求,使用私库可以降低对于中央仓库的负荷
  • 远程仓库的配置

    • 很多情况下,默认的中央仓库无法满足项目的需求,可能项目需要的构件存在于另一个远程仓库中,可以通过Pom/settings文件中来配置该仓库

       POM文件 
      
          .....
          
              
                  jboss
                  JBoss Repository
                  http://repository.jboss.com/maven2/
                  
                      true
                  
                  
                      false
                  
                  default
              
          
      
      
       settings文件 
      
          
              dev
              
                  true
              
              
              
                  jboss
                  JBoss Repository
                  http://repository.jboss.com/maven2/
                  
                      true
                  
                  
                      false
                  
                  default
              
          
          
      
      • 在repositories元素下,可以使用repository子元素声明一个或多个远程仓库,并且声明一个id,任何一个仓库声明的id必须是唯一id
      • Maven自带的中央仓库使用的id为central,如果其他仓库使用该id,则会覆盖中央仓库的配置,该配置中的url指向了仓库的地址
      • 该配置中的release和snapshots元素用来控制Maven对于发布版构件和快照版构件的下载,对于release和snapshots元素来说除了enabled子元素,还有updatePolicy和checksumPolicy元素

        • updatePolicy:用来配置Maven从远程仓库检查更新的频率,默认为daily

          • daily:每天
          • never:从不
          • always:每次
          • interval :X :每隔X分钟一次
        • checksumPolicy:配置Maven检查文件失败时的策略,默认为warn

          • fail:Maven遇到验证和错误就让构建失败
          • warn:Maven遇到验证和错误就发出警告信息
          • ignore:Maven遇到验证和错误时完全忽略

    true
    daily
    ignore
  • 远程仓库的验证

    • 大部分远程仓库无需认证就可以访问,但出于安全考虑,我们需要提供一些认证信息才能访问一些远程仓库
    • 配置认证信息和配置仓库信息不同,仓库信息可以直接配置在POM文件中,但是认证信息必须配置在settings.xml文件中
    • 其中server元素的id必须与POM文件中需要认证的repository元素的id完全一致

      
          ......
          
              
                  my-project
                  user
                  password
              
          
          .......
      
  • 部署至远程仓库

    • 私服一大作用就是部署第三方构件,包括组织内部生成的构件以及无法从外部仓库直接获取的构件,无论是日常开发中生成的构件,还是正式版本发布的构件,都需要部署到仓库中

      • distributionManagement包含repository和snapshotRepository子元素,前者表示发布版构件的仓库,后者表示快照版的仓库
      • 往远程仓库部署构件时,往往需要认证,认证配置需在settings.xml中创建一个server元素,其id与仓库的id匹配,无论从远程仓库下载构件,还是部署构件至远程仓库,当需要认证时,配置方式都是一样的

         在POM文件中配置distributionManagement 
        
            .....
            
                
                    jboss-release
                    JBoss Release Repository
                    http://192.168.1.99/content/repository/jboss-release
                
                
                    jboss-snapshots
                    JBoss Snapshots Repository
                    http://192.168.1.99/content/repository/jboss-snapshots
                
            
            .....
        

4.5 快照

  • 在Maven的世界中,任何一个项目或者构件都必须有自己的版本,版本的值可能是1.0.0,1.3-alpha-4,2.0,2.1-SNAPSHOT或者2.1-20191214.221414-13,其中1.0.0,1.3-alpha-4和2.0是稳定的发布版本,而2.1-SNAPSHOT和2.1-20091214.221414-13是不稳定的快照版本
  • 对于一个稳定的版本,如果仓库中已经包含,那么Maven就不会再去对照远程仓库进行更新,除非每次执行Maven命令前,清除本地仓库中等待稳定版本,而对于一个正在迭代的项目,如果要实时更新版本的内容就需要频繁的修改新的版本名称,这样是对版本号的滥用
  • 针对这种情况,使用快照版时,Maven会自动为构件打上时间戳,因此,Maven就能随时找到仓库中该构建最新版本的文件,一旦有新的更新,就会去同步到本地仓库.当项目经过完善的测试后需要发布的时候,再将快照版本更改为发布版本
  • 快照版本只应该在组织内部的项目或模块之间依赖使用,因为这时,组织对这些快照版本的依赖具有完全的理解和控制权,项目不应该依赖任何组织外部的快照版本依赖,由于快照版本的不稳定性,随时可能发生变化,这样的依赖会有潜在的危险

4.6 从仓库解析依赖的机制

  • 当本地仓库没有依赖构件的时候,Maven会自动从远程仓库下载,当依赖版本为快照版本时,Maven会自动找到最新的快照,这背后的依赖机制可以概括如下

    1. 当依赖的范围时system的时候,Maven直接从本地文件系统解析构件
    2. 根据依赖坐标计算仓库路径后,尝试从本地仓库寻找构件,如果发现相应的构件,则解析成功
    3. 在本地仓库不存在相应构件的情况下,如果依赖的版本时显式的发布版本构件,如1.2,2.1-beta-1等,则遍历所有的远程仓库,发现后,下载并解析使用
    4. 如果依赖的版本时RELEASE或LATEST,则基于更新策略读取所有远程仓库的元数据groupId/artifact/maven-metadata.xml,将其与本地仓库的对应元数据合并后,计算出RELEASE或LATEST真实的值,然后基于这个真实的值检查本地仓库和远程仓库
    5. 如果依赖的版本是SNAPSHOT,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/maven-metadata.xml,将其与本地仓库的对应数据合并后,得到最新的快照版本的值,然后基于该值检查本地仓库,或者从远程仓库下载
    6. 如果最后解析得到的构件版本是时间戳格式,如1.4.1-20191104.121455-8,则复制其时间戳格式的文件至非时间戳格式,如SNAPSHOT,并使用该非时间戳格式的构件

        基于groupId和artifactId的maven-metadata.xml 
      
      
          org.sonatype.nexus
          nexus
          
              1.4.2-SNAPSHOT
              1.3.7
              
                  1.3.5
                  1.3.6
                  1.3.7
                  1.4.0-SNAPSHOT
                  1.4.1-SNAPSHOT
                  1.4.2-SNAPSHOT
              
              20191214221133
          
      
  • 该XML文件列出了仓库中存在的构件所有可用的版本,同时latest元素指向了这些版本中最新的那个版本1.4.2-SNAPSHOT,而release元素指向了这些版本中最新的发布版本1.3.7,Maven通过合并多个远程仓库及本地仓库的元数据,就能计算出基于所有仓库的latest和release

    • 需要注意的是,在依赖声明使用LATEST和RELEASE是不推荐的做法,因为Maven随时可能解析到不同的构件,且Maven不会明确告诉用户这样的变化

4.7 镜像

  • 如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像,由于地理位置的因素,中央仓库的下载速度会比较慢,这时我们可以配置Maven使用镜像来代替中央仓库,编辑settings.xml

    
        ......
        
            
                maven.net.cn
                one of the central mirror in china
                http://maven.net.cn/content/groups/public/
                central
            
        
        ......
    
    • 的值为central,表示该配置为中央仓库的镜像,任何对于中央仓库的请求都会转至该镜像,用户也可以使用同样的方法配置其他仓库的镜像,另外三个元素id,name,url与一般仓库配置无异,表示该镜像仓库的唯一标识,名称以及地址

      • 若该镜像要进行验证,即基于该id配置仓库认证

    .....
    
        
            internal-repository
            Internal Repository Mananger
            http://192.168.1.100/maven2/
            *
        
    
    .....
  • 以上mirrorOf元素的值为*,表示该配置是所有Maven仓库的镜像,对于任何远程仓库的请求都会被转至该指定的仓库,如果镜像仓库需要验证,则配置一个id为internal-repository的即可

    • *:匹配所有远程仓库
    • external:*:匹配所有远程仓库,使用localhost的除外,使用file://协议的除外,也就是说,匹配所有不在本机上的远程仓库
    • repo1,repo2:匹配仓库repo1和repo2,用逗号分隔多个仓库
    • *,! repo1:匹配所有的远程仓库,除repo1外,使用感叹号将仓库从匹配中排除
  • 需要注意的是,由于镜像仓库完全屏蔽了被镜像仓库,当镜像仓库不稳定或停止服务时,Maven仍将无法访问被镜像仓库,因此无法下载构件

五. 生命周期和插件

5.1 Maven的生命周期

  • 在Maven出现之前,项目构建的生命周期就已经存在,但是不同的公司和开发人员虽然同样在做构件工作,其对于不同的项目却不能够重用,只能重新定制开发,而Maven的生命周期就是为了对所有的构件过程进行抽象和统一

    • 这个生命周期包含了项目的清理,初始化,编译,测试,打包,集成测试,验证,部署和站点生成等几乎所有的构件步骤,也就是说几乎所有项目的构件,都能映射到这样一个生命周期上
  • Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,在Maven的设计中,实际的任务(如编译主代码)都交由插件完成,这种思想和设计模式的模方法类似,在父类中定义算法的整体结构,子类可以通过实现或重写父类的方法来控制实际的行为

    public abstract class Template{
        public void build(){
            initialize();
            compile();
            test();
            packagee();
            integrationTest();
            deploy();
        }
        
        protect abstract void initialize();
        protect abstract void compile();
        protect abstract void test();
        protect abstract void packagee();
        protect abstract void integrationTest();
        protect abstract void deploy();
    }
    • 在Maven的生命周期中抽象了各个步骤,定义了他们的次序,但是没有提供具体的实现,而通过插件机制为每个构件步骤绑定一个或多个插件行为,而且Maven为大多数构件步骤都绑定了默认的插件,例如,针对编译的maven-compiler-plguin,针对测试的maven-surefire-plugin等
    • Maven定义的生命周期和插件机制一方面保证了所有Maven项目有一致的构件标准,另一方面又通过默认的插件简化和稳定实际项目的构件,此外,该机制还提供了足够的扩展,用户可以通过配置现有的插件或自定义插件来自定义构件行为
  • 三套声明周期

    • Maven拥有三套相互独立的生命周期,他们分别为clean,default和site,每个生命周期包含一些阶段,这些阶段是有序的,并且后面的阶段依赖于前面的阶段,但是三套声明周期本身是互相独立的

      1. clean生命周期:清理项目

        • pre-clean:执行一些清理前需要完成的工作
        • clean:清理上次构件生成的文件
        • post-clean:执行一些清理后需要完成的工作
      2. default声明周期:定义了真正的构件所需执行的所有步骤

        • validate
        • initialize
        • generate-sources
        • process-sources:处理项目主资源文件,一般来说针对/src/main/resources目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中
        • compile:编译项目的主源码,一般来说针对/src/main/java目录下的Java文件至目录输出的主classpath目录中
        • process-classes
        • generate-test-sources
        • process-test-sources:处理项目测试资源文件,一般来说针对/src/test/resources目录的内容进行变量替换工作后,复制到项目输出的测试classpath目录
        • test-compile:编译项目的测试代码,一般来说针对/src/test/java目录下的java文件至输出的测试classpath目录中
        • test:使用单元测试框架运行测试,测试代码不会被打包或部署
        • prepare-package
        • package:接收编译好的代码,打包成可发布的格式,如jar
        • pre-integration-test
        • integration-test
        • post-integration-test
        • verify
        • install:将包安装到Maven本地仓库,供本地其他Maven项目使用
        • deploy:将最终的包复制到远程仓库,供其他开发人员和Maven项目使用
      3. site声明周期:建立和发布项目站点,Maven能够基于POM所包含的信息,自动生成一个友好的站点供交流和发布项目信息

        • pre-site:执行一些在生成项目站点之前需要完成的工作
        • site:生成项目站点文档
        • post-site:执行一些在生成项目站点后需要完成的工作
        • site-deploy:将生成的项目站点发布到服务器上
    • 命令行与生命周期

      • 从命令行执行Maven任务最主要方式就是调用Maven的生命周期,各个生命周期是相互独立的,而生命周期的阶段是有前后依赖关系的

        • mvn clean:该命令调用clean生命周期的clean阶段,实际执行阶段为pre-clean和clean
        • mvn test:该命令调用default生命周期的test阶段,实际执行的阶段为default生命周期的validate,initialize直到test的所有阶段,这也解释为什么在执行测试的时候,项目代码能够自动编译
        • mvn clean install:该命令调用clean生命周期的clean阶段和default生命周期的install阶段
        • mvn clean deploy site-deploy:该命令调用clean生命周期的clean阶段,default生命周期的deploy阶段和site生命周期的site-deploy阶段

5.2 插件

5.2.1 插件目标和绑定

  • Maven的核心仅仅定义了抽象的生命周期,具体的任务是交由插件完成的,插件以独立的构件形式存在

    • 对于插件本身而言,为了能够复用代码,它往往能够完成多个任务,为每个功能编写一个插件显然不合理,因为这些任务背后有大量可复用的代码,因此,这些功能聚集到一个插件里面,每个功能就是一个插件目标
  • Maven的生命周期和插件相互绑定,用以完成实际的构件任务,具体而言,是生命周期的阶段与插件的目标相互绑定,以完成某个具体的构件任务

    • 例如:编译这一任务对应了default生命周期的compile这一阶段,而maven-compiler-plugin这一插件的compile目标能完成此任务

1590662084736

1590662084736

  • 自定义绑定

    • 除了内置绑定以外,用户还能够自己选择将某个插件目标绑定到生命周期的某个阶段上,这种自定义绑定方式能让Maven项目在构件过程中执行更多更丰富的任务

      • 当需要创建项目的源码jar包,maven-source-plugin可以帮我们完成任务,但是内置的插件绑定关系中并没有涉及这一任务,因此需要自行配置,它的jar-no-fork目标能将项目主代码打包成jar文件,可以将其绑定到default生命周期的verify阶段,在执行完集成测试和安装构件之前创建源码jar包

        
            
                
                    org.apache.maven.plugins
                    maven-source-plugin
                    2.1.1
                    
                        
                            attach-sources
                            verify
                            
                                jar-no-fork
                            
                        
                    
                
            
        
      • 在POM的build元素下的plugins子元素中声明插件的使用,上例用到了是maven-source-plugin,其groupId为org.apache.maven.plugins,用户总是应该声明一个非快照版本,这样可以避免由于插件版本变化造成的构件不稳定性

        • 除了基本的插件坐标声明外,还有插件执行配置,executions下每个execution子元素可以用来配置执行一个任务,该例配置了一个id为attach-sources的任务,通过phase配置将其绑定到verify生命周期阶段上,再通过goals配置指定要执行的插件目标,在运行mvn verify时就会执行该任务
        • 有时候.即是不通过phase元素配置生命周期阶段,插件目标也能绑定到生命周期中去,原因是:有很多插件的目标在编写时就已经定义了默认绑定阶段,可以使用maven-help-plugin查看详细信息

          • mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:2.1 -Ddetail

            • Bound to phase : package 默认绑定到package生命周期
      • 当插件目标被绑定到不同生命周期阶段的时候,其执行顺序会由生命周期阶段的先后顺序决定,如果多个目标被绑定到同一个阶段,他们的顺序就由插件声明的先后顺序决定

5.2.2 插件配置

  • 用户可以通过配置插件目标的参数,进一步调整插件目标所执行的任务,几乎所有Maven插件的目标都可以配置参数,用户可以通过命令行和POM配置等方式来配置这些参数

    • 命令行插件配置

      • 在日常的Maven使用中,我们通常从命令行输入并执行Maven命令,很多插件目标的参数都支持从命令行配置,用户可以在Maven命令中使用-D参数,并伴随一个参数键=参数值的形式,来配置插件目标的参数

        • mvn install -Dmaven.test.skip = ture:给maven.surefire-plugin提供一个maventest.skip参数,当参数为true时,就会跳过执行测试

          • 参数-D是Java自带的,其功能是通过命令行设置一个Java系统属性,Maven简单地重用了该参数,在准备插件的时候检查系统属性来实现插件参数的配置
    • POM中插件全局配置

      • 并不是所有的插件参数都适合用命令行配置,有些参数的值从项目创建到项目发布都不会改变,或者说很少改变,这种情况下在POM文件中一次性配置比重复在命令行输入更合理

        • 用户可以在声明插件的时候,对插件进行一个全局的配置,所有基于该插件的目标任务,都会使用这些配置,如:maven-compiler-plugin来编译1.8版本的源文件,生成与JVM1.8兼容的字节码文件

          
              
                  
                      org.apache.maven.plugins
                      maven-compiler-plugin
                      2.1
                      
                          1.8
                          1.8
                      
                  
              
          

5.2.3 获取插件信息

  • 当遇到一个构建任务时,不仅需要知道去哪里找到合适的插件,还需要详细的了解插件的配置点,由于Maven的插件非常多,其中大部分没有完善的文档,因此,通过帮助命令来了解插件显得非常重要

    1. 使用maven-help-plugin描述插件

      • 除了访问在线的插件文档外,可以借助maven-help-plugin来获取插件的详细信息

        • mvn help:decribe -Dplugin = org.apache.maven.plugins:maven-compiler-plugin:2.1

          • 这里执行的是maven-help-plugin的decribe目标,在参数plugin中输入需要描述插件的groupId,artifactId和version,Maven在命令行的输出maven-compiler-plugin的简要信息,包括插件的坐标,目标前缀和目标等信息
      • 在描述插件时,可以省去版本信息,Maven会自动获取最新版本来进行表述,并可以用目标前缀来替换坐标

        • mvn help:describe -Dplugin=compiler
      • 如果仅仅描述某个插件目标的信息,则加上goal参数,更详细的信息,则加上detail参数

        • mvn help:describe -Dplugin=compiler -Dgoal=compile -Ddetail

5.2.4 插件解析机制

  • 为了方便用户使用和配置插件,Maven不需要用户提供完整的插件坐标信息,就可以解析得到正确的插件

    • 插件仓库

      • 与依赖构件一样,插件构件同样基于坐标存储在Maven仓库中,在需要的时候Maven先从本地仓库寻找,如果不存在,则从远程仓库查找,找到插件之后,再下载到本地仓库使用
      • 不同于repositories以及repository子元素,插件的远程仓库使用pluginRepositories和pluginRepository配置

        
            
                central
                Maven Plugin Resository
                http://repo1.maven.org/maven2
                default
                
                    false
                
                
                    true
                    never
                
            
        
  • 插件默认的groupId

    • 在POM中配置插件时,如果该插件时Maven的官方插件(即groupId为org.apache.maven.plugins),就可以省略groupId,Maven在解析该插件的时候,会自动使用默认groupId不起

      
          
              
                  maven-compiler-plugin
                  2.1
                  
                      1.8
                      1.8
                  
              
          
      
  • 解析插件版本

    • 同样为了简化插件的配置和使用,在用户没有提供插件版本的情况下,Maven会自动解析插件版本

      • 首先Maven在超级POM中为所有核心插件设定了版本,超级POM是所有Maven项目的父POM,所有项目都继承这个超级POM配置,因此即使用户不加任何配置,Maven在使用核心插件时,它的版本就已经确定了
      • 如果用户使用的某个插件没有设定版本,并且这个插件也不属于核心插件,Maven机会去检查所有仓库中可用的版本,通过仓库元数据groupId/artifactId/maven-metadata.xml文件,如maven-compiler-plugin插件为例,它在org/apache/maven/plugins/maven-compiler-plugin/maven-metadata.xml

        
        
            org.apache.maven.plugins
            maven-compiler-plugin
            
                2.1
                2.1
                
                    2.0-beta-1
                    2.1
                    2.2
                    2.3
                
                20190102092331
            
        
      • Maven遍历本地仓库和所有远程仓库,将仓库元数据合并后,就能计算出latest和release的值,maven3将版本解析到最新的非快照版,如2.1

5.2.5 解析插件前缀

  • Maven命令支持使用插件前缀来简化插件的使用,插件前缀和groupId:artifact是一一对应的,这种匹配关系存储在仓库元数据中,与之前提到的groupId/artifactId/maven-metadata.xml不同,这里仓库元数据为groupId/maven-metadata.xml,一般插件都位于/org/apache/maven/plugins/和org/code-haus/mojo/,可以通过settings.xml配置让Maven检查其他groupId上的插件仓库元数据

    
        
            com.my.plugins
        
    
  • 在仓库的元数据文件中可以看到插件的前缀定义

    
        
            
                Maven Clean Plugin
                clean
                maven-clean-plugin
            
            
                Maven Compiler Plugin
                compile
                maven-compile-plugin
            
            
                Maven Dependency Plugin
                dependency
                maven-dependency-plugin
            
        
    

六.聚合与继承

  • Maven的集合特性能够把项目各个模块聚合在一起构建,而Maven的继承特性则能帮助抽泣各模块相同的依赖和插件等配置,在简化POM的同时,还能促进各个模块配置的一致性

6.1 集合

  • aggregator

    4.0.0
    com.lsy.project
    project-aggregator
    1.0.0-SNAPSHOT
    pom
    Aggregator
    
    
        project-email
        project-register
    
    • 对于聚合模块,其打包方式packaging的值必须为pom
    • 通过modules元素来实现聚合,用户可以通过在一个打包方式为pom的Maven项目中声明任意数量的module元素来实现模块的聚合,这里每个module的值都是一个当前POM的相对目录

      • 如aggregator的POM路径为.../project-aggregator/pom.xml,那么project-email对应 的目录为.../project-aggregator/project-email/,并且目录中包含了pom.xml,src/main/java,src/test/java等内容,离开了project-aggregator也能独立创建=
    • 聚合模块和其他模块的目录结构并非一定要父子关系,也可以是平行关系

      
          ../prject-email
          ../project-register
      
    • 当在聚合模块中执行mvn clean install时,Maven首先解析聚合模块的POM,分析要构件的模块,并计算出一个反应堆构件顺序(Reactor Build Order),然后根据这个顺序构件各个模块
    • 聚合模块的构件顺序

      • Maven按序读取POM文件,如果POM没有依赖模块,那么就构件模块,否则就先构件其依赖模块

6.2 继承

  • 从以上的聚合模块和其他模块中可以看到,多个被管理模块的POM文件中会有大量重复的相同配置,他们有相同的groupId和version,相同的依赖,相同的插件配置,而通过POM文案的继承可以消除重复

    • project-parent父模块

      4.0.0
      com.lsy.project
      project-parent
      1.0.0-SNAPSHOT
      pom
      Parent
      
      • 父模块的POM文件使用与其他模块一直的groupId和version,它的packaging方式为pom,与聚合模块一致
      • 父模块主要是为了消除配置的重复,因此它本身不包含除POM文件之外的项目文件,也就不需要src/main/java之类的文件夹了
    • project-email子模块

      
          com.lsy.project
          project-parent
          1.0.0-SNAPSHOT
          ../project-parent/pom.xml
      
      
      project-email
      Email
      
      
          .....
      
      • 使用parent元素声明父模块,parent下子元素groupId,artifactId,version指定父模块的坐标,元素relativePath表示父模块POM的相对路径,表示Email模块和其父模块是在平行的目录下

        • 当构件项目时,Maven会首先根据relativePath检查父POM文件,如果找不到再从本地仓库找,relativePath的默认值为../pom.xml,也就是说,Maven默认父POM在上一层目录
      • 子模块没有groupId和version,是因为子模块隐式的从父模块继承了这两个元素,从而消除了不必要的配置
  • 可继承的POM元素

    • groupId:项目组ID,项目坐标的核心元素
    • version:项目版本,项目坐标的核心元素
    • description:项目的描述信息
    • organization:项目的组织信息
    • inceptionYear:项目的创始年份
    • url:项目的URL地址
    • develops:项目的开发者信息
    • contributors:项目贡献者信息
    • distributionManagement:项目的部署配置
    • issueManagement:项目的缺陷跟踪系统信息
    • ciManagement:项目的持续集成系统信息
    • scm:项目的版本控制系统信息
    • mailingList:项目的邮件列表信息
    • properties:自定义属性
    • dependencies:项目的依赖配置
    • dependencyManagement:项目的依赖管理配置
    • repositories:项目的仓库配置
    • pluginRepositories:项目的插件仓库配置
    • build:包括项目的源码目录配置,输出目录配置,插件配置,插件管理配置等
    • reporting:项目的输出目录配置,报告插件配置等
  • 依赖管理

    • dependencies元素是可以被继承的,说明依赖是会被继承的,所以我们可以将子模块共有的依赖配置到父模块中,子模块就可以移除这些依赖,简化配置
    • 上述方法是可行的,但是可能将来会有新的模块并不需要父模块中的一些依赖,这就会产生不合理的现象,从而Maven提供了dependencyManagement元素,既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性

      • 在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用
      4.0.0
      com.lsy.project
      project-parent
      1.0.0-SNAPSHOT
      pom
      Parent
      
      
          4.3.1
      
      
      
          
              
                  org.springframework
                  spring-core
                  ${springframwork.version}
              
              
                  org.springframework
                  spring-beans
                  ${springframwork.version}
              
              
                  org.springframework
                  spring-context
                  ${springframwork.version}
              
          
      
* 这里使用dependencyManagement声明的依赖既不会给parent模块引入依赖,也不会给子模块引入依赖,不过这段配置是会被继承的

* 子模块POM

  ```xml
  
      com.lsy.project
      project-parent
      1.0.0-SNAPSHOT
      ../project-parent/pom.xml
  
  
  project-email
  Email
  
      
          
              org.springframework
              spring-core
          
          
              org.springframework
              spring-beans
          
          
              org.springframework
              spring-context
          
      
  ```

  * 使用这种依赖管理机制,可以在父POM中使用dependencyManagement声明依赖能够统一项目规范中的依赖版本,当版本在父POM中声明之后,子模块使用依赖时就不需要声明了.也不会发生多个子模块使用依赖版本不一致的情况

    * scoper元素的import依赖范围只在dependencyManagement元素下才有效果,使用该范围的依赖通常指向一个POM文件,作用是将POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中
    * 所以除了复制配置和继承父模块这两种方式外,还可以通过import范围依赖导入这一配置

    ```xml
    
        
            com.lsy.project
            project-parent
            1.0-SNAPSHOT
            pom
            import
        
    
    ```


  • 插件管理

    • Maven提供了dependencyManagement元素帮助管理依赖,类似的,Maven也提供了pluginManagement元素管理插件,该元素中配置的依赖同样不会造成实际的插件调用行为,而POM文件中配置了真正的plugin元素,并且groupId和artifact一致时,才会产生实际的插件行为
    • 父模块

      
          
              
                  
                      org.apache.maven.plugins
                      maven-source-plugin
                      3.1.1
                      
                          
                              attach-source
                              verify
                              
                                  jar-no-fork
                              
                          
                      
                  
              
          
      
    • 子模块

      
          
              
                  org.apache.maven.plugins
                  maven-source-plugin
              
          
      
      • 子模块使用了maven-source-plugin插件,同时又继承了父模块的pluginManagement配置
  • 集合与继承的关系

    • 多模块的聚合与继承其实是两个概念,其目的是完全不同的,前者为了方便快速的构件项目,后者主要为了消除重复配置

      • 对于聚合模块来说:它知道有哪些模块被聚合,但是那些被聚合的模块并不知道这个聚合模块的存在
      • 对于继承关系的父POM来说:它不知道有哪些子模块继承于它,但是那些子模块都必须知道自己的父模块是什么
    • 在实际项目中,一个POM往往即是聚合模块,又是父模块
  • 约定优于配置

    • Maven提倡"约定优于配置",Maven只需要一个简单的POM文件,就可以完成清除,构件等任务

      • 源码目录:src/main/java/
      • 编译输出目录为:target/classes/
      • 打包方式为:jar
      • 包输出目录为:target/
    • Maven此机制的来源就是超级POM文件,此文件在$MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml路径下

      
          
              central
              Maven Repository Swithboard
              http://repo1.maven.org/maven2
              default
              
                  false
              
          
      
      
      
      
          
              central
              Maven Plugin Repository
              http://repo1.maven.org/maven2
              default
              
                  false
              
              
                  never
              
          
      
      
      
      
           定义了项目的主输出目录 
          ${project.basedir}/target
           主代码输出目录 
          ${project.build.directory}/classes
           最终构件的名称格式 
          ${project.artifactId}-${project.version}
           测试代码输出目录 
          ${project.build.directory}/test-
        classes
           主源码目录 
          ${project.basedir}/src/main/java
           脚本源码目录 
          src/main/scripts
           测试源码目录 
          ${project.basedir}/src/test/java
           主资源目录 
          
              
                  ${project.basedir}/src/main/resources
              
          
          
          
              
                  ${project.basedir}/src/test/resources
              
          
          
          
          
           核心插件版本 
      
          
              
                  maven-antrun-plugin
                  1.3
              
              
                  maven-assembly-plugin
                  2.3
              
              
                  maven-clean-plugin
                  2.3
              
              
                  maven-compiler-plugin
                  2.0.3
              
              ......
          
      
      

七. Nexus私服

7.1 Nexus内置的仓库

  • Nexus仓库有四种类型

    1. group:仓库组
    2. hosted:宿主仓库
    3. proxy:代理仓库
    4. virtual:虚拟仓库

1590846416265

    • Maven可以从宿主仓库下载构件,Maven也可以从代理仓库下载构件,而代理仓库会简介的从远程仓库下载并缓存构件
    • 而一般为了方便,我们会从仓库组下载构件,而仓库组没有实际的内容,它只是管理一组实际的仓库,当它接收到请求时,它会转向其包含的宿主仓库或代理仓库获得实际构件的内容
    • 仓库的Policy属性

      • Release:发布版
      • Snapshot:快照版

    7.2 配置Maven从Nexus下载构件

    • 在POM文件中配置仓库和插件仓库------只对当前项目有效

      
          .......
          
              
                  nexus
                  Nexus
                  http://localhost:8081/nexus/content/groupId/public/
                  
                      true
                  
              
          
          
              
                  nexus
                  Nexus
                  http://localhost:8081/nexus/content/groupId/public/
                    
                      true
                  
                  
                      true
                  
              
          
          .......
      
    • 在settings.xml文件中配置---全局有效

      • settings.xml文件并不支持直接配置repositories和pluginRepositories,但是可以通过Maven提供的Profile机制,让用户将仓库配置到settings.xml中的Profile中
      
          ....
          
              
                  nexus
                  
                      
                            nexus
                          Nexus
                          http://localhost:8081/nexus/content/groupId/public/
                          
                              true
                          
                          
                              ture
                          
                      
                  
                  
                  
                      
                          nexus
                          Nexus
                          http://localhost:8081/nexus/content/groupId/public/
                          
                              true
                          
                          
                              true
                          
                      
                  
              
          
          
           激活指定id的profile 
          
              nexus
          
      
    • 以上配置已经能让Maven项目从Nexus私服下载构件了,但是Maven仍然会去访问中央仓库,现在我们想要将所有请求都仅仅通过私服,这就需要借助于Maven镜像了

      
          .....
          
              
                  nexus
                  *
                  http://localhost:8081/nexus/content/groupId/public/
              
          
          
          
              
                  nexus
                  
                      
                            central
                          central
                          http://central
                          
                              true
                          
                          
                              ture
                          
                      
                  
                  
                  
                      
                          central
                          Nexus
                          http://central
                          
                              true
                          
                          
                              true
                          
                      
                  
              
          
          
           激活指定id的profile 
          
              nexus
          
      
      • 这里仓库和插件仓库配置的id都为central,它们将会覆盖POM中央仓库的配置,并且这里的url已经无关紧要,因为所有的请求都会通过镜像访问私服地址

    7.3 部署构件至Nexus

    • 如果只是为了代理外部公共仓库,那么Nexus的代理仓库就已经完全足够了,对于另一类Nexus仓库---宿主仓库来说,他们主要作用是存储组织内部的,或一些无法从公共仓库获得的第三方构件,用户可以通过配置Maven自动部署构件至Nexus的宿主仓库

      • 使用Maven部署构件至Nexus

        • 日常开发生成的快照版本构件可以直接部署到Nexus中策略为Snapshot的宿主仓库中,项目正式发布的构件则应该部署到Nexus中策略为Release的宿主仓库中
        • POM文件配置

          
              .....
              
                  
                      nexus-releases
                      Nexus Release Repository
                      http://localhost:8081/nexus/content/repositories/
                          release/
                  
                  
                      nexus-snapshots
                      Nexus Snapshots Repository
                      http://localhost:8081/nexus/content/repositories/
                          snapshots
                  
              
              .....
          
        • Nexus的仓库对于匿名用户只是可读的,为了能够部署构件,还需要在settings.xml中配置认证信息

          
              ....
              
                  
                      nexus-releases
                      admin
                      admin123
                  
                  
                      nexus-snapshots
                      admin
                      admin123
                  
              
              ....
          

    八. Profile

    • 一个优秀的构件系统必须足够灵活,它应该能够让项目在不同的环境下都能够成功的构件,例如:开发环境,测试环境和产品环境,这些环境的数据库配置不尽相同,那么项目构建时就需要能够识别其所在的环境并正确使用配置

    8.1 属性

    
        4.3.1
    
    
    
        
            
                org.springframework
                spring-core
                ${springframwork.version}
            
            
                org.springframework
                spring-beans
                ${springframwork.version}
            
        
    
    • 这是最常见的Maven属性使用的方式,通过properties元素使得用户自定义一个或多个Maven属性,然后在POM的其他地方使用${属性名称}的方式来引用该属性,这种做法的最大意义是消除重复
    • Maven的属性有六类

      1. 内置属性

        1. ${basedir}:表示项目根目录,即包含pom.xml文件的目录
        2. ${version}:表示项目的版本
      2. POM属性:用户可以使用该类属性引用POM文件中对应元素的值

        • ${project.artifactId}:对应元素的值

          • ${project.build.sourceDirectory}:项目的主源码目录.默认为/src/main/java/
          • ${project.build.testSourceDirectory}:项目的测试代码源码目录.默认为/src/test/java/
          • ${project.build.directory}:项目构件输出目录.默认为/target
          • ${project.build.outputDirectory}:项目主代码编译输出目录.默认为/target/classes
          • ${project.build.testOutputDirectory}:项目测试代码编译输出目录.默认为/target/test-classes
          • ${project.build.groupId}:项目的groupId
          • ${project.build.artifactId}:项目的artifactId
          • ${project.build.build.finalName}:项目打包输出文件的名称,默认为\${project.artifactId}

            -${project.version}

      3. 自定义属性:元素下自定义的Maven属性
      4. settings属性:与POM属性同理,用户使用settings.来引用,如:${settings.localRepository}指向用户本地仓库的地址
      5. Java系统属性:所有Java系统属性都可以使用Maven属性引用,如${user.home}指向用户目录

        • 可以通过mvn help:system查看所有的java系统属性
      6. 环境变量属性:所有环境变量都可以用env.来引用,例如:${env.JAVA_HOME}

        • 可以通过 mvn help:system查看所有的环境变量

    8.2 资源过滤

    • 在不同的开发环境中,项目的源码应该使用不同的方式进行构件,最常见的就是数据库的配置了,在开发中,有些项目会在src/main/resources/目录下放置不同环境下的数据库配置

      database.jdbc.driverClass = com.mysql.jdbc.Driver
      database.jdbc.connectionURL = jdbc:mysql://localhost:3306/dev
      database.jdbc.username = dev
      database.jdbc.password = dev-passwd
      
      database.jdbc.driverClass = com.mysql.jdbc.Driver
      database.jdbc.connectionURL = jdbc:mysql://localhost:3306/test
      database.jdbc.username = test
      database.jdbc.password = test-passwd
    • 为了应对环境的变化,我们可以使用Maven属性将这些会发生变化的部分提取出来

      database.jdbc.driverClass = ${db.driver}
      database.jdbc.connectionURL = ${db.url}
      database.jdbc.username = ${db.username}
      database.jdbc.password = ${db.password}
    • 在settings.xml中通过profile定义不同环境下的配置数据

      
          
              dev
              
                  com.mysql.jdbc.Driver
                  jdbc:mysql://localhost:3306/dev
                  dev
                  dev-passwd
              
          
          
              
              test
              
                  com.mysql.jdbc.Driver
                  jdbc:mysql://localhost:3306/test
                  test
                  test-passwd
              
          
      
      • 需要注意的是:

        • Maven属性默认只会在POM文件中才会被解析,也就是说${db.username}放到POM文件中会变成dev或test,但是在src/main/resources/目录下仍然还是${db.username},因此,需要让Maven解析资源文件中的Maven属性
        • 资源文件处理的是maven-reosurces-plugin做事,它默认的行为只是将项目主资源文件复制到主代码编译输出目录
    • 开启资源过滤

      • Maven默认的主资源目录和测试目录的定义是在超级POM文件中
      
          
              ${project.basedir}/src/main/resources
              true
          
      
      
          
              ${project.basedir}/src/test/resources
              true
          
      
    • 通过mvn clean install -Pdev : 通过mvn 的 -P参数表示在命令行激活一个profile=dev的profile

    8.3 Profile

    • 激活Pofile

      1. 命令行激活

        • 用户可以使用mvn命令行参数-P 加上profile的id来激活profile,多个id之间逗号分隔

          • mvn clean install -Pdev,-Ptest
      2. settings文件显示激活

        • 如果用户希望某个profile默认一直处于激活状态,就可以配置settings.xml文件的activeProfile元素,表示其配置的profile对所有项目都处于激活状态

          
              .....
              
                  dev
              
              .....
          
      3. 系统属性激活

        • 用户可以配置当前某系统属性存在时,自动激活profile

          
              
                  
                      
                          test
                      
                  
                  .....
              
          
        • 用户可以配置当前某系统属性存在且等于a时,自动激活profile

          
              
                  
                      
                          test
                          a
                      
                  
                  .....
              
          
        • 用户可以在命令行声明系统属性

          • mvn clean install -Dtest=x
      4. 操作系统环境变量激活

        • Profile还可以根据操作系统环境激活

          
              
                  
                      
                          Windows10
                      
                  
                  .....
              
          
      5. 文件存在与否激活

        • Maven可以根据项目中某个文件是否存在来决定是否激活profile

          
              
                  
                      
                          a.properties
                          b.properties
                      
                  
                  .....
              
          
      6. 默认激活

        • 用户可以在定义profile的时候指定其默认激活

          
              
                  dev
                  
                      true
                  
                  .....
              
          
    • 以上激活优先级从上之下依次减小

      • 可以通过mvn help:active-profiles来查看当前激活的profile
      • 可以通过mvn help:all-profiles来查看所有的profile
    • profile的种类

      • 根据具体的需要,可以在一下位置声明profile

        • pom.xml:很显然,pom.xml中声明的profile只对当前项目有效
        • 用户settings.xml:用户目录下.m2/settings.xml中的profile只对本机该用户所有的Maven项目有效
        • 全局settings.xml:Maven安装目录下conf/settings.xml中profile对本机所有Maven项目都有效
    • POM中profile可使用的元素

      
          
          
          
          
          
          
          
          
          
              
              
              
              
              
          
      
        *   pom.xml:很显然,pom.xml中声明的profile只对当前项目有效
            
        *   用户settings.xml:用户目录下.m2/settings.xml中的profile只对本机该用户所有的Maven项目有效
            
        *   全局settings.xml:Maven安装目录下conf/settings.xml中profile对本机所有Maven项目都有效
            
    • POM中profile可使用的元素

    你可能感兴趣的:(maven)