Maven学习笔记总结

写在前面:早期初学maven时记录的一些笔记,部分内容摘自某些资料站以及博客文章。现在想整理一下,一些过于基础或者用处不大或者不实用过时的知识点不再做记录,maven的优势和用处等基础概念也不做赘述,网上详细资料,官方文档一大堆。

1. 基础概念

构件:在Maven中,任何依赖(jar包,tomcat等),或项目(自己打包的jar,war等)输出都可成为构件。每个构件都有自己的唯一标识(唯一坐标gav,其中artifactId构建名称必须小写字母,没有其他的特殊字符),由groupId,artifactId,和version等信息构成,由此,Maven就可以对构件进行版本控制,管理。任何可以被Maven的坐标系统唯一标识的对象都是一个Maven构件,但是,只包含其他POM文件(该文件本身不产生构件,打包方式为pom)使用定义的POM文件也是一个Maven构件。

类库管理是maven一个比较核心的功能,我们就需要将项目所依赖的类库加入到pom.xml中,那么maven会自动将依赖的类库下载到本地,并且下载的类库如果还依赖其他的类库,它也会自动下载过来,这样我们就不需要一个一个类库去下载了

约定优于配置:Maven 使用约定而不是配置,意味着开发者不需要再自己创建构建过程。为了构建工程,Maven 为开发者提供了选项来配置生命周期目标和工程依赖(依赖于 Maven 的插件扩展功能和默认的约定)。大部分的工程管理和构建相关的任务是由 Maven 插件完成的。

2.pom文件

POM 代表工程对象模型, 包含了关于工程和各种配置细节的信息,Maven 使用这些信息构建工程,每个工程应该只有一个 POM 文件。所有的 POM 文件需要 project 元素和三个必须的字段:groupId, artifactId,version。所有的 POM 都继承自一个父 POM(无论是否显式定义了这个父 POM)。父 POM 也被称作 Super POM,它包含了一些可以被继承的默认设置。Maven 使用 effective pom(Super pom 加上工程自己的配置)来执行相关的目标,它帮助开发者在 pom.xml 中做尽可能少的配置,当然这些配置可以被方便的重写。查看 effective pom 默认配置的一个简单方法是执行以下命令:mvn help:effective-pom。查看super pom的方式:${M2_HOME}/lib/maven-model-builder-3.0.4.jar,打开该文件,能找到超级父POM:\org\apache\maven\model\pom-4.0.0.xml

3. 依赖管理

<dependency>

     <groupId>实际项目</groupId>

     <artifactId>模块</artifactId>

     <version>版本</version>

     <type>依赖类型</type> <!--对应于项目坐标定义的packaging。通常不必声明,默认为jar-->

     <scope>依赖范围</scope>

     <optional>依赖是否可选</optional>

     <!—主要用于排除传递性依赖-->

     <exclusions>

       <exclusion>

         <groupId>…</groupId>

         <artifactId>…</artifactId>

       </exclusion>

     </exclusions>

</dependency>

3.1 依赖范围

1)     test:指的是测试范围有效,在编译打包、运行时都不会使用这个依赖。例如:junit jar包。

2)     compile:指的是编译范围有效,在编译、测试、打包、运行时都会将依赖存储进去。如果没有指定,就会默认使用该依赖范围。

3)     provided:在编译和测试的过程有效,最后生成包时不会加入,运行时自然也没效果。例如:servlet-api,因为servlet-api,tomcat等web服务器已经存在该jar包了,如果再打包可能会有冲突。

4)     runtime:在测试、运行的时候依赖,在编译的时候不依赖。例如:JDBC驱动,项目代码只需要jdk提供的jdbc接口,只有在执行测试和运行项目的时候才需要实现jdbc的功能。

5)     system:系统依赖范围(参考外部依赖)。该依赖范围与provided所表示的依赖范围一致,对于编译和测试有效,但在运行时无效。只是使用system范围依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用,systemPath元素可以引用环境变量。例如:

<dependency> 
    <groupId>javax.sql</groupId> 
    <artifactId>jdbc-stdext</artifactId> 
    <version>2.0</version> 
    <scope>system</scope>            
    <systemPath>${java.home}/lib/rt.jar</systemPath> 
</dependency>

3.2 引入外部依赖

现在你有了自己的工程库(library),通常情况下它会包含一些任何仓库无法使用,并且 maven 也无法下载的 jar 文件。如果你的代码正在使用这个库,那么 Maven 的构建过程将会失败,因为在编译阶段它不能下载或者引用这个库

方式一: 我们可以下载或打包好所需的外部jar包,将其打入本地中心库,在dos命令行执行(maven_home已配置的前提下)

mvn install:install-file -Dfile=本地jar包存放目录 -DgroupId=jar包添加依赖的groupId名称 -DartifactId=jar包添加依赖的artifactId名称 -Dversion=jar包添加依赖的version -Dpackaging=jar

举个栗子:

mvn install:install-file -Dfile=E:\download\alipay-sdk-java.jar -DgroupId=com.alipay -DartifactId=sdk-java -Dversion=1.0.1 -Dpackaging=jar

(eclipse中集成了这种操作,无需在命令行操作,idea中未找到,可能是因为我不熟)

同时还需要在pom文件中指定

<dependency>
    <groupId>com.alipay</groupId>
    <artifactId>sdk-java</artifactId>
    <version>1.0.1</version>
</dependency>

方式二:在主目录下新建一个lib目录,并将jar拷贝进去

    <dependency>
        <groupId>com.alipay</groupId>  
        <artifactId>sdk-java</artifactId>   
        <version>1.0.1</version>
        <scope>system</scope> <!--system,类似provided,需要显式提供依赖的jar以后,Maven就不会在Repository中查找它-->
        <systemPath>${project.basedir}/lib/alipay.jar</systemPath> <!--项目根目录下的lib文件夹下-->
    </dependency> 

另:idea支持手动添加依赖到项目中,对应的设置会体现在该module的iml中,但如果是maven项目的话,这样做是不可靠的,因为其构建方式决定了,一旦执行maven reimport重新导入,之前导入的依赖设置就会失效还原。所以,对于maven项目来说,常用的还是以上两种方式。

3.3 依赖传递

maven 提供了传递依赖的特性。所谓传递依赖是指 maven 会检查被依赖的 jar 文件,把它的依赖关系纳入最终解决的依赖关系链中。依赖传递的详细规则这里就不做阐述了(因为我记不住也几乎用不到,但这个特性确实是很强很有用的)

3.4 依赖排除

传递性依赖会给项目隐式的引入很多依赖,这极大的简化了项目依赖的管理,但是有些时候这种特性也会带来问题,它可能会把我们不需要的jar包也引入到了工程当中,使项目结构变得更复杂。或者你想替换掉默认的依赖换成自己想要的jar包,这时候就需要用到依赖排除(用的少,但是很重要,需要了解)

<dependency>    
     <groupId>org.springframework</groupId>  
     <artifactId>spring-core</artifactId>  
     <version>3.2.8</version>  
     <exclusions>  
          <exclusion>      
               <groupId>commons-logging</groupId>          
               <artifactId>commons-logging</artifactId>  
          </exclusion>  
     </exclusions>  
</dependency> 

例子中spring-core包依赖了commons-logging包,我们使用exclusions元素声明排除依赖,exclusions可以包含一个或者多个exclusion子元素,因此可以排除一个或者多个传递性依赖。需要注意的是,声明exclusions的时候只需要groupId和artifactId,而不需要version元素,这是因为只需要groupId和artifactId就能唯一定位依赖图中的某个依赖。换句话说,Maven解析后的依赖中,不可能出现groupId和artifactId相同,但是version不同的两个依赖。

其他如依赖调节,依赖可选等内容,几乎没用过,暂不做记录了。

4. 生命周期

Maven 有三个标准的生命周期:clean:项目清理的处理;default(或 build):项目部署的处理site:项目站点文档创建的处理。其中最重要的就是构建生命周期。每个生命周期都由不同的阶段组成。切记所有的生命周期的阶段都是以插件的形式存在的,或者这么说,Maven的一切就是一个大插件包合集。以构建周期为例,其阶段简化版划分为:

  • 验证(validate) - 验证项目是否正确,所有必要的信息可用
  • 编译(compile) - 编译项目的源代码
  • 测试(test) - 使用合适的单元测试框架测试编译的源代码。这些测试不应该要求代码被打包或部署
  • 打包(package) - 采用编译的代码,并以其可分配格式(如JAR)进行打包。
  • 验证(verify) - 对集成测试的结果执行任何检查,以确保满足质量标准
  • 安装(install) - 将软件包安装到本地存储库中,用作本地其他项目的依赖项
  • 部署(deploy) - 在构建环境中完成,将最终的包复制到远程存储库以与其他开发人员和项目共享。

这些生命周期阶段(以及此处未显示的其他生命周期阶段)依次执行,以完成默认生命周期。当一个阶段通过 Maven 命令调用时,例如 mvn compile,有且仅有该阶段之前以及包括该阶段在内的所有阶段,都会被执行。

5. 插件

(这个知识点很重要,可以多查点资料,推荐站点http://outofmemory.cn/maven/dive-into-pom.xml-file

每个插件都是一个构件,可以通过GAV坐标系统确定,其中gruopId若为org.apache.maven.plugins。插件在使用时可以省去groupId的指定,Maven默认以org.apache.maven.plugins作为groupId。Maven 实际上是一个依赖插件执行的框架,每个任务实际上是由插件完成。插件通常提供了一个目标的集合,并且可以使用下面的语法执行:mvn [plugin-name]:[goal-name]。

目标表示一个特定的、对构建和管理工程有帮助的任务。它属于某个插件,可能绑定了 0 个或多个构建阶段。没有绑定任何构建阶段的目标可以在构建生命周期之外被直接调用执行,执行的顺序依赖于目标和构建阶段被调用的顺序。Maven的生命周期中的各个阶段其实也是由插件组成的,整个生命周期是由各个插件有序的执行各自的目标。

有关插件的使用,本人用的不多,一个比较实用的例子是打包依赖,参考另一篇笔记。

<build>
<plugins>    
    <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <version>2.5</version>
        <executions> <!--在构建生命周期中执行一组目标的配置-->
            <execution> <!-- execution元素包含了插件执行需要的信息 -->  
                <id>default-clean</id>  <!-- 执行目标的标识符,用于标识构建过程中的目标-->
                <phase>clean</phase>  <!-- 绑定了目标的构建生命周期阶段,省略则绑定默认阶段 -->  
                <goals>   <!-- 配置的执行目标 -->  
                    <goal>clean</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
...
</plugins>  
</build>

6. Maven聚合与继承

 Maven聚合:当我们的模块非常多的时候,我们想要一次构建多个项目,而不是到多个模块的目录下分别执行命令。Maven的聚合特性就是为该需求服务的。

1、首先聚合模块必须为pom,即此pom.xml文件打包方式必为pom,否则无法构建。

2、必须有<modules>元素,它是实现聚合的最核心配置。这里可以声明多个module元素。每个module的值是一个当前项目的相对路径目录,如<module>chapter1<module>表示childOne模块位于当前项目的下一级目录chapter1下(和此pom.xml同级)。

3、为了方便用户构建项目,通常将聚合模块放在项目目录的最顶层,其他模块则作为聚合模块的子目录存在。(也可以设置平行目录,指定<module>的时候加上../)

4、聚合模块的pom没有实质性内容,只有一个pom.xml文件。只是用来帮助其他模块构建的工具。

Maven会分析聚合模块的POM、分析要构建的模块、并计算出一个反应堆构建顺序,然后根据这个顺序依次构建各个模块,这样便可以一次性构建所有聚合的模块

================================================================

Maven继承:在一个父级别的Maven的pom文件中定义了相关的常量、依赖、插件等等配置后,实际项目模块可以继承此父项目的pom文件,重复的项不必显示的再声明一遍了,相当于父Maven项目就是个模板,等着其他子模块去继承。父Maven项目要高度抽象,高度提取公共的部分(交集)。

作为父模块的pom,其打包类型必须为pom。父模块只是为了帮助消除配置的重复,因此他本身不包含除POM的项目文件,也就不需要src/main/java之类的文件夹了。

对于依赖的继承,我们可以把依赖放入<dependencyManagement>元素当中,这样的依赖就成了可选的

dependencies 与 dependencyManagement:

dependencies所有声明在dependencies里的依赖都会自动引入,并默认被所有的子项目继承

dependencyManagement 里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。

这样做的好处就是:如果有多个子项目都引用同一样依赖(而该依赖又不是所有子项目的公共依赖,不直接定义在dependencies下时),在每个使用的子项目里都声明一个版本号,这样当想升级或切换到另一个版本时,只需要在顶 层父容器里更新,而不需要一个一个子项目的修改;

parent :用于确定父项目的坐标。groupId:父项目的构件标识符。artifactId:父项目的唯一标识符。version:父项目的版本

relativePath:Maven首先在当前项目找父项目的pom,然后在文件系统的这个位置(relativePath所指定)找,然后在本地仓库,再在远程仓库找。

<parent> 
    <groupId>com.learnPro</groupId> 
    <artifactId>SIP-parent</artifactId> 
    <relativePath></relativePath> 
    <version>0.0.1-SNAPSHOT</version> 
</parent>

在继承中,构件的groupId和version元素是与父POM一样的,所以这里其实是可以省略不要的,如果有专门的版本version,可以显示的配置

================================================================

聚合与继承的关系

区别 :

1.对于聚合模块来说,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在。

2.对于继承关系的父POM来说,它不知道有哪些子模块继承于它,但那些子模块都必须知道自己的父POM是什么。

共同点 :

1.聚合POM与继承关系中的父POM的packaging都是pom。

2.聚合模块与继承关系中的父模块除了POM之外都没有实际的内容。

图示:

 

一个POM文件,既可以是一个聚合器,也可以充当父POM,并且自身也可以指定其他POM作为父POM。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值