zoukankan      html  css  js  c++  java
  • Maven工具学习(五)——Maven的核心概念和依赖管理

    1、Maven的坐标

       我们知道,Maven的核心概念是依赖的管理,那么Maven是如果来引入jar包的呢?答:通过坐标。

       在Maven中是,坐标是Jar包的唯一标识,然后Maven通过坐标在仓库中找到项目所需的Jar包。

       如下代码中,groupId、artifactId和version构成了一个Jar包的坐标(简称gav)。

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>

       Maven坐标主要组成:

    • groupId:组织标识(包名)
    • artifactId:项目名称
    • version:项目的当前版本

    2、Maven的仓库

           Maven的仓库,就指的就是存放jar包的地方。 在Maven中的仓库分为三个类型,它们分别为:本地仓库、远程仓库和中央仓库。其实远程仓库它包含了中央仓库,私服,和其他公共库。

    1. 本地仓库:就是用来存储从远程仓库或中央仓库下载的插件和jar包,项目使用一些插件或jar包,优先从本地仓库查找。默认本地仓库位置在 ${user.dir}/.m2/repository,${user.dir}表示windows用户目录。
    2. 远程仓库:就是当本地仓库没有相应的插件和jar包,maven会去远程仓库下载,默认回去中央仓库。远程仓库可以在互联网内也可以在局域网内。
    3. 中央仓库 :就是Maven团队自己维护的仓库,它服务于整个互联网,里面几乎存储了世界上大部分流行的开源项目构件,在maven软件中内置一个远程仓库地址。由于不在国内,所以我们从中央仓库下载jar包非常慢,所以国内推荐使用阿里巴巴维护的那个远程仓库。

    image

       Maven下载jar包的优先级:

           ①、如果本地仓库有,就从本地仓库里拿。

           ②、如果本地仓库没有,就从私服拿,顺便下载一份到本地仓库(如果有私服的话)。

           ③、如果私服没有,判断是否配置了镜像,如果配置了镜像,就去镜像那儿拿,如果没有没有配置镜像,就从中央仓库那拿。

           ④、如果中央仓库也没有,就抛出异常,坐标不对。
       所以, Maven的各个仓库的优先级是:本地仓库 > 私服 > 远程仓库

    3、依赖的管理

           依赖指的是项目与jar包之间的相互依赖,比如我们在使用Spring框架的时候,需要用到Spring依赖的jar包。而依赖管理指的就是使用Maven来管理项目中使用到的jar包,Maven管理的方式就是“自动下载项目所需要的jar包,统一管理jar包之间的依赖关系”。比如在文件上传的时候,光光有common-fileupload这个jar包是不行的,common-fileupload还依赖于common-io这个包jar包,此时由于Maven的依赖管理机制,都会帮我们自动下载这些包,我们并不需要管。

    image


    我们一般添加依赖都会去Maven的仓库去查找,网站为:https://mvnrepository.com/

    image

    4、依赖的配置

          依赖的配置就是我们在Maven的项目中的pom.xml中来配置我们的依赖。这一步大家都知道,但是对于Maven坐标中的其它属性可能并不认识,下面介绍一下:

    <dependencies>
    	<dependency>
    		<groupId>junit</groupId>     
    		<artifactId>junit</artifactId>     
    		<version>4.11</version>
    		<package>...</package>
    		<type>...</type>
    		<scope>...</scope>
    		<optional>...</optional>
    		<exclusions>     
    			<exclusion>     
    			  <groupId>...</groupId>     
    			  <artifactId>...</artifactId>     
    			</exclusion>
    	  </exclusions>     
    	</dependency>        
    </dependencies>

        这些元素标签的详细介绍:

    1. dependencies:用来管理依赖的总标签,一个 pom.xml 文件中只能存在一个这样的标签。
    2. dependency:包含在dependencies标签中,可以有无数个,每一个表示一个依赖。
    3. groupId,artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到需要的依赖。
    4. package:依赖的打包类型。普通项目为jar包,Web项目为war包。
    5. type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值是jar。
    6. scope:依赖的范围,默认值是 compile。后面会进行详解。
    7. optional:标记依赖是否可选。
    8. exclusions:用来排除传递性依赖,后面会进行详细介绍。

    5、依赖的范围

         依赖的范围:就是该依赖在各种环境下是否还可以使用。比如测试的jar包 junit4.11,,我们只希望在代码测试的时候使用,不希望在布署项目的时候把这个jar包放置上去,所以我们可以将Junit依赖设置为test范围的依赖。 还有server-api.jar, jsp-api.jar 是在代码编译的时候有用,但在布署项目的时候,是没有的用的,并且是不能把这个两个 jar包放置到tomcat里面的,因为tomcat里面有这两个jar包,会造成冲突, 所以一定要把这两个jar包给去掉,所以此时我们就要将它们设置为provided范围的依赖。这就是依赖的范围。

        依赖范围有六种: compile、test、provided、runtime、import和system, 其中import和system不常用,使用就不介绍了。maven的依赖范围用 <scope>元素表示的。

    1. complie:编译依赖, 是默认的依赖范围。 在main 目录下的代码可以访问这个范围下的依赖,即jar包。 test 目录下的代码也可以访问这个范围下的依赖。 布署到 tomcat下时, 要把这个依赖放置在 WEB-INF 下的 lib 文件夹里面。
    2. test:测试依赖,仅仅是测试使用的。 在main目录下的代码不能访问这个依赖, test 目录下的代码可以访问这个依赖, 布署到tomcat下时, 不会把这个依赖放置在 WEB-INF下的 lib文件夹里面。 如 junit 4.11这个依赖。
    3. provided:提供依赖 在main目录下的代码可以访问这个依赖, test目录下的代码也可以访问这个依赖, 但在布署到tomcat下时,不会把这个依赖放置在 WEB-INF 下的lib文件夹里面。 如 servlet-api, jsp-api
    4. runtime:运行依赖。 main目录下的代码不能访问这个依赖, test目录下的代码可以访问这个依赖, 布署到tomcat下时,会把这个依赖放置在 WEB-INF 下的lib 文件夹里面。 如 jdbc 驱动

      

        总结成一张表就可以知道依赖范围表示的作用域如下:

    依赖范围 对于编译执行环境有效 对于测试执行环境有效 对于运行时执行环境有效 例 子
    compile             √             √             √ spring-core
    test             —             √             — junit
    provided             √             √             — servlet-api
    runtime             —             √             √

    jdbc

    6、依赖的传递

        依赖的传递:如果我们的项目引用了一个Jar包,而该Jar包又引用了其它Jar包,那么此时的项目与引入的其它jar包就是传递依赖,但是在默认情况下项目编译时,Maven会把直接引用和简洁引用的Jar包都下载到本地。

    image

       其中项目A直接依赖于B,而B也直接依赖于C,那么A就是间接依赖于C的,这就是传递性依赖。但是传递性依赖可能会产生依赖冲突。

       如:项目A需要引入依赖包B,依赖包B又引入了依赖包C,那么B是A的直接依赖,C是A的传递依赖。如果项目A还需要引入依赖包D,依赖包D也引用了依赖包C,当依赖包B引用的C和依赖包D引用的C版本不同时,则发生了依赖冲突。

    image

    7、依赖的冲突

         刚刚在上面说了,如果项目中引入了多个相同的jar,而jar包的版本不同就会产生依赖冲突。但是Maven才不会这么蠢,Maven中采用了两种避免冲突的策略:①、短路径优先;②、声明优先。因此在Maven中实际上是不存在依赖冲突的。

        ①、短路优先:就是哪个传递依赖离它最近则就优先选择它。

    image

    通过上面的图片我们可以分析出:

    • 项目A——>B——>C——>D
    • 项目A——>B——>D

    第二条路径是离项目A最近的,也就是路径最短的。所以项目A最后用的是B依赖的D的jar包。

    但是我们思考一下,如果传递依赖的路径相同那么它们会怎么选择呢?我们接着往下看。


        ②、声明优先:就是谁先声明的就先选择谁呗。

    image

    通过上面的图片我们可以分析出:

    • 项目A——>B——>C
    • 项目A——>D——>C

    可以发现它们的路径是一样的,那此时就是按照谁先声明的就选择谁。在pom.xml 的依赖 <dependencies > </dependencies > 里面, 先放置哪一个,就用哪一个。 你先放置B, 那么C就是 B里面的, 你先放置D, 那么C就是D里面的。 所以在放置依赖时,有一定的顺序。

    8、依赖的排除

           依赖的排除:就是排除掉我们不想要的依赖。比如:项目A依赖于B,B依赖于C,但有可能C依赖是不兼容的,会对项目造成一定的影响。此时,我们可以使用关键字exclusion排除C依赖。我们以spring-aop依赖为例:spring-aop是依赖于spring-beans和spring-core的。

    image

    假如我们需要排除掉spring-beans:

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.6.RELEASE</version>
      <exclusions>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-beans</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    排除后的结果为;

    image

    9、版本的管理

       我们对版本的管理暂且有两种方式:①、版本锁定;②、版本常量。

       ①、版本锁定

       版本锁定:指的是锁定项目中依赖的版本。这种是目前实际项目中使用的最多的。版本锁定需要使用到dependencyManagement元素。需要说明的是dependencyManagement仅仅起到指锁定依赖版本的作用,其它的作用它什么都没有。而真正依赖包的下载任然要定义在dependencie元素中。

      <!--锁定依赖的版本,它仅仅是起到锁定作用,不下载依赖包-->
      <dependencyManagement>
        <dependencies>
          <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
          </dependency>
    
          <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.6.RELEASE</version>
          </dependency>
    
          <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
          </dependency>
    
          <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.6.RELEASE</version>
          </dependency>
        </dependencies>
      </dependencyManagement>
    
      <!--依赖包的下载仍然有dependencies管理-->
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <!--<version>4.11</version>-->
          <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
          <!--<version>4.1.6.RELEASE</version>-->
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>4.1.6.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <!--<version>4.1.6.RELEASE</version>-->
        </dependency>
      </dependencies>

       当我们使用dependencyManagement元素来锁定依赖版本后,dependencie元素中的依赖版本可写,也可不写,但是依赖版本引入则就有两种不同的方式了:

    1. 在dependencies中的依赖中如果没有声明依赖的版本,就到dependenciesManage中去找,找到就使用锁定的版本号,没有就报错。
    2. 在dependencies中声明了依赖的版本,则使用该依赖的版本,不管在dependenciesManage中有没有声明依赖的版本,都以dependencies中声明的版本为主。


        dependencies和dependencyManagement的区别:

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


       ②、版本常量

       首先我们在pom.xml文件中提取出各自依赖的版本常量。封装至properties元素中,然后在依赖的version元素中用OGNL表达式获取即可,版本常量方式用的也很多。

       这种方式方便了项目版本依赖管理的统一和后面的升级,而且它还具有继承性,在父项目pom.xml文件中定义的版本常量,在子模块中的pom.xml文件也能使用。

      <!--定义版本常量-->
      <properties>
        <spring.version>5.2.6.RELEASE</spring.version>
        <junit.version>4.11</junit.version>
      </properties>
      
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <!--使用版本常量-->
          <version>${junit.version}</version>
          <scope>test</scope>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
          <version>${spring.version}</version>
        </dependency>
      </dependencies>
  • 相关阅读:
    SQL SERVER Convert操作日期函数 分类: 数据库 2013-09-11 15:49 429人阅读 评论(0) 收藏
    UE格式化XML文件 分类: 开发常见问题解决方案 2013-08-27 15:40 6763人阅读 评论(0) 收藏
    The configuration section for Logging cannot be found in the configuration source 分类: .NET 2013-08-08 17:02 680人阅读 评论(0) 收藏
    C# 4.0 新特性dynamic、可选参数、命名参数等 分类: .NET 2013-07-26 11:15 414人阅读 评论(0) 收藏
    git 、github的简单使用
    对Http请求的总结
    Android Studio中对.9.png简单操作
    Spring简单的AOP运用
    Java 动态代理
    Spring Boot中自定义注解
  • 原文地址:https://www.cnblogs.com/tanghaorong/p/13054262.html
Copyright © 2011-2022 走看看